WIP Patch: Precalculate stable functions, infrastructure v1
Hello everyone again!
This is the continuation of my previous patch on the same topic; here
there are changes made thanks to Tom Lane comments (see thread here
[1]: /messages/by-id/98c77534fa51aa4bf84a5b39931c42ea@postgrespro.ru
with the first again) and here I send infrastructure patch which
includes:
- creation of CachedExpr node
- usual node functions for it
- mutator to replace nonovolatile functions' and operators' expressions
by appropriate cached expressions.
Any suggestions are welcome!
[1]: /messages/by-id/98c77534fa51aa4bf84a5b39931c42ea@postgrespro.ru
/messages/by-id/98c77534fa51aa4bf84a5b39931c42ea@postgrespro.ru
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
Precalculate-stable-functions-infrastructure-v1.patchtext/x-diff; name=Precalculate-stable-functions-infrastructure-v1.patchDownload
From d7871c9aaf64210130b591a93c13b18c74ebb2b4 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Wed, 3 May 2017 18:09:16 +0300
Subject: [PATCH] Precalculate stable functions, infrastructure v1
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- creation of CachedExpr node
- usual node functions for it
- mutator to replace nonovolatile functions' and operators' expressions by
appropriate cached expressions.
---
src/backend/nodes/copyfuncs.c | 22 +++++++
src/backend/nodes/equalfuncs.c | 22 +++++++
src/backend/nodes/nodeFuncs.c | 121 ++++++++++++++++++++++++++++++++++
src/backend/nodes/outfuncs.c | 32 +++++++++
src/backend/nodes/readfuncs.c | 33 ++++++++++
src/backend/optimizer/plan/planner.c | 124 +++++++++++++++++++++++++++++++++++
src/include/nodes/nodeFuncs.h | 2 +
src/include/nodes/nodes.h | 1 +
src/include/nodes/primnodes.h | 32 +++++++++
9 files changed, 389 insertions(+)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 35a237a..1a16e3a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1529,6 +1529,25 @@ _copyNullIfExpr(const NullIfExpr *from)
return newnode;
}
+static CachedExpr *
+_copyCachedExpr(const CachedExpr *from)
+{
+ CachedExpr *newnode = makeNode(CachedExpr);
+
+ COPY_SCALAR_FIELD(subexprtype);
+ switch(from->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ COPY_NODE_FIELD(subexpr.funcexpr);
+ break;
+ case CACHED_OPEXPR:
+ COPY_NODE_FIELD(subexpr.opexpr);
+ break;
+ }
+
+ return newnode;
+}
+
/*
* _copyScalarArrayOpExpr
*/
@@ -4869,6 +4888,9 @@ copyObjectImpl(const void *from)
case T_NullIfExpr:
retval = _copyNullIfExpr(from);
break;
+ case T_CachedExpr:
+ retval = _copyCachedExpr(from);
+ break;
case T_ScalarArrayOpExpr:
retval = _copyScalarArrayOpExpr(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 21dfbb0..5a0929a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -384,6 +384,25 @@ _equalNullIfExpr(const NullIfExpr *a, const NullIfExpr *b)
}
static bool
+_equalCachedExpr(const CachedExpr *a, const CachedExpr *b)
+{
+ COMPARE_SCALAR_FIELD(subexprtype);
+
+ /* the same subexprtype for b because we have already compared it */
+ switch(a->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ COMPARE_NODE_FIELD(subexpr.funcexpr);
+ break;
+ case CACHED_OPEXPR:
+ COMPARE_NODE_FIELD(subexpr.opexpr);
+ break;
+ }
+
+ return true;
+ }
+
+static bool
_equalScalarArrayOpExpr(const ScalarArrayOpExpr *a, const ScalarArrayOpExpr *b)
{
COMPARE_SCALAR_FIELD(opno);
@@ -3031,6 +3050,9 @@ equal(const void *a, const void *b)
case T_NullIfExpr:
retval = _equalNullIfExpr(a, b);
break;
+ case T_CachedExpr:
+ retval = _equalCachedExpr(a, b);
+ break;
case T_ScalarArrayOpExpr:
retval = _equalScalarArrayOpExpr(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 3e8189c..9621511 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -32,6 +32,7 @@ static bool planstate_walk_subplans(List *plans, bool (*walker) (),
void *context);
static bool planstate_walk_members(List *plans, PlanState **planstates,
bool (*walker) (), void *context);
+static const Node *get_const_subexpr(const CachedExpr *cachedexpr);
/*
@@ -92,6 +93,9 @@ exprType(const Node *expr)
case T_NullIfExpr:
type = ((const NullIfExpr *) expr)->opresulttype;
break;
+ case T_CachedExpr:
+ type = exprType(get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
type = BOOLOID;
break;
@@ -311,6 +315,8 @@ exprTypmod(const Node *expr)
return exprTypmod((Node *) linitial(nexpr->args));
}
break;
+ case T_CachedExpr:
+ return exprTypmod(get_const_subexpr((const CachedExpr *) expr));
case T_SubLink:
{
const SubLink *sublink = (const SubLink *) expr;
@@ -573,6 +579,10 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
return true;
}
+ if (expr && IsA(expr, CachedExpr))
+ return exprIsLengthCoercion(
+ get_const_subexpr((const CachedExpr *) expr), coercedTypmod);
+
return false;
}
@@ -655,6 +665,10 @@ strip_implicit_coercions(Node *node)
if (c->coercionformat == COERCE_IMPLICIT_CAST)
return strip_implicit_coercions((Node *) c->arg);
}
+ else if (IsA(node, CachedExpr))
+ {
+ return strip_implicit_coercions(get_subexpr((CachedExpr *) node));
+ }
return node;
}
@@ -727,6 +741,8 @@ expression_returns_set_walker(Node *node, void *context)
return false;
if (IsA(node, XmlExpr))
return false;
+ if (IsA(node, CachedExpr))
+ return false;
return expression_tree_walker(node, expression_returns_set_walker,
context);
@@ -790,6 +806,9 @@ exprCollation(const Node *expr)
case T_NullIfExpr:
coll = ((const NullIfExpr *) expr)->opcollid;
break;
+ case T_CachedExpr:
+ coll = exprCollation(get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
coll = InvalidOid; /* result is always boolean */
break;
@@ -973,6 +992,10 @@ exprInputCollation(const Node *expr)
case T_NullIfExpr:
coll = ((const NullIfExpr *) expr)->inputcollid;
break;
+ case T_CachedExpr:
+ coll = exprInputCollation(
+ get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
coll = ((const ScalarArrayOpExpr *) expr)->inputcollid;
break;
@@ -1034,6 +1057,9 @@ exprSetCollation(Node *expr, Oid collation)
case T_NullIfExpr:
((NullIfExpr *) expr)->opcollid = collation;
break;
+ case T_CachedExpr:
+ exprSetCollation(get_subexpr((CachedExpr *) expr), collation);
+ break;
case T_ScalarArrayOpExpr:
Assert(!OidIsValid(collation)); /* result is always boolean */
break;
@@ -1168,6 +1194,10 @@ exprSetInputCollation(Node *expr, Oid inputcollation)
case T_NullIfExpr:
((NullIfExpr *) expr)->inputcollid = inputcollation;
break;
+ case T_CachedExpr:
+ exprSetInputCollation(get_subexpr((CachedExpr *) expr),
+ inputcollation);
+ break;
case T_ScalarArrayOpExpr:
((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation;
break;
@@ -1277,6 +1307,9 @@ exprLocation(const Node *expr)
exprLocation((Node *) opexpr->args));
}
break;
+ case T_CachedExpr:
+ loc = exprLocation(get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
{
const ScalarArrayOpExpr *saopexpr = (const ScalarArrayOpExpr *) expr;
@@ -1611,6 +1644,8 @@ fix_opfuncids_walker(Node *node, void *context)
{
if (node == NULL)
return false;
+ if (IsA(node, CachedExpr))
+ return fix_opfuncids_walker(get_subexpr((CachedExpr *) node), context);
if (IsA(node, OpExpr))
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
@@ -1710,6 +1745,9 @@ check_functions_in_node(Node *node, check_function_callback checker,
return true;
}
break;
+ case T_CachedExpr:
+ return check_functions_in_node(get_subexpr((CachedExpr *) node),
+ checker, context);
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -1980,6 +2018,17 @@ expression_tree_walker(Node *node,
return true;
}
break;
+ case T_CachedExpr:
+ {
+ /*
+ * cachedexpr is processed by my_walker, so its subexpr is
+ * processed too and we need to process sub-nodes of subexpr.
+ */
+ if (expression_tree_walker(get_subexpr((CachedExpr *) node),
+ walker, context))
+ return true;
+ }
+ break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -2617,6 +2666,36 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_CachedExpr:
+ {
+ CachedExpr *expr = (CachedExpr *) node;
+ CachedExpr *newnode;
+
+ FLATCOPY(newnode, expr, CachedExpr);
+
+ /*
+ * expr is already mutated, so its subexpr is already mutated
+ * too and we need to mutate sub-nodes of subexpr.
+ */
+ switch(newnode->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ newnode->subexpr.funcexpr = (FuncExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.funcexpr,
+ mutator, context);
+ break;
+ case CACHED_OPEXPR:
+ newnode->subexpr.opexpr = (OpExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.opexpr,
+ mutator, context);
+ break;
+ }
+
+ return (Node *) newnode;
+ }
+ break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -3838,3 +3917,45 @@ planstate_walk_members(List *plans, PlanState **planstates,
return false;
}
+
+/*
+ * get_const_subexpr
+ * Get const subexpression of given const cached expression.
+ */
+static const Node *
+get_const_subexpr(const CachedExpr *cachedexpr)
+{
+ if (cachedexpr == NULL)
+ return NULL;
+
+ switch (cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ return (const Node *) cachedexpr->subexpr.funcexpr;
+ case CACHED_OPEXPR:
+ return (const Node *) cachedexpr->subexpr.opexpr;
+ }
+
+ return NULL;
+}
+
+/*
+ * get_subexpr
+ * Get subexpression of given cached expression.
+ */
+Node *
+get_subexpr(CachedExpr *cachedexpr)
+{
+ if (cachedexpr == NULL)
+ return NULL;
+
+ switch (cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ return (Node *) cachedexpr->subexpr.funcexpr;
+ case CACHED_OPEXPR:
+ return (Node *) cachedexpr->subexpr.opexpr;
+ }
+
+ return NULL;
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 98f6768..94bcf11 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1239,6 +1239,35 @@ _outNullIfExpr(StringInfo str, const NullIfExpr *node)
}
static void
+_outCachedExpr(StringInfo str, const CachedExpr *node)
+{
+ WRITE_NODE_TYPE("CACHEDEXPR");
+
+ /* do-it-yourself enum representation; out subexprtype begin... */
+ appendStringInfoString(str, " :subexprtype ");
+
+ switch(node->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_funcexpr");
+
+ WRITE_NODE_FIELD(subexpr.funcexpr);
+ }
+ break;
+ case CACHED_OPEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_opexpr");
+
+ WRITE_NODE_FIELD(subexpr.opexpr);
+ }
+ break;
+ }
+}
+
+static void
_outScalarArrayOpExpr(StringInfo str, const ScalarArrayOpExpr *node)
{
WRITE_NODE_TYPE("SCALARARRAYOPEXPR");
@@ -3769,6 +3798,9 @@ outNode(StringInfo str, const void *obj)
case T_NullIfExpr:
_outNullIfExpr(str, obj);
break;
+ case T_CachedExpr:
+ _outCachedExpr(str, obj);
+ break;
case T_ScalarArrayOpExpr:
_outScalarArrayOpExpr(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index f9a227e..836c20a 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -750,6 +750,37 @@ _readNullIfExpr(void)
}
/*
+ * _readCachedExpr
+ */
+static CachedExpr *
+_readCachedExpr(void)
+{
+ READ_LOCALS(CachedExpr);
+
+ /* do-it-yourself enum representation */
+ token = pg_strtok(&length); /* skip :subexprtype */
+ token = pg_strtok(&length); /* get field value */
+ if (strncmp(token, "cached_funcexpr", 15) == 0)
+ local_node->subexprtype = CACHED_FUNCEXPR;
+ else if (strncmp(token, "cached_opexpr", 13) == 0)
+ local_node->subexprtype = CACHED_OPEXPR;
+ else
+ elog(ERROR, "unrecognized subexprtype \"%.*s\"", length, token);
+
+ switch (local_node->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ READ_NODE_FIELD(subexpr.funcexpr);
+ break;
+ case CACHED_OPEXPR:
+ READ_NODE_FIELD(subexpr.opexpr);
+ break;
+ }
+
+ READ_DONE();
+}
+
+/*
* _readScalarArrayOpExpr
*/
static ScalarArrayOpExpr *
@@ -2464,6 +2495,8 @@ parseNodeString(void)
return_value = _readDistinctExpr();
else if (MATCH("NULLIFEXPR", 10))
return_value = _readNullIfExpr();
+ else if (MATCH("CACHEDEXPR", 10))
+ return_value = _readCachedExpr();
else if (MATCH("SCALARARRAYOPEXPR", 17))
return_value = _readScalarArrayOpExpr();
else if (MATCH("BOOLEXPR", 8))
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index c4a5651..4dd8cbb 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -184,6 +184,7 @@ static PathTarget *make_sort_input_target(PlannerInfo *root,
bool *have_postponed_srfs);
static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel,
List *targets, List *targets_contain_srfs);
+static Node *replace_cached_expressions_mutator(Node *node);
/*****************************************************************************
@@ -6086,3 +6087,126 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
return result;
}
+
+static Node *
+replace_cached_expressions_mutator(Node *node)
+{
+ if (node == NULL)
+ return NULL;
+
+ /* mutate certain types of nodes */
+ if (IsA(node, RestrictInfo))
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) node;
+
+ /*
+ * For an OR clause, recurse into the marked-up tree so that we replace
+ * cached expressions for contained RestrictInfos too.
+ */
+ if (rinfo->orclause)
+ rinfo->orclause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->orclause);
+ else
+ rinfo->clause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->clause);
+
+ /* do NOT recurse into children */
+ return node;
+ }
+ else if (IsA(node, FuncExpr))
+ {
+ /*
+ * Function is cached if:
+ * 1) it doesn't return set,
+ * 2) it's not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ FuncExpr *funcexpr;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool func_returns_set;
+
+ /* firstly recurse into children */
+ funcexpr = (FuncExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ func_returns_set = funcexpr->funcretset ||
+ expression_returns_set((Node *) funcexpr->args);
+
+ foreach(arg, funcexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (func_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &funcexpr->xpr))
+ {
+ /* return FuncExpr, which will not be cached */
+ return (Node *) funcexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_FUNCEXPR;
+ new_node->subexpr.funcexpr = funcexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, OpExpr))
+ {
+ /*
+ * Operator is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ OpExpr *opexpr = (OpExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ /* firstly recurse into children */
+ opexpr = (OpExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ op_returns_set = opexpr->opretset ||
+ expression_returns_set((Node *) opexpr->args);
+
+ foreach(arg, opexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &opexpr->xpr))
+ {
+ /* return OpExpr, which will not be cached */
+ return (Node *) opexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_OPEXPR;
+ new_node->subexpr.opexpr = opexpr;
+
+ return (Node *) new_node;
+ }
+ }
+
+ /* otherwise recurse into children */
+ return expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+}
diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h
index b6c9b48..2ec4700 100644
--- a/src/include/nodes/nodeFuncs.h
+++ b/src/include/nodes/nodeFuncs.h
@@ -77,4 +77,6 @@ struct PlanState;
extern bool planstate_tree_walker(struct PlanState *planstate, bool (*walker) (),
void *context);
+extern Node * get_subexpr(CachedExpr *cachedexpr);
+
#endif /* NODEFUNCS_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f59d719..054bc61 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -155,6 +155,7 @@ typedef enum NodeTag
T_OpExpr,
T_DistinctExpr,
T_NullIfExpr,
+ T_CachedExpr,
T_ScalarArrayOpExpr,
T_BoolExpr,
T_SubLink,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 86ec82e..7d4cf61 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -523,6 +523,38 @@ typedef OpExpr DistinctExpr;
typedef OpExpr NullIfExpr;
/*
+ * Discriminator for CachedExpr.
+ *
+ * Identifies the subexpression to be cached in execution (= executed only once
+ * and then used cached value) and which member in the CachedExpr->subexpr union
+ * is valid.
+ */
+typedef enum CachedSubExprType
+{
+ CACHED_FUNCEXPR, /* cached FuncExpr */
+ CACHED_OPEXPR /* cached OpExpr */
+} CachedSubExprType;
+
+/*
+ * CachedExpr - expression node for precalculated stable and immutable functions
+ * (= they are calculated once for all output rows, but as many times as
+ * function is mentioned in query), if they don't return a set and their
+ * arguments are constants or recursively precalculated functions. The same for
+ * operators' functions.
+ */
+typedef struct CachedExpr
+{
+ Expr xpr;
+ CachedSubExprType subexprtype; /* expression to be cached */
+
+ union SubExpr
+ {
+ FuncExpr *funcexpr; /* for CACHED_FUNCEXPR */
+ OpExpr *opexpr; /* for CACHED_OPEXPR */
+ } subexpr;
+} CachedExpr;
+
+/*
* ScalarArrayOpExpr - expression node for "scalar op ANY/ALL (array)"
*
* The operator must yield boolean. It is applied to the left operand
--
1.9.1
and here I send infrastructure patch which includes <...>
Next 2 patches:
Patch 'planning and execution', which includes:
- replacement nonvolatile functions and operators by appropriate cached
expressions;
- planning and execution cached expressions;
- regression tests.
Patch 'costs', which includes cost changes for cached expressions
(according to their behaviour).
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
Precalculate-stable-functions-costs-v1.patchtext/x-diff; name=Precalculate-stable-functions-costs-v1.patchDownload
From cf446cbfc8625701f9e3f32d1870b47de869802a Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Thu, 4 May 2017 19:36:05 +0300
Subject: [PATCH 3/3] Precalculate stable functions, costs v1
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- cost changes for cached expressions (according to their behaviour)
---
src/backend/optimizer/path/costsize.c | 58 ++++++++++++++++++++++++++++++++---
1 file changed, 53 insertions(+), 5 deletions(-)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 52643d0..34707fa 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -140,6 +140,7 @@ static MergeScanSelCache *cached_scansel(PlannerInfo *root,
PathKey *pathkey);
static void cost_rescan(PlannerInfo *root, Path *path,
Cost *rescan_startup_cost, Cost *rescan_total_cost);
+static double cost_eval_cacheable_expr_per_tuple(Node *node);
static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
ParamPathInfo *param_info,
@@ -3464,6 +3465,44 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
*cost = context.total;
}
+/*
+ * cost_eval_cacheable_expr_per_tuple
+ * Evaluate per tuple cost for expressions that can be cacheable.
+ *
+ * This function was created to not duplicate code for some expression and
+ * cached some expression.
+ */
+static double
+cost_eval_cacheable_expr_per_tuple(Node *node)
+{
+ double result;
+
+ /*
+ * For each operator or function node in the given tree, we charge the
+ * estimated execution cost given by pg_proc.procost (remember to multiply
+ * this by cpu_operator_cost).
+ */
+ if (IsA(node, FuncExpr))
+ {
+ result = get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+ }
+ else if (IsA(node, OpExpr))
+ {
+ OpExpr *opexpr = (OpExpr *) node;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ result = get_func_cost(opexpr->opfuncid) * cpu_operator_cost;
+ }
+ else
+ {
+ elog(ERROR, "non cacheable expression node type: %d", (int) nodeTag(node));
+ }
+
+ return result;
+}
+
static bool
cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
{
@@ -3537,13 +3576,22 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
* moreover, since our rowcount estimates for functions tend to be pretty
* phony, the results would also be pretty phony.
*/
- if (IsA(node, FuncExpr))
+ if (IsA(node, FuncExpr) ||
+ IsA(node, OpExpr))
{
- context->total.per_tuple +=
- get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+ context->total.per_tuple += cost_eval_cacheable_expr_per_tuple(node);
+ }
+ else if (IsA(node, CachedExpr))
+ {
+ /*
+ * Calculate subexpression cost per tuple as usual and add it to startup
+ * cost (because subexpression will be executed only once for all
+ * tuples).
+ */
+ context->total.startup += cost_eval_cacheable_expr_per_tuple(
+ get_subexpr((CachedExpr *) node));
}
- else if (IsA(node, OpExpr) ||
- IsA(node, DistinctExpr) ||
+ else if (IsA(node, DistinctExpr) ||
IsA(node, NullIfExpr))
{
/* rely on struct equivalence to treat these all alike */
--
1.9.1
Precalculate-stable-functions-planning-and-execution-v1.patchtext/x-diff; name=Precalculate-stable-functions-planning-and-execution-v1.patchDownload
From 508f8b959ff9d1ab78dfc79ab4657b4c10a11690 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Thu, 4 May 2017 19:09:51 +0300
Subject: [PATCH 2/3] Precalculate stable functions, planning and execution v1
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- replacement nonvolatile functions and operators by appropriate cached
expressions
- planning and execution cached expressions
- regression tests
---
src/backend/executor/execExpr.c | 70 ++
src/backend/executor/execExprInterp.c | 191 +++++
src/backend/optimizer/path/allpaths.c | 9 +-
src/backend/optimizer/path/clausesel.c | 13 +
src/backend/optimizer/plan/planagg.c | 1 +
src/backend/optimizer/plan/planner.c | 28 +
src/backend/optimizer/util/clauses.c | 43 ++
src/backend/utils/adt/ruleutils.c | 5 +
src/include/executor/execExpr.h | 38 +-
src/include/optimizer/planner.h | 3 +
src/include/optimizer/tlist.h | 7 +-
src/pl/plpgsql/src/pl_exec.c | 10 +
.../expected/precalculate_stable_functions.out | 827 +++++++++++++++++++++
src/test/regress/serial_schedule | 1 +
.../regress/sql/precalculate_stable_functions.sql | 282 +++++++
15 files changed, 1518 insertions(+), 10 deletions(-)
create mode 100644 src/test/regress/expected/precalculate_stable_functions.out
create mode 100644 src/test/regress/sql/precalculate_stable_functions.sql
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 5a34a46..c004f4c 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -72,6 +72,8 @@ static bool isAssignmentIndirectionExpr(Expr *expr);
static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
PlanState *parent, ExprState *state,
Datum *resv, bool *resnull);
+static void ExecInitCachedExpr(ExprEvalStep *scratch, CachedExpr *cachedexpr,
+ PlanState *parent, ExprState *state);
/*
@@ -865,6 +867,14 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
break;
}
+ case T_CachedExpr:
+ {
+ ExecInitCachedExpr(&scratch, (CachedExpr *) node, parent,
+ state);
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
@@ -2675,3 +2685,63 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
}
}
}
+
+/*
+ * Prepare evaluation of an CachedExpr expression.
+ */
+static void
+ExecInitCachedExpr(ExprEvalStep *scratch, CachedExpr *cachedexpr,
+ PlanState *parent, ExprState *state)
+{
+ FuncData *data = palloc0(sizeof(FuncData));
+
+ /* initialize subexpression as usual */
+ switch (cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ {
+ FuncExpr *func = cachedexpr->subexpr.funcexpr;
+
+ ExecInitFunc(scratch, (Expr *) func,
+ func->args, func->funcid, func->inputcollid,
+ parent, state);
+ }
+ break;
+ case CACHED_OPEXPR:
+ {
+ OpExpr *op = cachedexpr->subexpr.opexpr;
+
+ ExecInitFunc(scratch, (Expr *) op,
+ op->args, op->opfuncid, op->inputcollid,
+ parent, state);
+ }
+ break;
+ }
+
+ /* copy data from scratch */
+ *data = scratch->d.func;
+
+ /* initialize scratch as cached expression */
+ switch (scratch->opcode)
+ {
+ case EEOP_FUNCEXPR:
+ scratch->opcode = EEOP_CACHED_FUNCEXPR;
+ break;
+ case EEOP_FUNCEXPR_STRICT:
+ scratch->opcode = EEOP_CACHED_FUNCEXPR_STRICT;
+ break;
+ case EEOP_FUNCEXPR_FUSAGE:
+ scratch->opcode = EEOP_CACHED_FUNCEXPR_FUSAGE;
+ break;
+ case EEOP_FUNCEXPR_STRICT_FUSAGE:
+ scratch->opcode = EEOP_CACHED_FUNCEXPR_STRICT_FUSAGE;
+ break;
+ default:
+ elog(ERROR, "unknown opcode for caching expression");
+ break;
+ }
+ scratch->d.cachedexpr.subexprdata = data;
+ scratch->d.cachedexpr.isExecuted = false;
+ scratch->d.cachedexpr.resnull = false;
+ scratch->d.cachedexpr.resvalue = (Datum) 0;
+}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index fed0052..8c5989e 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -279,6 +279,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
TupleTableSlot *innerslot;
TupleTableSlot *outerslot;
TupleTableSlot *scanslot;
+ MemoryContext oldContext; /* for EEOP_CACHED_* */
/*
* This array has to be in the same order as enum ExprEvalOp.
@@ -309,6 +310,10 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_FUNCEXPR_STRICT,
&&CASE_EEOP_FUNCEXPR_FUSAGE,
&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+ &&CASE_EEOP_CACHED_FUNCEXPR,
+ &&CASE_EEOP_CACHED_FUNCEXPR_STRICT,
+ &&CASE_EEOP_CACHED_FUNCEXPR_FUSAGE,
+ &&CASE_EEOP_CACHED_FUNCEXPR_STRICT_FUSAGE,
&&CASE_EEOP_BOOL_AND_STEP_FIRST,
&&CASE_EEOP_BOOL_AND_STEP,
&&CASE_EEOP_BOOL_AND_STEP_LAST,
@@ -721,6 +726,192 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_CACHED_FUNCEXPR)
+ {
+ FunctionCallInfo fcinfo = op->d.cachedexpr.subexprdata->fcinfo_data;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ fcinfo->isnull = op->d.cachedexpr.resnull;
+ *op->resvalue = op->d.cachedexpr.resvalue;
+ *op->resnull = fcinfo->isnull;
+
+ goto cached_funcexpr;
+ }
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* execute function as usual */
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.cachedexpr.subexprdata->fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = fcinfo->isnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ cached_funcexpr:
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHED_FUNCEXPR_STRICT)
+ {
+ FunctionCallInfo fcinfo = op->d.cachedexpr.subexprdata->fcinfo_data;
+ bool *argnull = fcinfo->argnull;
+ int argno;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ fcinfo->isnull = op->d.cachedexpr.resnull;
+ if (!fcinfo->isnull)
+ *op->resvalue = op->d.cachedexpr.resvalue;
+ *op->resnull = fcinfo->isnull;
+
+ goto cached_funcexpr_strict;
+ }
+
+ /* strict function, so check for NULL args */
+ for (argno = 0; argno < op->d.func.nargs; argno++)
+ {
+ if (argnull[argno])
+ {
+ *op->resnull = true;
+
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.isExecuted = true;
+
+ goto cached_strictfail;
+ }
+ }
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* execute function as usual */
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.cachedexpr.subexprdata->fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = fcinfo->isnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ cached_funcexpr_strict:
+ cached_strictfail:
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHED_FUNCEXPR_FUSAGE)
+ {
+ FunctionCallInfo fcinfo = op->d.cachedexpr.subexprdata->fcinfo_data;
+ PgStat_FunctionCallUsage fcusage;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ fcinfo->isnull = op->d.cachedexpr.resnull;
+ *op->resvalue = op->d.cachedexpr.resvalue;
+ *op->resnull = fcinfo->isnull;
+
+ goto cached_funcexpr_fusage;
+ }
+
+ pgstat_init_function_usage(fcinfo, &fcusage);
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* execute function as usual */
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.cachedexpr.subexprdata->fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = fcinfo->isnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ pgstat_end_function_usage(&fcusage, true);
+
+ cached_funcexpr_fusage:
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHED_FUNCEXPR_STRICT_FUSAGE)
+ {
+ FunctionCallInfo fcinfo = op->d.cachedexpr.subexprdata->fcinfo_data;
+ PgStat_FunctionCallUsage fcusage;
+ bool *argnull = fcinfo->argnull;
+ int argno;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ fcinfo->isnull = op->d.cachedexpr.resnull;
+ if (!fcinfo->isnull)
+ *op->resvalue = op->d.cachedexpr.resvalue;
+ *op->resnull = fcinfo->isnull;
+
+ goto cached_funcexpr_strict_fusage;
+ }
+
+ /* strict function, so check for NULL args */
+ for (argno = 0; argno < op->d.func.nargs; argno++)
+ {
+ if (argnull[argno])
+ {
+ *op->resnull = true;
+
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.isExecuted = true;
+
+ goto cached_strictfail_fusage;
+ }
+ }
+
+ pgstat_init_function_usage(fcinfo, &fcusage);
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* execute function as usual */
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.cachedexpr.subexprdata->fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = fcinfo->isnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ pgstat_end_function_usage(&fcusage, true);
+
+ cached_funcexpr_strict_fusage:
+ cached_strictfail_fusage:
+ EEO_NEXT();
+ }
+
/*
* If any of its clauses is FALSE, an AND's result is FALSE regardless
* of the states of the rest of the clauses, so we can stop evaluating
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index b93b4fc..a322255 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -378,7 +378,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
set_subquery_pathlist(root, rel, rti, rte);
break;
case RTE_FUNCTION:
- set_function_size_estimates(root, rel);
+ {
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo);
+ set_function_size_estimates(root, rel);
+ }
break;
case RTE_TABLEFUNC:
set_tablefunc_size_estimates(root, rel);
@@ -517,6 +521,9 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
*/
check_index_predicates(root, rel);
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo);
+
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
}
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 758ddea..fc799f1 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
@@ -825,6 +826,18 @@ clause_selectivity(PlannerInfo *root,
jointype,
sjinfo);
}
+ else if (IsA(clause, CachedExpr))
+ {
+ /*
+ * Not sure this case is needed, but it can't hurt.
+ * Calculate selectivity of subexpression.
+ */
+ s1 = clause_selectivity(root,
+ get_subexpr((CachedExpr *) clause),
+ varRelid,
+ jointype,
+ sjinfo);
+ }
else
{
/*
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 5565736..7a28764 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -38,6 +38,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 4dd8cbb..985e1b4 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6088,6 +6088,34 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
return result;
}
+/*
+ * replace_pathtarget_cached_expressions
+ * Replace cached expresisons in a PathTarget tlist.
+ *
+ * As a notational convenience, returns the same PathTarget pointer passed in.
+ */
+PathTarget *
+replace_pathtarget_cached_expressions(PathTarget *target)
+{
+ target->exprs = (List *) replace_cached_expressions_mutator(
+ (Node *) target->exprs);
+
+ return target;
+}
+
+/*
+ * replace_qual_cached_expressions
+ * Replace cacehd expressions in a WHERE clause. The input can be either an
+ * implicitly-ANDed list of boolean expressions, or a list of RestrictInfo
+ * nodes.
+ */
+List *
+replace_qual_cached_expressions(List *quals)
+{
+ /* No setup needed for tree walk, so away we go */
+ return (List *) replace_cached_expressions_mutator((Node *) quals);
+}
+
static Node *
replace_cached_expressions_mutator(Node *node)
{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a1dafc8..adf8dac 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2758,6 +2758,49 @@ eval_const_expressions_mutator(Node *node,
newexpr->location = expr->location;
return (Node *) newexpr;
}
+ case T_CachedExpr:
+ {
+ CachedExpr *cachedexpr = (CachedExpr *) node;
+ Node *new_subexpr = eval_const_expressions_mutator(
+ get_subexpr(cachedexpr), context);
+ CachedExpr *new_cachedexpr;
+
+ /*
+ * If unsafe transformations are used cached expression should
+ * be always simplified.
+ */
+ if (context->estimate)
+ Assert(IsA(new_subexpr, Const));
+
+ if (IsA(new_subexpr, Const))
+ {
+ /* successfully simplified it */
+ return new_subexpr;
+ }
+ else
+ {
+ /*
+ * The expression cannot be simplified any further, so build
+ * and return a replacement CachedExpr node using the
+ * possibly-simplified arguments of subexpression.
+ */
+ new_cachedexpr = makeNode(CachedExpr);
+ new_cachedexpr->subexprtype = cachedexpr->subexprtype;
+ switch (new_cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ new_cachedexpr->subexpr.funcexpr = (FuncExpr *)
+ new_subexpr;
+ break;
+ case CACHED_OPEXPR:
+ new_cachedexpr->subexpr.opexpr = (OpExpr *)
+ new_subexpr;
+ break;
+ }
+
+ return (Node *) new_cachedexpr;
+ }
+ }
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index cbde1ff..cc33655 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7690,6 +7690,11 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_CachedExpr:
+ get_rule_expr(get_subexpr((CachedExpr *) node), context,
+ showimplicit);
+ break;
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 86fdb33..c0c4207 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -85,6 +85,12 @@ typedef enum ExprEvalOp
EEOP_FUNCEXPR_FUSAGE,
EEOP_FUNCEXPR_STRICT_FUSAGE,
+ /* evaluate CachedExpr */
+ EEOP_CACHED_FUNCEXPR,
+ EEOP_CACHED_FUNCEXPR_STRICT,
+ EEOP_CACHED_FUNCEXPR_FUSAGE,
+ EEOP_CACHED_FUNCEXPR_STRICT_FUSAGE,
+
/*
* Evaluate boolean AND expression, one step per subexpression. FIRST/LAST
* subexpressions are special-cased for performance. Since AND always has
@@ -217,6 +223,20 @@ typedef enum ExprEvalOp
} ExprEvalOp;
+/*
+ * Inline data of ExprEvalStep for operations
+ * EEOP_FUNCEXPR_* / NULLIF / DISTINCT / CACHED_FUNCEXPR_*
+ */
+typedef struct FuncData
+{
+ FmgrInfo *finfo; /* function's lookup data */
+ FunctionCallInfo fcinfo_data; /* arguments etc */
+ /* faster to access without additional indirection: */
+ PGFunction fn_addr; /* actual call address */
+ int nargs; /* number of arguments */
+} FuncData;
+
+
typedef struct ExprEvalStep
{
/*
@@ -289,14 +309,18 @@ typedef struct ExprEvalStep
} constval;
/* for EEOP_FUNCEXPR_* / NULLIF / DISTINCT */
+ FuncData func;
+
+ /* for EEOP_CACHED_FUNCEXPR_* */
struct
- {
- FmgrInfo *finfo; /* function's lookup data */
- FunctionCallInfo fcinfo_data; /* arguments etc */
- /* faster to access without additional indirection: */
- PGFunction fn_addr; /* actual call address */
- int nargs; /* number of arguments */
- } func;
+ {
+ /* cached ExprEvalOp data */
+ FuncData *subexprdata;
+
+ bool isExecuted;
+ bool resnull;
+ Datum resvalue;
+ } cachedexpr;
/* for EEOP_BOOL_*_STEP */
struct
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index f3aaa23..bbadcdd 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -59,4 +59,7 @@ extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);
extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti);
+extern PathTarget *replace_pathtarget_cached_expressions(PathTarget *target);
+extern List *replace_qual_cached_expressions(List *quals);
+
#endif /* PLANNER_H */
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index ccb93d8..0b893d0 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -65,8 +65,11 @@ extern void split_pathtarget_at_srfs(PlannerInfo *root,
PathTarget *target, PathTarget *input_target,
List **targets, List **targets_contain_srfs);
-/* Convenience macro to get a PathTarget with valid cost/width fields */
+/* Convenience macro to get a PathTarget with valid cost/width fields and
+ * cached expressions.
+ */
#define create_pathtarget(root, tlist) \
- set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist))
+ set_pathtarget_cost_width(root, replace_pathtarget_cached_expressions( \
+ make_pathtarget_from_tlist(tlist)))
#endif /* TLIST_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 7a40c99..2e27052 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -6535,6 +6535,16 @@ exec_simple_check_node(Node *node)
return TRUE;
}
+ case T_CachedExpr:
+ {
+ /*
+ * If CachedExpr will not be initialized by ExecInitCachedExpr
+ * possibly it will use cached value when it shouldn't (for
+ * example, snapshot has changed), so return false.
+ */
+ return FALSE;
+ }
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/test/regress/expected/precalculate_stable_functions.out b/src/test/regress/expected/precalculate_stable_functions.out
new file mode 100644
index 0000000..cfef1d2
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions.out
@@ -0,0 +1,827 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create tables for testing
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+-- Create volatile functions for testing
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create stable functions for testing
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+-- Create immutable functions for testing
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create operators for testing
+CREATE operator === (PROCEDURE = equal_integers_vlt, LEFTARG = integer, RIGHTARG = integer);
+CREATE operator ==== (PROCEDURE = equal_integers_stl, LEFTARG = integer, RIGHTARG = integer);
+CREATE operator ===== (PROCEDURE = equal_integers_imm, LEFTARG = integer, RIGHTARG = integer);
+CREATE operator ====== (PROCEDURE = equal_booleans_stl_strict, LEFTARG = boolean, RIGHTARG = boolean);
+-- Simple functions testing
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- Functions with constant arguments and nested functions testing
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Nested and strict operators testing
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and operators testing
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Tracking functions testing
+SET track_functions TO 'all';
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SET track_functions TO DEFAULT;
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+BEGIN;
+SELECT simple();
+ simple
+--------
+ 2
+(1 row)
+
+INSERT INTO two VALUES (3);
+SELECT simple();
+ simple
+--------
+ 3
+(1 row)
+
+ROLLBACK;
+-- Drop tables for testing
+DROP TABLE two;
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 04206c3..f2710b9 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -179,3 +179,4 @@ test: with
test: xml
test: event_trigger
test: stats
+test: precalculate_stable_functions
diff --git a/src/test/regress/sql/precalculate_stable_functions.sql b/src/test/regress/sql/precalculate_stable_functions.sql
new file mode 100644
index 0000000..c86c382
--- /dev/null
+++ b/src/test/regress/sql/precalculate_stable_functions.sql
@@ -0,0 +1,282 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+
+-- Create tables for testing
+
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+
+-- Create volatile functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create stable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create immutable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create operators for testing
+
+CREATE operator === (PROCEDURE = equal_integers_vlt, LEFTARG = integer, RIGHTARG = integer);
+CREATE operator ==== (PROCEDURE = equal_integers_stl, LEFTARG = integer, RIGHTARG = integer);
+CREATE operator ===== (PROCEDURE = equal_integers_imm, LEFTARG = integer, RIGHTARG = integer);
+CREATE operator ====== (PROCEDURE = equal_booleans_stl_strict, LEFTARG = boolean, RIGHTARG = boolean);
+
+-- Simple functions testing
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+-- WHERE clause testing
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+-- Functions with constant arguments and nested functions testing
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions testing
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions with null arguments testing
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+-- Operators testing
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+
+-- Nested and strict operators testing
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+
+-- Mixed functions and operators testing
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+-- Tracking functions testing
+
+SET track_functions TO 'all';
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+SET track_functions TO DEFAULT;
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+
+BEGIN;
+SELECT simple();
+INSERT INTO two VALUES (3);
+SELECT simple();
+ROLLBACK;
+
+-- Drop tables for testing
+
+DROP TABLE two;
--
1.9.1
On Thu, May 4, 2017 at 7:51 PM, Marina Polyakova <m.polyakova@postgrespro.ru
wrote:
and here I send infrastructure patch which includes <...>
Next 2 patches:
Patch 'planning and execution', which includes:
- replacement nonvolatile functions and operators by appropriate cached
expressions;
- planning and execution cached expressions;
- regression tests.Patch 'costs', which includes cost changes for cached expressions
(according to their behaviour).
Great, thank you for your work.
It's good and widely used practice to prepend number to the patch name
while dealing with patch set.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hi Marina,
I've noticed that this patch needs a review and decided to take a look.
Here is a short summary:
* Patches apply to the master branch. There are a trailing whitespaces,
though.
* All tests pass.
* I see 8-10% performance improvement on full text search queries.
* It seems that there is no obvious performance degradation on regular
queries (according to pgbench).
In short, it looks very promising.
--
Best regards,
Aleksander Alekseev
Hello, Aleksander!
I've noticed that this patch needs a review and decided to take a look.
Thank you very much!
There are a trailing whitespaces,
though.
Oh, sorry, I'll check them.
I see 8-10% performance improvement on full text search queries.
Glad to hear it =)
It seems that there is no obvious performance degradation on regular
queries (according to pgbench).
Thanks for testing it, I'll try not to forget about it next time =[
In short, it looks very promising.
And thanks again!
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello!
Here's v2 of the patches. Changes from v1:
* Precalculation of DistinctExpr, NullIfExpr, ScalarArrayOpExpr (as
usual, if their operators are not volatile theirselves, don't return set
and their arguments are consts or cached expressions too);
* Removed trailing whitespaces.
Also, as I tested, it seems that there is no obvious performance
degradation too on regular queries (according to pgbench).
Patches are attached. Any suggestions are welcome!
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-Precalculate-stable-functions-infrastructure-v2.patchtext/x-diff; name=0001-Precalculate-stable-functions-infrastructure-v2.patchDownload
From 92ee741fc8294186db689a4438009369b9c460b4 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 14:24:36 +0300
Subject: [PATCH 1/3] Precalculate stable functions, infrastructure v2
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- creation of CachedExpr node
- usual node functions for it
- mutator to replace nonovolatile functions' and operators' expressions by
appropriate cached expressions.
---
src/backend/nodes/copyfuncs.c | 31 +++++
src/backend/nodes/equalfuncs.c | 31 +++++
src/backend/nodes/nodeFuncs.c | 151 ++++++++++++++++++++
src/backend/nodes/outfuncs.c | 56 ++++++++
src/backend/nodes/readfuncs.c | 48 +++++++
src/backend/optimizer/plan/planner.c | 259 +++++++++++++++++++++++++++++++++++
src/include/nodes/nodeFuncs.h | 1 +
src/include/nodes/nodes.h | 1 +
src/include/nodes/primnodes.h | 38 +++++
9 files changed, 616 insertions(+)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6ad3844..f9f69a1 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1527,6 +1527,34 @@ _copyNullIfExpr(const NullIfExpr *from)
return newnode;
}
+static CachedExpr *
+_copyCachedExpr(const CachedExpr *from)
+{
+ CachedExpr *newnode = makeNode(CachedExpr);
+
+ COPY_SCALAR_FIELD(subexprtype);
+ switch(from->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ COPY_NODE_FIELD(subexpr.funcexpr);
+ break;
+ case CACHED_OPEXPR:
+ COPY_NODE_FIELD(subexpr.opexpr);
+ break;
+ case CACHED_DISTINCTEXPR:
+ COPY_NODE_FIELD(subexpr.distinctexpr);
+ break;
+ case CACHED_NULLIFEXPR:
+ COPY_NODE_FIELD(subexpr.nullifexpr);
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ COPY_NODE_FIELD(subexpr.saopexpr);
+ break;
+ }
+
+ return newnode;
+}
+
/*
* _copyScalarArrayOpExpr
*/
@@ -4867,6 +4895,9 @@ copyObjectImpl(const void *from)
case T_NullIfExpr:
retval = _copyNullIfExpr(from);
break;
+ case T_CachedExpr:
+ retval = _copyCachedExpr(from);
+ break;
case T_ScalarArrayOpExpr:
retval = _copyScalarArrayOpExpr(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index c9a8c34..8863759 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -384,6 +384,34 @@ _equalNullIfExpr(const NullIfExpr *a, const NullIfExpr *b)
}
static bool
+_equalCachedExpr(const CachedExpr *a, const CachedExpr *b)
+{
+ COMPARE_SCALAR_FIELD(subexprtype);
+
+ /* the same subexprtype for b because we have already compared it */
+ switch(a->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ COMPARE_NODE_FIELD(subexpr.funcexpr);
+ break;
+ case CACHED_OPEXPR:
+ COMPARE_NODE_FIELD(subexpr.opexpr);
+ break;
+ case CACHED_DISTINCTEXPR:
+ COMPARE_NODE_FIELD(subexpr.distinctexpr);
+ break;
+ case CACHED_NULLIFEXPR:
+ COMPARE_NODE_FIELD(subexpr.nullifexpr);
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ COMPARE_NODE_FIELD(subexpr.saopexpr);
+ break;
+ }
+
+ return true;
+}
+
+static bool
_equalScalarArrayOpExpr(const ScalarArrayOpExpr *a, const ScalarArrayOpExpr *b)
{
COMPARE_SCALAR_FIELD(opno);
@@ -3031,6 +3059,9 @@ equal(const void *a, const void *b)
case T_NullIfExpr:
retval = _equalNullIfExpr(a, b);
break;
+ case T_CachedExpr:
+ retval = _equalCachedExpr(a, b);
+ break;
case T_ScalarArrayOpExpr:
retval = _equalScalarArrayOpExpr(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 3e8189c..e3dd576 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -32,6 +32,7 @@ static bool planstate_walk_subplans(List *plans, bool (*walker) (),
void *context);
static bool planstate_walk_members(List *plans, PlanState **planstates,
bool (*walker) (), void *context);
+static const Node *get_const_subexpr(const CachedExpr *cachedexpr);
/*
@@ -92,6 +93,9 @@ exprType(const Node *expr)
case T_NullIfExpr:
type = ((const NullIfExpr *) expr)->opresulttype;
break;
+ case T_CachedExpr:
+ type = exprType(get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
type = BOOLOID;
break;
@@ -311,6 +315,8 @@ exprTypmod(const Node *expr)
return exprTypmod((Node *) linitial(nexpr->args));
}
break;
+ case T_CachedExpr:
+ return exprTypmod(get_const_subexpr((const CachedExpr *) expr));
case T_SubLink:
{
const SubLink *sublink = (const SubLink *) expr;
@@ -573,6 +579,10 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
return true;
}
+ if (expr && IsA(expr, CachedExpr))
+ return exprIsLengthCoercion(
+ get_const_subexpr((const CachedExpr *) expr), coercedTypmod);
+
return false;
}
@@ -655,6 +665,10 @@ strip_implicit_coercions(Node *node)
if (c->coercionformat == COERCE_IMPLICIT_CAST)
return strip_implicit_coercions((Node *) c->arg);
}
+ else if (IsA(node, CachedExpr))
+ {
+ return strip_implicit_coercions(get_subexpr((CachedExpr *) node));
+ }
return node;
}
@@ -727,6 +741,8 @@ expression_returns_set_walker(Node *node, void *context)
return false;
if (IsA(node, XmlExpr))
return false;
+ if (IsA(node, CachedExpr))
+ return false;
return expression_tree_walker(node, expression_returns_set_walker,
context);
@@ -790,6 +806,9 @@ exprCollation(const Node *expr)
case T_NullIfExpr:
coll = ((const NullIfExpr *) expr)->opcollid;
break;
+ case T_CachedExpr:
+ coll = exprCollation(get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
coll = InvalidOid; /* result is always boolean */
break;
@@ -973,6 +992,10 @@ exprInputCollation(const Node *expr)
case T_NullIfExpr:
coll = ((const NullIfExpr *) expr)->inputcollid;
break;
+ case T_CachedExpr:
+ coll = exprInputCollation(
+ get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
coll = ((const ScalarArrayOpExpr *) expr)->inputcollid;
break;
@@ -1034,6 +1057,9 @@ exprSetCollation(Node *expr, Oid collation)
case T_NullIfExpr:
((NullIfExpr *) expr)->opcollid = collation;
break;
+ case T_CachedExpr:
+ exprSetCollation(get_subexpr((CachedExpr *) expr), collation);
+ break;
case T_ScalarArrayOpExpr:
Assert(!OidIsValid(collation)); /* result is always boolean */
break;
@@ -1168,6 +1194,10 @@ exprSetInputCollation(Node *expr, Oid inputcollation)
case T_NullIfExpr:
((NullIfExpr *) expr)->inputcollid = inputcollation;
break;
+ case T_CachedExpr:
+ exprSetInputCollation(get_subexpr((CachedExpr *) expr),
+ inputcollation);
+ break;
case T_ScalarArrayOpExpr:
((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation;
break;
@@ -1277,6 +1307,9 @@ exprLocation(const Node *expr)
exprLocation((Node *) opexpr->args));
}
break;
+ case T_CachedExpr:
+ loc = exprLocation(get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
{
const ScalarArrayOpExpr *saopexpr = (const ScalarArrayOpExpr *) expr;
@@ -1611,6 +1644,8 @@ fix_opfuncids_walker(Node *node, void *context)
{
if (node == NULL)
return false;
+ if (IsA(node, CachedExpr))
+ return fix_opfuncids_walker(get_subexpr((CachedExpr *) node), context);
if (IsA(node, OpExpr))
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
@@ -1710,6 +1745,9 @@ check_functions_in_node(Node *node, check_function_callback checker,
return true;
}
break;
+ case T_CachedExpr:
+ return check_functions_in_node(get_subexpr((CachedExpr *) node),
+ checker, context);
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -1980,6 +2018,17 @@ expression_tree_walker(Node *node,
return true;
}
break;
+ case T_CachedExpr:
+ {
+ /*
+ * cachedexpr is processed by my_walker, so its subexpr is
+ * processed too and we need to process sub-nodes of subexpr.
+ */
+ if (expression_tree_walker(get_subexpr((CachedExpr *) node),
+ walker, context))
+ return true;
+ }
+ break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -2617,6 +2666,54 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_CachedExpr:
+ {
+ CachedExpr *expr = (CachedExpr *) node;
+ CachedExpr *newnode;
+
+ FLATCOPY(newnode, expr, CachedExpr);
+
+ /*
+ * expr is already mutated, so its subexpr is already mutated
+ * too and we need to mutate sub-nodes of subexpr.
+ */
+ switch(newnode->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ newnode->subexpr.funcexpr = (FuncExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.funcexpr, mutator,
+ context);
+ break;
+ case CACHED_OPEXPR:
+ newnode->subexpr.opexpr = (OpExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.opexpr, mutator,
+ context);
+ break;
+ case CACHED_DISTINCTEXPR:
+ newnode->subexpr.distinctexpr = (DistinctExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.distinctexpr, mutator,
+ context);
+ break;
+ case CACHED_NULLIFEXPR:
+ newnode->subexpr.nullifexpr = (NullIfExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.nullifexpr, mutator,
+ context);
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ newnode->subexpr.saopexpr = (ScalarArrayOpExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.saopexpr, mutator,
+ context);
+ break;
+ }
+
+ return (Node *) newnode;
+ }
+ break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -3838,3 +3935,57 @@ planstate_walk_members(List *plans, PlanState **planstates,
return false;
}
+
+/*
+ * get_const_subexpr
+ * Get const subexpression of given const cached expression.
+ */
+static const Node *
+get_const_subexpr(const CachedExpr *cachedexpr)
+{
+ if (cachedexpr == NULL)
+ return NULL;
+
+ switch (cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ return (const Node *) cachedexpr->subexpr.funcexpr;
+ case CACHED_OPEXPR:
+ return (const Node *) cachedexpr->subexpr.opexpr;
+ case CACHED_DISTINCTEXPR:
+ return (const Node *) cachedexpr->subexpr.distinctexpr;
+ case CACHED_NULLIFEXPR:
+ return (const Node *) cachedexpr->subexpr.nullifexpr;
+ case CACHED_SCALARARRAYOPEXPR:
+ return (const Node *) cachedexpr->subexpr.saopexpr;
+ }
+
+ return NULL;
+}
+
+/*
+ * get_subexpr
+ * Get subexpression of given cached expression.
+ */
+Node *
+get_subexpr(CachedExpr *cachedexpr)
+{
+ if (cachedexpr == NULL)
+ return NULL;
+
+ switch (cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ return (Node *) cachedexpr->subexpr.funcexpr;
+ case CACHED_OPEXPR:
+ return (Node *) cachedexpr->subexpr.opexpr;
+ case CACHED_DISTINCTEXPR:
+ return (Node *) cachedexpr->subexpr.distinctexpr;
+ case CACHED_NULLIFEXPR:
+ return (Node *) cachedexpr->subexpr.nullifexpr;
+ case CACHED_SCALARARRAYOPEXPR:
+ return (Node *) cachedexpr->subexpr.saopexpr;
+ }
+
+ return NULL;
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8d9ff63..c0c8363 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1237,6 +1237,59 @@ _outNullIfExpr(StringInfo str, const NullIfExpr *node)
}
static void
+_outCachedExpr(StringInfo str, const CachedExpr *node)
+{
+ WRITE_NODE_TYPE("CACHEDEXPR");
+
+ /* do-it-yourself enum representation; out subexprtype begin... */
+ appendStringInfoString(str, " :subexprtype ");
+
+ switch(node->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_funcexpr");
+
+ WRITE_NODE_FIELD(subexpr.funcexpr);
+ }
+ break;
+ case CACHED_OPEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_opexpr");
+
+ WRITE_NODE_FIELD(subexpr.opexpr);
+ }
+ break;
+ case CACHED_DISTINCTEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_distinctexpr");
+
+ WRITE_NODE_FIELD(subexpr.distinctexpr);
+ }
+ break;
+ case CACHED_NULLIFEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_nullifexpr");
+
+ WRITE_NODE_FIELD(subexpr.nullifexpr);
+ }
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_scalararrayopexpr");
+
+ WRITE_NODE_FIELD(subexpr.saopexpr);
+ }
+ break;
+ }
+}
+
+static void
_outScalarArrayOpExpr(StringInfo str, const ScalarArrayOpExpr *node)
{
WRITE_NODE_TYPE("SCALARARRAYOPEXPR");
@@ -3767,6 +3820,9 @@ outNode(StringInfo str, const void *obj)
case T_NullIfExpr:
_outNullIfExpr(str, obj);
break;
+ case T_CachedExpr:
+ _outCachedExpr(str, obj);
+ break;
case T_ScalarArrayOpExpr:
_outScalarArrayOpExpr(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index e24f5d6..acb14f9 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -750,6 +750,52 @@ _readNullIfExpr(void)
}
/*
+ * _readCachedExpr
+ */
+static CachedExpr *
+_readCachedExpr(void)
+{
+ READ_LOCALS(CachedExpr);
+
+ /* do-it-yourself enum representation */
+ token = pg_strtok(&length); /* skip :subexprtype */
+ token = pg_strtok(&length); /* get field value */
+ if (strncmp(token, "cached_funcexpr", 15) == 0)
+ local_node->subexprtype = CACHED_FUNCEXPR;
+ else if (strncmp(token, "cached_opexpr", 13) == 0)
+ local_node->subexprtype = CACHED_OPEXPR;
+ else if (strncmp(token, "cached_distinctexpr", 19) == 0)
+ local_node->subexprtype = CACHED_DISTINCTEXPR;
+ else if (strncmp(token, "cached_nullifexpr", 17) == 0)
+ local_node->subexprtype = CACHED_NULLIFEXPR;
+ else if (strncmp(token, "cached_scalararrayopexpr", 24) == 0)
+ local_node->subexprtype = CACHED_SCALARARRAYOPEXPR;
+ else
+ elog(ERROR, "unrecognized subexprtype \"%.*s\"", length, token);
+
+ switch (local_node->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ READ_NODE_FIELD(subexpr.funcexpr);
+ break;
+ case CACHED_OPEXPR:
+ READ_NODE_FIELD(subexpr.opexpr);
+ break;
+ case CACHED_DISTINCTEXPR:
+ READ_NODE_FIELD(subexpr.distinctexpr);
+ break;
+ case CACHED_NULLIFEXPR:
+ READ_NODE_FIELD(subexpr.nullifexpr);
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ READ_NODE_FIELD(subexpr.saopexpr);
+ break;
+ }
+
+ READ_DONE();
+}
+
+/*
* _readScalarArrayOpExpr
*/
static ScalarArrayOpExpr *
@@ -2462,6 +2508,8 @@ parseNodeString(void)
return_value = _readDistinctExpr();
else if (MATCH("NULLIFEXPR", 10))
return_value = _readNullIfExpr();
+ else if (MATCH("CACHEDEXPR", 10))
+ return_value = _readCachedExpr();
else if (MATCH("SCALARARRAYOPEXPR", 17))
return_value = _readScalarArrayOpExpr();
else if (MATCH("BOOLEXPR", 8))
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index c4a5651..552b73d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -184,6 +184,7 @@ static PathTarget *make_sort_input_target(PlannerInfo *root,
bool *have_postponed_srfs);
static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel,
List *targets, List *targets_contain_srfs);
+static Node *replace_cached_expressions_mutator(Node *node);
/*****************************************************************************
@@ -6086,3 +6087,261 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
return result;
}
+
+static Node *
+replace_cached_expressions_mutator(Node *node)
+{
+ if (node == NULL)
+ return NULL;
+
+ /* mutate certain types of nodes */
+ if (IsA(node, RestrictInfo))
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) node;
+
+ /*
+ * For an OR clause, recurse into the marked-up tree so that we replace
+ * cached expressions for contained RestrictInfos too.
+ */
+ if (rinfo->orclause)
+ rinfo->orclause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->orclause);
+ else
+ rinfo->clause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->clause);
+
+ /* do NOT recurse into children */
+ return node;
+ }
+ else if (IsA(node, FuncExpr))
+ {
+ /*
+ * Function is cached if:
+ * 1) it doesn't return set,
+ * 2) it's not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ FuncExpr *funcexpr;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool func_returns_set;
+
+ /* firstly recurse into children */
+ funcexpr = (FuncExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ func_returns_set = funcexpr->funcretset ||
+ expression_returns_set((Node *) funcexpr->args);
+
+ foreach(arg, funcexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (func_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &funcexpr->xpr))
+ {
+ /* return FuncExpr, which will not be cached */
+ return (Node *) funcexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_FUNCEXPR;
+ new_node->subexpr.funcexpr = funcexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, OpExpr))
+ {
+ /*
+ * Operator is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ OpExpr *opexpr = (OpExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ /* firstly recurse into children */
+ opexpr = (OpExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ op_returns_set = opexpr->opretset ||
+ expression_returns_set((Node *) opexpr->args);
+
+ foreach(arg, opexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &opexpr->xpr))
+ {
+ /* return OpExpr, which will not be cached */
+ return (Node *) opexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_OPEXPR;
+ new_node->subexpr.opexpr = opexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, DistinctExpr))
+ {
+ /*
+ * Operator of DistinctExpr is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ DistinctExpr *distinctexpr = (DistinctExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid((OpExpr *) distinctexpr);
+
+ /* firstly recurse into children */
+ distinctexpr = (DistinctExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ op_returns_set = distinctexpr->opretset ||
+ expression_returns_set((Node *) distinctexpr->args);
+
+ foreach(arg, distinctexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &distinctexpr->xpr))
+ {
+ /* return DistinctExpr, which will not be cached */
+ return (Node *) distinctexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_DISTINCTEXPR;
+ new_node->subexpr.distinctexpr = distinctexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, NullIfExpr))
+ {
+ /*
+ * Operator of NullIfExpr is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ NullIfExpr *nullifexpr = (NullIfExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid((OpExpr *) nullifexpr);
+
+ /* firstly recurse into children */
+ nullifexpr = (NullIfExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ op_returns_set = nullifexpr->opretset ||
+ expression_returns_set((Node *) nullifexpr->args);
+
+ foreach(arg, nullifexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &nullifexpr->xpr))
+ {
+ /* return NullIfExpr, which will not be cached */
+ return (Node *) nullifexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_NULLIFEXPR;
+ new_node->subexpr.nullifexpr = nullifexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ /*
+ * Operator of ScalarArrayOpExpr is cached if:
+ * 1) its function is not volatile itself,
+ * 2) its arguments are constants or cached expressions too.
+ * (it returns boolean so we don't need to check if it returns set)
+ */
+ ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ set_sa_opfuncid(saopexpr);
+
+ /* firstly recurse into children */
+ saopexpr = (ScalarArrayOpExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+
+ foreach(arg, saopexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &saopexpr->xpr))
+ {
+ /* return ScalarArrayOpExpr, which will not be cached */
+ return (Node *) saopexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_SCALARARRAYOPEXPR;
+ new_node->subexpr.saopexpr = saopexpr;
+
+ return (Node *) new_node;
+ }
+ }
+
+ /* otherwise recurse into children */
+ return expression_tree_mutator(node, replace_cached_expressions_mutator,
+ NULL);
+}
diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h
index b6c9b48..0dbfa12 100644
--- a/src/include/nodes/nodeFuncs.h
+++ b/src/include/nodes/nodeFuncs.h
@@ -76,5 +76,6 @@ extern bool raw_expression_tree_walker(Node *node, bool (*walker) (),
struct PlanState;
extern bool planstate_tree_walker(struct PlanState *planstate, bool (*walker) (),
void *context);
+extern Node * get_subexpr(CachedExpr *cachedexpr);
#endif /* NODEFUNCS_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f59d719..054bc61 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -155,6 +155,7 @@ typedef enum NodeTag
T_OpExpr,
T_DistinctExpr,
T_NullIfExpr,
+ T_CachedExpr,
T_ScalarArrayOpExpr,
T_BoolExpr,
T_SubLink,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 86ec82e..3f89653 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1498,4 +1498,42 @@ typedef struct OnConflictExpr
List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */
} OnConflictExpr;
+/*
+ * Discriminator for CachedExpr.
+ *
+ * Identifies the subexpression to be cached in execution (= executed only once
+ * and then used cached value) and which member in the CachedExpr->subexpr union
+ * is valid.
+ */
+typedef enum CachedSubExprType
+{
+ CACHED_FUNCEXPR, /* cached FuncExpr */
+ CACHED_OPEXPR, /* cached OpExpr */
+ CACHED_DISTINCTEXPR, /* cached DistinctExpr */
+ CACHED_NULLIFEXPR, /* cached NullIfExpr */
+ CACHED_SCALARARRAYOPEXPR /* cached ScalarArrayOpExpr */
+} CachedSubExprType;
+
+/*
+ * CachedExpr - expression node for precalculated stable and immutable functions
+ * (= they are calculated once for all output rows, but as many times as
+ * function is mentioned in query), if they don't return a set and their
+ * arguments are constants or recursively precalculated functions. The same for
+ * operators' functions.
+ */
+typedef struct CachedExpr
+{
+ Expr xpr;
+ CachedSubExprType subexprtype; /* expression to be cached */
+
+ union SubExpr
+ {
+ FuncExpr *funcexpr; /* for CACHED_FUNCEXPR */
+ OpExpr *opexpr; /* for CACHED_OPEXPR */
+ DistinctExpr *distinctexpr; /* for CACHED_DISTINCTEXPR */
+ NullIfExpr *nullifexpr; /* for CACHED_NULLIFEXPR */
+ ScalarArrayOpExpr *saopexpr; /* for CACHED_SCALARARRAYOPEXPR */
+ } subexpr;
+} CachedExpr;
+
#endif /* PRIMNODES_H */
--
1.9.1
0002-Precalculate-stable-functions-planning-and-execution-v2.patchtext/x-diff; name=0002-Precalculate-stable-functions-planning-and-execution-v2.patchDownload
From cf6bf3748e836e148edd4ef332ab2d92527be712 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 15:31:21 +0300
Subject: [PATCH 2/3] Precalculate stable functions, planning and execution v2
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- replacement nonvolatile functions and operators by appropriate cached
expressions
- planning and execution cached expressions
- regression tests
---
src/backend/executor/execExpr.c | 256 +-
src/backend/executor/execExprInterp.c | 390 ++-
src/backend/optimizer/path/allpaths.c | 9 +-
src/backend/optimizer/path/clausesel.c | 13 +
src/backend/optimizer/plan/planagg.c | 1 +
src/backend/optimizer/plan/planner.c | 28 +
src/backend/optimizer/util/clauses.c | 55 +
src/backend/utils/adt/ruleutils.c | 5 +
src/include/executor/execExpr.h | 72 +-
src/include/optimizer/planner.h | 3 +
src/include/optimizer/tlist.h | 8 +-
src/pl/plpgsql/src/pl_exec.c | 10 +
.../expected/precalculate_stable_functions.out | 2625 ++++++++++++++++++++
src/test/regress/serial_schedule | 1 +
.../regress/sql/precalculate_stable_functions.sql | 949 +++++++
15 files changed, 4344 insertions(+), 81 deletions(-)
create mode 100644 src/test/regress/expected/precalculate_stable_functions.out
create mode 100644 src/test/regress/sql/precalculate_stable_functions.sql
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 5a34a46..1127034 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -72,6 +72,13 @@ static bool isAssignmentIndirectionExpr(Expr *expr);
static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
PlanState *parent, ExprState *state,
Datum *resv, bool *resnull);
+static void ExecInitScalarArrayOpExpr(ExprEvalStep *scratch,
+ ScalarArrayOpExpr *opexpr,
+ PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull);
+static void ExecInitCachedExpr(ExprEvalStep *scratch, CachedExpr *cachedexpr,
+ PlanState *parent, ExprState *state, Datum *resv,
+ bool *resnull);
/*
@@ -865,55 +872,17 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
break;
}
- case T_ScalarArrayOpExpr:
+ case T_CachedExpr:
{
- ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
- Expr *scalararg;
- Expr *arrayarg;
- FmgrInfo *finfo;
- FunctionCallInfo fcinfo;
- AclResult aclresult;
-
- Assert(list_length(opexpr->args) == 2);
- scalararg = (Expr *) linitial(opexpr->args);
- arrayarg = (Expr *) lsecond(opexpr->args);
-
- /* Check permission to call function */
- aclresult = pg_proc_aclcheck(opexpr->opfuncid,
- GetUserId(),
- ACL_EXECUTE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_PROC,
- get_func_name(opexpr->opfuncid));
- InvokeFunctionExecuteHook(opexpr->opfuncid);
-
- /* Set up the primary fmgr lookup information */
- finfo = palloc0(sizeof(FmgrInfo));
- fcinfo = palloc0(sizeof(FunctionCallInfoData));
- fmgr_info(opexpr->opfuncid, finfo);
- fmgr_info_set_expr((Node *) node, finfo);
- InitFunctionCallInfoData(*fcinfo, finfo, 2,
- opexpr->inputcollid, NULL, NULL);
-
- /* Evaluate scalar directly into left function argument */
- ExecInitExprRec(scalararg, parent, state,
- &fcinfo->arg[0], &fcinfo->argnull[0]);
+ ExecInitCachedExpr(&scratch, (CachedExpr *) node, parent,
+ state, resv, resnull);
+ break;
+ }
- /*
- * Evaluate array argument into our return value. There's no
- * danger in that, because the return value is guaranteed to
- * be overwritten by EEOP_SCALARARRAYOP, and will not be
- * passed to any other expression.
- */
- ExecInitExprRec(arrayarg, parent, state, resv, resnull);
-
- /* And perform the operation */
- scratch.opcode = EEOP_SCALARARRAYOP;
- scratch.d.scalararrayop.element_type = InvalidOid;
- scratch.d.scalararrayop.useOr = opexpr->useOr;
- scratch.d.scalararrayop.finfo = finfo;
- scratch.d.scalararrayop.fcinfo_data = fcinfo;
- scratch.d.scalararrayop.fn_addr = finfo->fn_addr;
+ case T_ScalarArrayOpExpr:
+ {
+ ExecInitScalarArrayOpExpr(&scratch, (ScalarArrayOpExpr *) node,
+ parent, state, resv, resnull);
ExprEvalPushStep(state, &scratch);
break;
}
@@ -2675,3 +2644,196 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
}
}
}
+
+/*
+ * Prepare evaluation of a ScalarArrayOpExpr expression.
+ *
+ * This function was created to not duplicate code for ScalarArrayOpExpr and
+ * cached ScalarArrayOpExpr.
+ */
+static void
+ExecInitScalarArrayOpExpr(ExprEvalStep *scratch, ScalarArrayOpExpr *opexpr,
+ PlanState *parent, ExprState *state, Datum *resv,
+ bool *resnull)
+{
+ Expr *scalararg;
+ Expr *arrayarg;
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo;
+ AclResult aclresult;
+
+ Assert(list_length(opexpr->args) == 2);
+ scalararg = (Expr *) linitial(opexpr->args);
+ arrayarg = (Expr *) lsecond(opexpr->args);
+
+ /* Check permission to call function */
+ aclresult = pg_proc_aclcheck(opexpr->opfuncid,
+ GetUserId(),
+ ACL_EXECUTE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_PROC,
+ get_func_name(opexpr->opfuncid));
+ InvokeFunctionExecuteHook(opexpr->opfuncid);
+
+ /* Set up the primary fmgr lookup information */
+ finfo = palloc0(sizeof(FmgrInfo));
+ fcinfo = palloc0(sizeof(FunctionCallInfoData));
+ fmgr_info(opexpr->opfuncid, finfo);
+ fmgr_info_set_expr((Node *) opexpr, finfo);
+ InitFunctionCallInfoData(*fcinfo, finfo, 2,
+ opexpr->inputcollid, NULL, NULL);
+
+ /* Evaluate scalar directly into left function argument */
+ ExecInitExprRec(scalararg, parent, state,
+ &fcinfo->arg[0], &fcinfo->argnull[0]);
+
+ /*
+ * Evaluate array argument into our return value. There's no
+ * danger in that, because the return value is guaranteed to
+ * be overwritten by EEOP_SCALARARRAYOP, and will not be
+ * passed to any other expression.
+ */
+ ExecInitExprRec(arrayarg, parent, state, resv, resnull);
+
+ /* And perform the operation */
+ scratch->opcode = EEOP_SCALARARRAYOP;
+ scratch->d.scalararrayop.element_type = InvalidOid;
+ scratch->d.scalararrayop.useOr = opexpr->useOr;
+ scratch->d.scalararrayop.finfo = finfo;
+ scratch->d.scalararrayop.fcinfo_data = fcinfo;
+ scratch->d.scalararrayop.fn_addr = finfo->fn_addr;
+}
+
+/*
+ * Prepare evaluation of an CachedExpr expression.
+ */
+static void
+ExecInitCachedExpr(ExprEvalStep *scratch, CachedExpr *cachedexpr,
+ PlanState *parent, ExprState *state, Datum *resv,
+ bool *resnull)
+{
+ CacheableInlineData *subexpr = palloc0(sizeof(CacheableInlineData));
+
+ /* initialize subexpression as usual */
+ switch (cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ {
+ FuncExpr *func = cachedexpr->subexpr.funcexpr;
+
+ ExecInitFunc(scratch, (Expr *) func,
+ func->args, func->funcid, func->inputcollid,
+ parent, state);
+ }
+ break;
+ case CACHED_OPEXPR:
+ {
+ OpExpr *op = cachedexpr->subexpr.opexpr;
+
+ ExecInitFunc(scratch, (Expr *) op,
+ op->args, op->opfuncid, op->inputcollid,
+ parent, state);
+ }
+ break;
+ case CACHED_DISTINCTEXPR:
+ {
+ DistinctExpr *distinctexpr = cachedexpr->subexpr.distinctexpr;
+
+ ExecInitFunc(scratch, (Expr *) distinctexpr,
+ distinctexpr->args, distinctexpr->opfuncid,
+ distinctexpr->inputcollid, parent, state);
+ /*
+ * Change opcode of subexpression call instruction to
+ * EEOP_DISTINCT.
+ *
+ * XXX: historically we've not called the function usage
+ * pgstat infrastructure - that seems inconsistent given that
+ * we do so for normal function *and* operator evaluation. If
+ * we decided to do that here, we'd probably want separate
+ * opcodes for FUSAGE or not.
+ */
+ scratch->opcode = EEOP_DISTINCT;
+ }
+ break;
+ case CACHED_NULLIFEXPR:
+ {
+ NullIfExpr *nullifexpr = cachedexpr->subexpr.nullifexpr;
+
+ ExecInitFunc(scratch, (Expr *) nullifexpr,
+ nullifexpr->args, nullifexpr->opfuncid,
+ nullifexpr->inputcollid, parent, state);
+
+ /*
+ * Change opcode of subexpression call instruction to
+ * EEOP_NULLIF.
+ *
+ * XXX: historically we've not called the function usage
+ * pgstat infrastructure - that seems inconsistent given that
+ * we do so for normal function *and* operator evaluation. If
+ * we decided to do that here, we'd probably want separate
+ * opcodes for FUSAGE or not.
+ */
+ scratch->opcode = EEOP_NULLIF;
+ }
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ {
+ ExecInitScalarArrayOpExpr(scratch, cachedexpr->subexpr.saopexpr,
+ parent, state, resv, resnull);
+ }
+ break;
+ }
+
+ /* copy data from scratch */
+ switch (scratch->opcode)
+ {
+ case EEOP_FUNCEXPR:
+ case EEOP_FUNCEXPR_STRICT:
+ case EEOP_FUNCEXPR_FUSAGE:
+ case EEOP_FUNCEXPR_STRICT_FUSAGE:
+ case EEOP_DISTINCT:
+ case EEOP_NULLIF:
+ subexpr->func = scratch->d.func;
+ break;
+ case EEOP_SCALARARRAYOP:
+ subexpr->scalararrayop = scratch->d.scalararrayop;
+ break;
+ default:
+ elog(ERROR, "unknown opcode for caching expression");
+ break;
+ }
+
+ /* initialize scratch as cached expression */
+ switch (scratch->opcode)
+ {
+ case EEOP_FUNCEXPR:
+ scratch->opcode = EEOP_CACHED_FUNCEXPR;
+ break;
+ case EEOP_FUNCEXPR_STRICT:
+ scratch->opcode = EEOP_CACHED_FUNCEXPR_STRICT;
+ break;
+ case EEOP_FUNCEXPR_FUSAGE:
+ scratch->opcode = EEOP_CACHED_FUNCEXPR_FUSAGE;
+ break;
+ case EEOP_FUNCEXPR_STRICT_FUSAGE:
+ scratch->opcode = EEOP_CACHED_FUNCEXPR_STRICT_FUSAGE;
+ break;
+ case EEOP_DISTINCT:
+ scratch->opcode = EEOP_CACHED_DISTINCT;
+ break;
+ case EEOP_NULLIF:
+ scratch->opcode = EEOP_CACHED_NULLIF;
+ break;
+ case EEOP_SCALARARRAYOP:
+ scratch->opcode = EEOP_CACHED_SCALARARRAYOP;
+ break;
+ default:
+ elog(ERROR, "unknown opcode for caching expression");
+ break;
+ }
+ scratch->d.cachedexpr.subexpr = subexpr;
+ scratch->d.cachedexpr.isExecuted = false;
+ scratch->d.cachedexpr.resnull = false;
+ scratch->d.cachedexpr.resvalue = (Datum) 0;
+ ExprEvalPushStep(state, scratch);
+}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index fed0052..5f456bc 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -279,6 +279,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
TupleTableSlot *innerslot;
TupleTableSlot *outerslot;
TupleTableSlot *scanslot;
+ MemoryContext oldContext; /* for EEOP_CACHED_* */
/*
* This array has to be in the same order as enum ExprEvalOp.
@@ -309,6 +310,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_FUNCEXPR_STRICT,
&&CASE_EEOP_FUNCEXPR_FUSAGE,
&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+ &&CASE_EEOP_CACHED_FUNCEXPR,
+ &&CASE_EEOP_CACHED_FUNCEXPR_STRICT,
+ &&CASE_EEOP_CACHED_FUNCEXPR_FUSAGE,
+ &&CASE_EEOP_CACHED_FUNCEXPR_STRICT_FUSAGE,
+ &&CASE_EEOP_CACHED_DISTINCT,
+ &&CASE_EEOP_CACHED_NULLIF,
+ &&CASE_EEOP_CACHED_SCALARARRAYOP,
&&CASE_EEOP_BOOL_AND_STEP_FIRST,
&&CASE_EEOP_BOOL_AND_STEP,
&&CASE_EEOP_BOOL_AND_STEP_LAST,
@@ -721,6 +729,346 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_CACHED_FUNCEXPR)
+ {
+ struct FuncData *func = &(op->d.cachedexpr.subexpr->func);
+ FunctionCallInfo fcinfo = func->fcinfo_data;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ *op->resnull = op->d.cachedexpr.resnull;
+ *op->resvalue = op->d.cachedexpr.resvalue;
+
+ goto cached_funcexpr;
+ }
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* execute function as usual */
+ fcinfo->isnull = false;
+ *op->resvalue = (func->fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ cached_funcexpr:
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHED_FUNCEXPR_STRICT)
+ {
+ struct FuncData *func = &(op->d.cachedexpr.subexpr->func);
+ FunctionCallInfo fcinfo = func->fcinfo_data;
+ bool *argnull = fcinfo->argnull;
+ int argno;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ *op->resnull = op->d.cachedexpr.resnull;
+ if (!(*op->resnull))
+ *op->resvalue = op->d.cachedexpr.resvalue;
+
+ goto cached_funcexpr_strict;
+ }
+
+ /* strict function, so check for NULL args */
+ for (argno = 0; argno < func->nargs; argno++)
+ {
+ if (argnull[argno])
+ {
+ *op->resnull = true;
+
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.isExecuted = true;
+
+ goto cached_strictfail;
+ }
+ }
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* execute function as usual */
+ fcinfo->isnull = false;
+ *op->resvalue = (func->fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ cached_funcexpr_strict:
+ cached_strictfail:
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHED_FUNCEXPR_FUSAGE)
+ {
+ struct FuncData *func = &(op->d.cachedexpr.subexpr->func);
+ FunctionCallInfo fcinfo = func->fcinfo_data;
+ PgStat_FunctionCallUsage fcusage;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ *op->resnull = op->d.cachedexpr.resnull;
+ *op->resvalue = op->d.cachedexpr.resvalue;
+
+ goto cached_funcexpr_fusage;
+ }
+
+ pgstat_init_function_usage(fcinfo, &fcusage);
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* execute function as usual */
+ fcinfo->isnull = false;
+ *op->resvalue = (func->fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ pgstat_end_function_usage(&fcusage, true);
+
+ cached_funcexpr_fusage:
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHED_FUNCEXPR_STRICT_FUSAGE)
+ {
+ struct FuncData *func = &(op->d.cachedexpr.subexpr->func);
+ FunctionCallInfo fcinfo = func->fcinfo_data;
+ PgStat_FunctionCallUsage fcusage;
+ bool *argnull = fcinfo->argnull;
+ int argno;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ *op->resnull = op->d.cachedexpr.resnull;
+ if (!(*op->resnull))
+ *op->resvalue = op->d.cachedexpr.resvalue;
+
+ goto cached_funcexpr_strict_fusage;
+ }
+
+ /* strict function, so check for NULL args */
+ for (argno = 0; argno < func->nargs; argno++)
+ {
+ if (argnull[argno])
+ {
+ *op->resnull = true;
+
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.isExecuted = true;
+
+ goto cached_strictfail_fusage;
+ }
+ }
+
+ pgstat_init_function_usage(fcinfo, &fcusage);
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* execute function as usual */
+ fcinfo->isnull = false;
+ *op->resvalue = (func->fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ pgstat_end_function_usage(&fcusage, true);
+
+ cached_funcexpr_strict_fusage:
+ cached_strictfail_fusage:
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHED_DISTINCT)
+ {
+ /*
+ * IS DISTINCT FROM must evaluate arguments (already done into
+ * fcinfo->arg/argnull) to determine whether they are NULL; if
+ * either is NULL then the result is determined. If neither is
+ * NULL, then proceed to evaluate the comparison function, which
+ * is just the type's standard equality operator. We need not
+ * care whether that function is strict. Because the handling of
+ * nulls is different, we can't just reuse EEOP_CACHED_FUNCEXPR.
+ */
+ struct FuncData *func = &(op->d.cachedexpr.subexpr->func);
+ FunctionCallInfo fcinfo = func->fcinfo_data;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ *op->resnull = op->d.cachedexpr.resnull;
+ *op->resvalue = op->d.cachedexpr.resvalue;
+
+ goto cached_distinct;
+ }
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* check function arguments for NULLness */
+ if (fcinfo->argnull[0] && fcinfo->argnull[1])
+ {
+ /* Both NULL? Then is not distinct... */
+ *op->resvalue = BoolGetDatum(false);
+ *op->resnull = false;
+ }
+ else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+ {
+ /* Only one is NULL? Then is distinct... */
+ *op->resvalue = BoolGetDatum(true);
+ *op->resnull = false;
+ }
+ else
+ {
+ /* Neither null, so apply the equality function */
+ Datum eqresult;
+
+ fcinfo->isnull = false;
+ eqresult = (func->fn_addr) (fcinfo);
+ /* Must invert result of "="; safe to do even if null */
+ *op->resvalue = BoolGetDatum(!DatumGetBool(eqresult));
+ *op->resnull = fcinfo->isnull;
+ }
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ cached_distinct:
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHED_NULLIF)
+ {
+ /*
+ * The arguments are already evaluated into fcinfo->arg/argnull.
+ */
+ struct FuncData *func = &(op->d.cachedexpr.subexpr->func);
+ FunctionCallInfo fcinfo = func->fcinfo_data;
+
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ *op->resnull = op->d.cachedexpr.resnull;
+ *op->resvalue = op->d.cachedexpr.resvalue;
+
+ goto cached_nullif;
+ }
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /* if either argument is NULL they can't be equal */
+ if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+ {
+ Datum result;
+
+ fcinfo->isnull = false;
+ result = (func->fn_addr) (fcinfo);
+
+ /* if the arguments are equal return null */
+ if (!fcinfo->isnull && DatumGetBool(result))
+ {
+ *op->resvalue = (Datum) 0;
+ *op->resnull = true;
+
+ goto cache_nullif;
+ }
+ }
+
+ /* Arguments aren't equal, so return the first one */
+ *op->resvalue = fcinfo->arg[0];
+ *op->resnull = fcinfo->argnull[0];
+
+ cache_nullif:
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ cached_nullif:
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHED_SCALARARRAYOP)
+ {
+ if (op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result */
+ *op->resnull = op->d.cachedexpr.resnull;
+ if (!(*op->resnull))
+ *op->resvalue = op->d.cachedexpr.resvalue;
+
+ goto cached_scalararrayop;
+ }
+
+ /*
+ * If function is cacheable then switch per-query memory context.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ /*
+ * Execute function as usual.
+ * Too complex for an inline implementation.
+ */
+ ExecEvalScalarArrayOp(state, op);
+
+ /* save result and switch memory context back */
+ op->d.cachedexpr.resnull = *op->resnull;
+ op->d.cachedexpr.resvalue = *op->resvalue;
+ op->d.cachedexpr.isExecuted = true;
+ MemoryContextSwitchTo(oldContext);
+
+ cached_scalararrayop:
+ EEO_NEXT();
+ }
+
/*
* If any of its clauses is FALSE, an AND's result is FALSE regardless
* of the states of the rest of the clauses, so we can stop evaluating
@@ -2880,9 +3228,10 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
void
ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
{
- FunctionCallInfo fcinfo = op->d.scalararrayop.fcinfo_data;
- bool useOr = op->d.scalararrayop.useOr;
- bool strictfunc = op->d.scalararrayop.finfo->fn_strict;
+ struct ScalarArrayOpData *scalararrayop;
+ FunctionCallInfo fcinfo;
+ bool useOr;
+ bool strictfunc;
ArrayType *arr;
int nitems;
Datum result;
@@ -2895,6 +3244,23 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
bits8 *bitmap;
int bitmask;
+ switch (ExecEvalStepOp(state, op))
+ {
+ case EEOP_SCALARARRAYOP:
+ scalararrayop = &(op->d.scalararrayop);
+ break;
+ case EEOP_CACHED_SCALARARRAYOP:
+ scalararrayop = &(op->d.cachedexpr.subexpr->scalararrayop);
+ break;
+ default:
+ elog(ERROR, "unknown opcode for evaluation \"scalar op ANY/ALL (array)\"");
+ break;
+ }
+
+ fcinfo = scalararrayop->fcinfo_data;
+ useOr = scalararrayop->useOr;
+ strictfunc = scalararrayop->finfo->fn_strict;
+
/*
* If the array is NULL then we return NULL --- it's not very meaningful
* to do anything else, even if the operator isn't strict.
@@ -2933,18 +3299,18 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
* We arrange to look up info about the element type only once per series
* of calls, assuming the element type doesn't change underneath us.
*/
- if (op->d.scalararrayop.element_type != ARR_ELEMTYPE(arr))
+ if (scalararrayop->element_type != ARR_ELEMTYPE(arr))
{
get_typlenbyvalalign(ARR_ELEMTYPE(arr),
- &op->d.scalararrayop.typlen,
- &op->d.scalararrayop.typbyval,
- &op->d.scalararrayop.typalign);
- op->d.scalararrayop.element_type = ARR_ELEMTYPE(arr);
+ &scalararrayop->typlen,
+ &scalararrayop->typbyval,
+ &scalararrayop->typalign);
+ scalararrayop->element_type = ARR_ELEMTYPE(arr);
}
- typlen = op->d.scalararrayop.typlen;
- typbyval = op->d.scalararrayop.typbyval;
- typalign = op->d.scalararrayop.typalign;
+ typlen = scalararrayop->typlen;
+ typbyval = scalararrayop->typbyval;
+ typalign = scalararrayop->typalign;
/* Initialize result appropriately depending on useOr */
result = BoolGetDatum(!useOr);
@@ -2984,7 +3350,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
else
{
fcinfo->isnull = false;
- thisresult = (op->d.scalararrayop.fn_addr) (fcinfo);
+ thisresult = (scalararrayop->fn_addr) (fcinfo);
}
/* Combine results per OR or AND semantics */
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index b93b4fc..a322255 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -378,7 +378,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
set_subquery_pathlist(root, rel, rti, rte);
break;
case RTE_FUNCTION:
- set_function_size_estimates(root, rel);
+ {
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo);
+ set_function_size_estimates(root, rel);
+ }
break;
case RTE_TABLEFUNC:
set_tablefunc_size_estimates(root, rel);
@@ -517,6 +521,9 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
*/
check_index_predicates(root, rel);
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo);
+
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
}
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 758ddea..fc799f1 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
@@ -825,6 +826,18 @@ clause_selectivity(PlannerInfo *root,
jointype,
sjinfo);
}
+ else if (IsA(clause, CachedExpr))
+ {
+ /*
+ * Not sure this case is needed, but it can't hurt.
+ * Calculate selectivity of subexpression.
+ */
+ s1 = clause_selectivity(root,
+ get_subexpr((CachedExpr *) clause),
+ varRelid,
+ jointype,
+ sjinfo);
+ }
else
{
/*
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 5565736..7a28764 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -38,6 +38,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 552b73d..7c68d6d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6088,6 +6088,34 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
return result;
}
+/*
+ * replace_pathtarget_cached_expressions
+ * Replace cached expresisons in a PathTarget tlist.
+ *
+ * As a notational convenience, returns the same PathTarget pointer passed in.
+ */
+PathTarget *
+replace_pathtarget_cached_expressions(PathTarget *target)
+{
+ target->exprs = (List *) replace_cached_expressions_mutator(
+ (Node *) target->exprs);
+
+ return target;
+}
+
+/*
+ * replace_qual_cached_expressions
+ * Replace cacehd expressions in a WHERE clause. The input can be either an
+ * implicitly-ANDed list of boolean expressions, or a list of RestrictInfo
+ * nodes.
+ */
+List *
+replace_qual_cached_expressions(List *quals)
+{
+ /* No setup needed for tree walk, so away we go */
+ return (List *) replace_cached_expressions_mutator((Node *) quals);
+}
+
static Node *
replace_cached_expressions_mutator(Node *node)
{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a1dafc8..0c0284a 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2758,6 +2758,61 @@ eval_const_expressions_mutator(Node *node,
newexpr->location = expr->location;
return (Node *) newexpr;
}
+ case T_CachedExpr:
+ {
+ CachedExpr *cachedexpr = (CachedExpr *) node;
+ Node *new_subexpr = eval_const_expressions_mutator(
+ get_subexpr(cachedexpr), context);
+ CachedExpr *new_cachedexpr;
+
+ /*
+ * If unsafe transformations are used cached expression should
+ * be always simplified.
+ */
+ if (context->estimate)
+ Assert(IsA(new_subexpr, Const));
+
+ if (IsA(new_subexpr, Const))
+ {
+ /* successfully simplified it */
+ return new_subexpr;
+ }
+ else
+ {
+ /*
+ * The expression cannot be simplified any further, so build
+ * and return a replacement CachedExpr node using the
+ * possibly-simplified arguments of subexpression.
+ */
+ new_cachedexpr = makeNode(CachedExpr);
+ new_cachedexpr->subexprtype = cachedexpr->subexprtype;
+ switch (new_cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ new_cachedexpr->subexpr.funcexpr = (FuncExpr *)
+ new_subexpr;
+ break;
+ case CACHED_OPEXPR:
+ new_cachedexpr->subexpr.opexpr = (OpExpr *)
+ new_subexpr;
+ break;
+ case CACHED_DISTINCTEXPR:
+ new_cachedexpr->subexpr.distinctexpr =
+ (DistinctExpr *) new_subexpr;
+ break;
+ case CACHED_NULLIFEXPR:
+ new_cachedexpr->subexpr.nullifexpr = (NullIfExpr *)
+ new_subexpr;
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ new_cachedexpr->subexpr.saopexpr =
+ (ScalarArrayOpExpr *) new_subexpr;
+ break;
+ }
+
+ return (Node *) new_cachedexpr;
+ }
+ }
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 43b1475..838389d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7720,6 +7720,11 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_CachedExpr:
+ get_rule_expr(get_subexpr((CachedExpr *) node), context,
+ showimplicit);
+ break;
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 86fdb33..b515cc2 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -85,6 +85,15 @@ typedef enum ExprEvalOp
EEOP_FUNCEXPR_FUSAGE,
EEOP_FUNCEXPR_STRICT_FUSAGE,
+ /* evaluate CachedExpr */
+ EEOP_CACHED_FUNCEXPR,
+ EEOP_CACHED_FUNCEXPR_STRICT,
+ EEOP_CACHED_FUNCEXPR_FUSAGE,
+ EEOP_CACHED_FUNCEXPR_STRICT_FUSAGE,
+ EEOP_CACHED_DISTINCT,
+ EEOP_CACHED_NULLIF,
+ EEOP_CACHED_SCALARARRAYOP,
+
/*
* Evaluate boolean AND expression, one step per subexpression. FIRST/LAST
* subexpressions are special-cased for performance. Since AND always has
@@ -217,6 +226,39 @@ typedef enum ExprEvalOp
} ExprEvalOp;
+/* Inline data of ExprEvalStep for operations that can be cacheable */
+typedef union CacheableInlineData
+{
+ /*
+ * For EEOP_FUNCEXPR_* / NULLIF / DISTINCT /
+ * EEOP_CACHED_FUNCEXPR_* / NULLIF / DISTINCT.
+ */
+ struct FuncData
+ {
+ FmgrInfo *finfo; /* function's lookup data */
+ FunctionCallInfo fcinfo_data; /* arguments etc */
+ /* faster to access without additional indirection: */
+ PGFunction fn_addr; /* actual call address */
+ int nargs; /* number of arguments */
+ } func;
+
+ /* for EEOP_SCALARARRAYOP / EEOP_CACHED_SCALARARRAYOP */
+ struct ScalarArrayOpData
+ {
+ /* element_type/typlen/typbyval/typalign are filled at runtime */
+ Oid element_type; /* InvalidOid if not yet filled */
+ bool useOr; /* use OR or AND semantics? */
+ int16 typlen; /* array element type storage info */
+ bool typbyval;
+ char typalign;
+ FmgrInfo *finfo; /* function's lookup data */
+ FunctionCallInfo fcinfo_data; /* arguments etc */
+ /* faster to access without additional indirection: */
+ PGFunction fn_addr; /* actual call address */
+ } scalararrayop;
+} CacheableInlineData;
+
+
typedef struct ExprEvalStep
{
/*
@@ -289,14 +331,18 @@ typedef struct ExprEvalStep
} constval;
/* for EEOP_FUNCEXPR_* / NULLIF / DISTINCT */
+ struct FuncData func;
+
+ /* for EEOP_CACHED_* */
struct
{
- FmgrInfo *finfo; /* function's lookup data */
- FunctionCallInfo fcinfo_data; /* arguments etc */
- /* faster to access without additional indirection: */
- PGFunction fn_addr; /* actual call address */
- int nargs; /* number of arguments */
- } func;
+ /* cached ExprEvalOp data */
+ CacheableInlineData *subexpr;
+
+ bool isExecuted;
+ bool resnull;
+ Datum resvalue;
+ } cachedexpr;
/* for EEOP_BOOL_*_STEP */
struct
@@ -500,19 +546,7 @@ typedef struct ExprEvalStep
} convert_rowtype;
/* for EEOP_SCALARARRAYOP */
- struct
- {
- /* element_type/typlen/typbyval/typalign are filled at runtime */
- Oid element_type; /* InvalidOid if not yet filled */
- bool useOr; /* use OR or AND semantics? */
- int16 typlen; /* array element type storage info */
- bool typbyval;
- char typalign;
- FmgrInfo *finfo; /* function's lookup data */
- FunctionCallInfo fcinfo_data; /* arguments etc */
- /* faster to access without additional indirection: */
- PGFunction fn_addr; /* actual call address */
- } scalararrayop;
+ struct ScalarArrayOpData scalararrayop;
/* for EEOP_XMLEXPR */
struct
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index f3aaa23..bbadcdd 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -59,4 +59,7 @@ extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);
extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti);
+extern PathTarget *replace_pathtarget_cached_expressions(PathTarget *target);
+extern List *replace_qual_cached_expressions(List *quals);
+
#endif /* PLANNER_H */
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index ccb93d8..7488bd2 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -65,8 +65,12 @@ extern void split_pathtarget_at_srfs(PlannerInfo *root,
PathTarget *target, PathTarget *input_target,
List **targets, List **targets_contain_srfs);
-/* Convenience macro to get a PathTarget with valid cost/width fields */
+/*
+ * Convenience macro to get a PathTarget with valid cost/width fields and
+ * cached expressions.
+ */
#define create_pathtarget(root, tlist) \
- set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist))
+ set_pathtarget_cost_width(root, replace_pathtarget_cached_expressions( \
+ make_pathtarget_from_tlist(tlist)))
#endif /* TLIST_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 7a40c99..2e27052 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -6535,6 +6535,16 @@ exec_simple_check_node(Node *node)
return TRUE;
}
+ case T_CachedExpr:
+ {
+ /*
+ * If CachedExpr will not be initialized by ExecInitCachedExpr
+ * possibly it will use cached value when it shouldn't (for
+ * example, snapshot has changed), so return false.
+ */
+ return FALSE;
+ }
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/test/regress/expected/precalculate_stable_functions.out b/src/test/regress/expected/precalculate_stable_functions.out
new file mode 100644
index 0000000..093e6f8
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions.out
@@ -0,0 +1,2625 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create types and tables for testing
+CREATE TYPE my_integer AS (value integer);
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+-- Create volatile functions for testing
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_array_int (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create stable functions for testing
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_array_int (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+-- Create immutable functions for testing
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create operators for testing
+CREATE operator === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE operator ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE operator ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE operator ====== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+CREATE operator ==== (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- Simple functions testing
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- Functions with constant arguments and nested functions testing
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Nested and strict operators testing
+-- (also partly mixed functions and operators testing)
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- IS DISTINCT FROM expression testing
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- IS DISTINCT FROM expressions with null arguments testing
+SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Nested IS DISTINCT FROM expression testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- NULLIF expressions with null arguments testing
+SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+
+
+
+
+(4 rows)
+
+-- Nested NULLIF expression testing
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing (also partly mixed functions and "scalar op ANY/ALL
+-- (array)" / "scalar IN (2 or more values)" expressions testing)
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and operators testing
+-- (most of it was earlier in Nested and strict operators testing)
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and IS DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer),
+ (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT equal_booleans_stl_strict(
+ (x_stl() IS DISTINCT FROM 1),
+ (x_stl() IS DISTINCT FROM 2)
+)
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF(x_stl_my_integer(), '(1)'::my_integer),
+ NULLIF(x_stl_my_integer(), '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal integers stable
+ equal_integers_stl
+--------------------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" /
+-- "scalar IN (2 or more values)" expressions testing)
+SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x;
+NOTICE: s array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x;
+NOTICE: s array_int
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed operators and IS DISTINCT FROM expressions testing
+-- should not be precalculated
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed operators and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+-- should not be precalculated
+SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed IS DISTINCT FROM and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ nullif
+--------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ nullif
+--------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or
+-- more values)" expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ (1 === ANY('{2, 3}')) IS DISTINCT FROM
+ (1 === ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ (1 ==== ANY('{2, 3}')) IS DISTINCT FROM
+ (1 ==== ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Tracking functions testing
+SET track_functions TO 'all';
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SET track_functions TO DEFAULT;
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+BEGIN;
+SELECT simple();
+ simple
+--------
+ 2
+(1 row)
+
+INSERT INTO two VALUES (3);
+SELECT simple();
+ simple
+--------
+ 3
+(1 row)
+
+ROLLBACK;
+-- Drop tables for testing
+DROP TABLE two;
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 04206c3..f2710b9 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -179,3 +179,4 @@ test: with
test: xml
test: event_trigger
test: stats
+test: precalculate_stable_functions
diff --git a/src/test/regress/sql/precalculate_stable_functions.sql b/src/test/regress/sql/precalculate_stable_functions.sql
new file mode 100644
index 0000000..a59791d
--- /dev/null
+++ b/src/test/regress/sql/precalculate_stable_functions.sql
@@ -0,0 +1,949 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+
+-- Create types and tables for testing
+
+CREATE TYPE my_integer AS (value integer);
+
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+
+-- Create volatile functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_array_int (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create stable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_array_int (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create immutable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create operators for testing
+
+CREATE operator === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE operator ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE operator ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE operator ====== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+
+CREATE operator ==== (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- Simple functions testing
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+-- WHERE clause testing
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+-- Functions with constant arguments and nested functions testing
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions testing
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions with null arguments testing
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+-- Operators testing
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+
+-- Nested and strict operators testing
+-- (also partly mixed functions and operators testing)
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x;
+SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x;
+
+-- IS DISTINCT FROM expression testing
+
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+
+-- IS DISTINCT FROM expressions with null arguments testing
+
+SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+
+-- Nested IS DISTINCT FROM expression testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+-- NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- NULLIF expressions with null arguments testing
+
+SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x;
+
+SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x;
+
+-- Nested NULLIF expression testing
+
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+
+SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x;
+SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+
+SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x;
+SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x;
+
+SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x;
+SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x;
+
+-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing (also partly mixed functions and "scalar op ANY/ALL
+-- (array)" / "scalar IN (2 or more values)" expressions testing)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and operators testing
+-- (most of it was earlier in Nested and strict operators testing)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+-- Mixed functions and IS DISTINCT FROM expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer),
+ (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT equal_booleans_stl_strict(
+ (x_stl() IS DISTINCT FROM 1),
+ (x_stl() IS DISTINCT FROM 2)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and NULLIF expressions testing
+
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF(x_stl_my_integer(), '(1)'::my_integer),
+ NULLIF(x_stl_my_integer(), '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2))
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" /
+-- "scalar IN (2 or more values)" expressions testing)
+
+SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x;
+SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x;
+
+-- Mixed operators and IS DISTINCT FROM expressions testing
+
+-- should not be precalculated
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+
+-- Mixed operators and NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x;
+
+-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+
+-- should not be precalculated
+SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- Mixed IS DISTINCT FROM and NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or
+-- more values)" expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ (1 === ANY('{2, 3}')) IS DISTINCT FROM
+ (1 === ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ (1 ==== ANY('{2, 3}')) IS DISTINCT FROM
+ (1 ==== ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- Tracking functions testing
+
+SET track_functions TO 'all';
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+SET track_functions TO DEFAULT;
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+
+BEGIN;
+SELECT simple();
+INSERT INTO two VALUES (3);
+SELECT simple();
+ROLLBACK;
+
+-- Drop tables for testing
+
+DROP TABLE two;
--
1.9.1
0003-Precalculate-stable-functions-costs-v2.patchtext/x-diff; name=0003-Precalculate-stable-functions-costs-v2.patchDownload
From 823e2aa5477342875b73b9941bdc9ed28d3ebe01 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 16:05:38 +0300
Subject: [PATCH 3/3] Precalculate stable functions, costs v2
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- cost changes for cached expressions (according to their behaviour)
---
src/backend/optimizer/path/costsize.c | 89 ++++++++++++++++++++++++++---------
1 file changed, 67 insertions(+), 22 deletions(-)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 52643d0..505772a 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -140,6 +140,7 @@ static MergeScanSelCache *cached_scansel(PlannerInfo *root,
PathKey *pathkey);
static void cost_rescan(PlannerInfo *root, Path *path,
Cost *rescan_startup_cost, Cost *rescan_total_cost);
+static double cost_eval_cacheable_expr_per_tuple(Node *node);
static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
ParamPathInfo *param_info,
@@ -3464,6 +3465,59 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
*cost = context.total;
}
+/*
+ * cost_eval_cacheable_expr_per_tuple
+ * Evaluate per tuple cost for expressions that can be cacheable.
+ *
+ * This function was created to not duplicate code for some expression and
+ * cached some expression.
+ */
+static double
+cost_eval_cacheable_expr_per_tuple(Node *node)
+{
+ double result;
+
+ /*
+ * For each operator or function node in the given tree, we charge the
+ * estimated execution cost given by pg_proc.procost (remember to multiply
+ * this by cpu_operator_cost).
+ */
+ if (IsA(node, FuncExpr))
+ {
+ result = get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+ }
+ else if (IsA(node, OpExpr) ||
+ IsA(node, DistinctExpr) ||
+ IsA(node, NullIfExpr))
+ {
+ OpExpr *opexpr = (OpExpr *) node;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ result = get_func_cost(opexpr->opfuncid) * cpu_operator_cost;
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ /*
+ * Estimate that the operator will be applied to about half of the
+ * array elements before the answer is determined.
+ */
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
+ Node *arraynode = (Node *) lsecond(saop->args);
+
+ set_sa_opfuncid(saop);
+ result = get_func_cost(saop->opfuncid) * cpu_operator_cost *
+ estimate_array_length(arraynode) * 0.5;
+ }
+ else
+ {
+ elog(ERROR, "non cacheable expression node type: %d", (int) nodeTag(node));
+ }
+
+ return result;
+}
+
static bool
cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
{
@@ -3537,32 +3591,23 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
* moreover, since our rowcount estimates for functions tend to be pretty
* phony, the results would also be pretty phony.
*/
- if (IsA(node, FuncExpr))
+ if (IsA(node, FuncExpr) ||
+ IsA(node, OpExpr) ||
+ IsA(node, DistinctExpr) ||
+ IsA(node, NullIfExpr) ||
+ IsA(node, ScalarArrayOpExpr))
{
- context->total.per_tuple +=
- get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+ context->total.per_tuple += cost_eval_cacheable_expr_per_tuple(node);
}
- else if (IsA(node, OpExpr) ||
- IsA(node, DistinctExpr) ||
- IsA(node, NullIfExpr))
- {
- /* rely on struct equivalence to treat these all alike */
- set_opfuncid((OpExpr *) node);
- context->total.per_tuple +=
- get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost;
- }
- else if (IsA(node, ScalarArrayOpExpr))
- {
+ else if (IsA(node, CachedExpr))
+ {
/*
- * Estimate that the operator will be applied to about half of the
- * array elements before the answer is determined.
+ * Calculate subexpression cost per tuple as usual and add it to startup
+ * cost (because subexpression will be executed only once for all
+ * tuples).
*/
- ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
- Node *arraynode = (Node *) lsecond(saop->args);
-
- set_sa_opfuncid(saop);
- context->total.per_tuple += get_func_cost(saop->opfuncid) *
- cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
+ context->total.startup += cost_eval_cacheable_expr_per_tuple(
+ get_subexpr((CachedExpr *) node));
}
else if (IsA(node, Aggref) ||
IsA(node, WindowFunc))
--
1.9.1
Here's v2 of the patches. Changes from v1:
And here there's v3 of planning and execution: common executor steps for
all types of cached expression.
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0002-Precalculate-stable-functions-planning-and-execution-v3.patchtext/x-diff; charset=us-ascii; name=0002-Precalculate-stable-functions-planning-and-execution-v3.patchDownload
From 5e89221251670526eb2b5750868ac73eee48f10b Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 15:31:21 +0300
Subject: [PATCH 2/3] Precalculate stable functions, planning and execution v3
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- replacement nonvolatile functions and operators by appropriate cached
expressions
- planning and execution cached expressions
- regression tests
---
src/backend/executor/execExpr.c | 37 +
src/backend/executor/execExprInterp.c | 37 +
src/backend/optimizer/path/allpaths.c | 9 +-
src/backend/optimizer/path/clausesel.c | 13 +
src/backend/optimizer/plan/planagg.c | 1 +
src/backend/optimizer/plan/planner.c | 28 +
src/backend/optimizer/util/clauses.c | 55 +
src/backend/utils/adt/ruleutils.c | 5 +
src/include/executor/execExpr.h | 19 +
src/include/optimizer/planner.h | 3 +
src/include/optimizer/tlist.h | 8 +-
src/pl/plpgsql/src/pl_exec.c | 10 +
.../expected/precalculate_stable_functions.out | 2625 ++++++++++++++++++++
src/test/regress/serial_schedule | 1 +
.../regress/sql/precalculate_stable_functions.sql | 949 +++++++
15 files changed, 3797 insertions(+), 3 deletions(-)
create mode 100644 src/test/regress/expected/precalculate_stable_functions.out
create mode 100644 src/test/regress/sql/precalculate_stable_functions.sql
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 5a34a46..3c2068d 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -865,6 +865,43 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
break;
}
+ case T_CachedExpr:
+ {
+ int adjust_jump;
+
+ /*
+ * Allocate and fill scratch memory used by all steps of
+ * CachedExpr evaluation.
+ */
+ scratch.d.cachedexpr.isExecuted = (bool *) palloc(sizeof(bool));
+ scratch.d.cachedexpr.resnull = (bool *) palloc(sizeof(bool));
+ scratch.d.cachedexpr.resvalue = (Datum *) palloc(sizeof(Datum));
+
+ *scratch.d.cachedexpr.isExecuted = false;
+ *scratch.d.cachedexpr.resnull = false;
+ *scratch.d.cachedexpr.resvalue = (Datum) 0;
+ scratch.d.cachedexpr.jumpdone = -1;
+
+ /* add EEOP_CACHEDEXPR_IF_CACHED step */
+ scratch.opcode = EEOP_CACHEDEXPR_IF_CACHED;
+ ExprEvalPushStep(state, &scratch);
+ adjust_jump = state->steps_len - 1;
+
+ /* add subexpression steps */
+ ExecInitExprRec((Expr *) get_subexpr((CachedExpr *) node),
+ parent, state, resv, resnull);
+
+ /* add EEOP_CACHEDEXPR_SUBEXPR_END step */
+ scratch.opcode = EEOP_CACHEDEXPR_SUBEXPR_END;
+ ExprEvalPushStep(state, &scratch);
+
+ /* adjust jump target */
+ state->steps[adjust_jump].d.cachedexpr.jumpdone =
+ state->steps_len;
+
+ break;
+ }
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index fed0052..ac7b7f8 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -279,6 +279,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
TupleTableSlot *innerslot;
TupleTableSlot *outerslot;
TupleTableSlot *scanslot;
+ MemoryContext oldContext; /* for EEOP_CACHEDEXPR_* */
/*
* This array has to be in the same order as enum ExprEvalOp.
@@ -309,6 +310,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_FUNCEXPR_STRICT,
&&CASE_EEOP_FUNCEXPR_FUSAGE,
&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+ &&CASE_EEOP_CACHEDEXPR_IF_CACHED,
+ &&CASE_EEOP_CACHEDEXPR_SUBEXPR_END,
&&CASE_EEOP_BOOL_AND_STEP_FIRST,
&&CASE_EEOP_BOOL_AND_STEP,
&&CASE_EEOP_BOOL_AND_STEP_LAST,
@@ -721,6 +724,40 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_CACHEDEXPR_IF_CACHED)
+ {
+ if (*op->d.cachedexpr.isExecuted)
+ {
+ /* use saved result and skip subexpression evaluation */
+ *op->resnull = *op->d.cachedexpr.resnull;
+ if (!(*op->resnull))
+ *op->resvalue = *op->d.cachedexpr.resvalue;
+
+ EEO_JUMP(op->d.cachedexpr.jumpdone);
+ }
+
+ /*
+ * Switch per-query memory context for subexpression evaluation.
+ * It is necessary to save result between all tuples.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHEDEXPR_SUBEXPR_END)
+ {
+ /* save result and switch memory context back */
+ *op->d.cachedexpr.resnull = *op->resnull;
+ if (!(*op->resnull))
+ *op->d.cachedexpr.resvalue = *op->resvalue;
+ *op->d.cachedexpr.isExecuted = true;
+
+ MemoryContextSwitchTo(oldContext);
+
+ EEO_NEXT();
+ }
+
/*
* If any of its clauses is FALSE, an AND's result is FALSE regardless
* of the states of the rest of the clauses, so we can stop evaluating
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index b93b4fc..a322255 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -378,7 +378,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
set_subquery_pathlist(root, rel, rti, rte);
break;
case RTE_FUNCTION:
- set_function_size_estimates(root, rel);
+ {
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo);
+ set_function_size_estimates(root, rel);
+ }
break;
case RTE_TABLEFUNC:
set_tablefunc_size_estimates(root, rel);
@@ -517,6 +521,9 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
*/
check_index_predicates(root, rel);
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo);
+
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
}
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 758ddea..fc799f1 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
@@ -825,6 +826,18 @@ clause_selectivity(PlannerInfo *root,
jointype,
sjinfo);
}
+ else if (IsA(clause, CachedExpr))
+ {
+ /*
+ * Not sure this case is needed, but it can't hurt.
+ * Calculate selectivity of subexpression.
+ */
+ s1 = clause_selectivity(root,
+ get_subexpr((CachedExpr *) clause),
+ varRelid,
+ jointype,
+ sjinfo);
+ }
else
{
/*
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 5565736..7a28764 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -38,6 +38,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 552b73d..7c68d6d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6088,6 +6088,34 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
return result;
}
+/*
+ * replace_pathtarget_cached_expressions
+ * Replace cached expresisons in a PathTarget tlist.
+ *
+ * As a notational convenience, returns the same PathTarget pointer passed in.
+ */
+PathTarget *
+replace_pathtarget_cached_expressions(PathTarget *target)
+{
+ target->exprs = (List *) replace_cached_expressions_mutator(
+ (Node *) target->exprs);
+
+ return target;
+}
+
+/*
+ * replace_qual_cached_expressions
+ * Replace cacehd expressions in a WHERE clause. The input can be either an
+ * implicitly-ANDed list of boolean expressions, or a list of RestrictInfo
+ * nodes.
+ */
+List *
+replace_qual_cached_expressions(List *quals)
+{
+ /* No setup needed for tree walk, so away we go */
+ return (List *) replace_cached_expressions_mutator((Node *) quals);
+}
+
static Node *
replace_cached_expressions_mutator(Node *node)
{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a1dafc8..0c0284a 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2758,6 +2758,61 @@ eval_const_expressions_mutator(Node *node,
newexpr->location = expr->location;
return (Node *) newexpr;
}
+ case T_CachedExpr:
+ {
+ CachedExpr *cachedexpr = (CachedExpr *) node;
+ Node *new_subexpr = eval_const_expressions_mutator(
+ get_subexpr(cachedexpr), context);
+ CachedExpr *new_cachedexpr;
+
+ /*
+ * If unsafe transformations are used cached expression should
+ * be always simplified.
+ */
+ if (context->estimate)
+ Assert(IsA(new_subexpr, Const));
+
+ if (IsA(new_subexpr, Const))
+ {
+ /* successfully simplified it */
+ return new_subexpr;
+ }
+ else
+ {
+ /*
+ * The expression cannot be simplified any further, so build
+ * and return a replacement CachedExpr node using the
+ * possibly-simplified arguments of subexpression.
+ */
+ new_cachedexpr = makeNode(CachedExpr);
+ new_cachedexpr->subexprtype = cachedexpr->subexprtype;
+ switch (new_cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ new_cachedexpr->subexpr.funcexpr = (FuncExpr *)
+ new_subexpr;
+ break;
+ case CACHED_OPEXPR:
+ new_cachedexpr->subexpr.opexpr = (OpExpr *)
+ new_subexpr;
+ break;
+ case CACHED_DISTINCTEXPR:
+ new_cachedexpr->subexpr.distinctexpr =
+ (DistinctExpr *) new_subexpr;
+ break;
+ case CACHED_NULLIFEXPR:
+ new_cachedexpr->subexpr.nullifexpr = (NullIfExpr *)
+ new_subexpr;
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ new_cachedexpr->subexpr.saopexpr =
+ (ScalarArrayOpExpr *) new_subexpr;
+ break;
+ }
+
+ return (Node *) new_cachedexpr;
+ }
+ }
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 43b1475..838389d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7720,6 +7720,11 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_CachedExpr:
+ get_rule_expr(get_subexpr((CachedExpr *) node), context,
+ showimplicit);
+ break;
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 86fdb33..650c8af 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -86,6 +86,16 @@ typedef enum ExprEvalOp
EEOP_FUNCEXPR_STRICT_FUSAGE,
/*
+ * Evaluate CachedExpr. EEOP_CACHEDEXPR_IF_CACHED is used before
+ * subexpression evaluation (if subexpression was evaluated use cached value
+ * and jump to next state or get prepared to subexpression evaluation
+ * otherwise). EEOP_CACHEDEXPR_SUBEXPR_END is used after subexpression
+ * evaluation for caching its result.
+ */
+ EEOP_CACHEDEXPR_IF_CACHED,
+ EEOP_CACHEDEXPR_SUBEXPR_END,
+
+ /*
* Evaluate boolean AND expression, one step per subexpression. FIRST/LAST
* subexpressions are special-cased for performance. Since AND always has
* at least two subexpressions, FIRST and LAST never apply to the same
@@ -298,6 +308,15 @@ typedef struct ExprEvalStep
int nargs; /* number of arguments */
} func;
+ /* for EEOP_CACHEDEXPR_* */
+ struct
+ {
+ bool *isExecuted;
+ bool *resnull;
+ Datum *resvalue;
+ int jumpdone; /* jump here if result determined */
+ } cachedexpr;
+
/* for EEOP_BOOL_*_STEP */
struct
{
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index f3aaa23..bbadcdd 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -59,4 +59,7 @@ extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);
extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti);
+extern PathTarget *replace_pathtarget_cached_expressions(PathTarget *target);
+extern List *replace_qual_cached_expressions(List *quals);
+
#endif /* PLANNER_H */
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index ccb93d8..7488bd2 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -65,8 +65,12 @@ extern void split_pathtarget_at_srfs(PlannerInfo *root,
PathTarget *target, PathTarget *input_target,
List **targets, List **targets_contain_srfs);
-/* Convenience macro to get a PathTarget with valid cost/width fields */
+/*
+ * Convenience macro to get a PathTarget with valid cost/width fields and
+ * cached expressions.
+ */
#define create_pathtarget(root, tlist) \
- set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist))
+ set_pathtarget_cost_width(root, replace_pathtarget_cached_expressions( \
+ make_pathtarget_from_tlist(tlist)))
#endif /* TLIST_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 7a40c99..2e27052 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -6535,6 +6535,16 @@ exec_simple_check_node(Node *node)
return TRUE;
}
+ case T_CachedExpr:
+ {
+ /*
+ * If CachedExpr will not be initialized by ExecInitCachedExpr
+ * possibly it will use cached value when it shouldn't (for
+ * example, snapshot has changed), so return false.
+ */
+ return FALSE;
+ }
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/test/regress/expected/precalculate_stable_functions.out b/src/test/regress/expected/precalculate_stable_functions.out
new file mode 100644
index 0000000..093e6f8
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions.out
@@ -0,0 +1,2625 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create types and tables for testing
+CREATE TYPE my_integer AS (value integer);
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+-- Create volatile functions for testing
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_array_int (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create stable functions for testing
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_array_int (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+-- Create immutable functions for testing
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create operators for testing
+CREATE operator === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE operator ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE operator ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE operator ====== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+CREATE operator ==== (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- Simple functions testing
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- Functions with constant arguments and nested functions testing
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Nested and strict operators testing
+-- (also partly mixed functions and operators testing)
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- IS DISTINCT FROM expression testing
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- IS DISTINCT FROM expressions with null arguments testing
+SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Nested IS DISTINCT FROM expression testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- NULLIF expressions with null arguments testing
+SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+
+
+
+
+(4 rows)
+
+-- Nested NULLIF expression testing
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing (also partly mixed functions and "scalar op ANY/ALL
+-- (array)" / "scalar IN (2 or more values)" expressions testing)
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and operators testing
+-- (most of it was earlier in Nested and strict operators testing)
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and IS DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer),
+ (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT equal_booleans_stl_strict(
+ (x_stl() IS DISTINCT FROM 1),
+ (x_stl() IS DISTINCT FROM 2)
+)
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF(x_stl_my_integer(), '(1)'::my_integer),
+ NULLIF(x_stl_my_integer(), '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal integers stable
+ equal_integers_stl
+--------------------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" /
+-- "scalar IN (2 or more values)" expressions testing)
+SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x;
+NOTICE: s array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x;
+NOTICE: s array_int
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed operators and IS DISTINCT FROM expressions testing
+-- should not be precalculated
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed operators and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+-- should not be precalculated
+SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed IS DISTINCT FROM and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ nullif
+--------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ nullif
+--------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or
+-- more values)" expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ (1 === ANY('{2, 3}')) IS DISTINCT FROM
+ (1 === ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ (1 ==== ANY('{2, 3}')) IS DISTINCT FROM
+ (1 ==== ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Tracking functions testing
+SET track_functions TO 'all';
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SET track_functions TO DEFAULT;
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+BEGIN;
+SELECT simple();
+ simple
+--------
+ 2
+(1 row)
+
+INSERT INTO two VALUES (3);
+SELECT simple();
+ simple
+--------
+ 3
+(1 row)
+
+ROLLBACK;
+-- Drop tables for testing
+DROP TABLE two;
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 04206c3..f2710b9 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -179,3 +179,4 @@ test: with
test: xml
test: event_trigger
test: stats
+test: precalculate_stable_functions
diff --git a/src/test/regress/sql/precalculate_stable_functions.sql b/src/test/regress/sql/precalculate_stable_functions.sql
new file mode 100644
index 0000000..a59791d
--- /dev/null
+++ b/src/test/regress/sql/precalculate_stable_functions.sql
@@ -0,0 +1,949 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+
+-- Create types and tables for testing
+
+CREATE TYPE my_integer AS (value integer);
+
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+
+-- Create volatile functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_array_int (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create stable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_array_int (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create immutable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create operators for testing
+
+CREATE operator === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE operator ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE operator ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE operator ====== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+
+CREATE operator ==== (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- Simple functions testing
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+-- WHERE clause testing
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+-- Functions with constant arguments and nested functions testing
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions testing
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions with null arguments testing
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+-- Operators testing
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+
+-- Nested and strict operators testing
+-- (also partly mixed functions and operators testing)
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x;
+SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x;
+
+-- IS DISTINCT FROM expression testing
+
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+
+-- IS DISTINCT FROM expressions with null arguments testing
+
+SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+
+-- Nested IS DISTINCT FROM expression testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+-- NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- NULLIF expressions with null arguments testing
+
+SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x;
+
+SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x;
+
+-- Nested NULLIF expression testing
+
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+
+SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x;
+SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+
+SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x;
+SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x;
+
+SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x;
+SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x;
+
+-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing (also partly mixed functions and "scalar op ANY/ALL
+-- (array)" / "scalar IN (2 or more values)" expressions testing)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and operators testing
+-- (most of it was earlier in Nested and strict operators testing)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+-- Mixed functions and IS DISTINCT FROM expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer),
+ (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT equal_booleans_stl_strict(
+ (x_stl() IS DISTINCT FROM 1),
+ (x_stl() IS DISTINCT FROM 2)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and NULLIF expressions testing
+
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF(x_stl_my_integer(), '(1)'::my_integer),
+ NULLIF(x_stl_my_integer(), '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2))
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" /
+-- "scalar IN (2 or more values)" expressions testing)
+
+SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x;
+SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x;
+
+-- Mixed operators and IS DISTINCT FROM expressions testing
+
+-- should not be precalculated
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+
+-- Mixed operators and NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x;
+
+-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+
+-- should not be precalculated
+SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- Mixed IS DISTINCT FROM and NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or
+-- more values)" expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ (1 === ANY('{2, 3}')) IS DISTINCT FROM
+ (1 === ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ (1 ==== ANY('{2, 3}')) IS DISTINCT FROM
+ (1 ==== ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- Tracking functions testing
+
+SET track_functions TO 'all';
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+SET track_functions TO DEFAULT;
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+
+BEGIN;
+SELECT simple();
+INSERT INTO two VALUES (3);
+SELECT simple();
+ROLLBACK;
+
+-- Drop tables for testing
+
+DROP TABLE two;
--
1.9.1
Hi,
On 2017-05-18 19:00:09 +0300, Marina Polyakova wrote:
Here's v2 of the patches. Changes from v1:
And here there's v3 of planning and execution: common executor steps for all
types of cached expression.
I've not followed this thread, but just scanned this quickly because it
affects execExpr* stuff.
+ case T_CachedExpr: + { + int adjust_jump; + + /* + * Allocate and fill scratch memory used by all steps of + * CachedExpr evaluation. + */ + scratch.d.cachedexpr.isExecuted = (bool *) palloc(sizeof(bool)); + scratch.d.cachedexpr.resnull = (bool *) palloc(sizeof(bool)); + scratch.d.cachedexpr.resvalue = (Datum *) palloc(sizeof(Datum)); + + *scratch.d.cachedexpr.isExecuted = false; + *scratch.d.cachedexpr.resnull = false; + *scratch.d.cachedexpr.resvalue = (Datum) 0;
Looks like having something like struct CachedExprState would be better,
than these separate allocations? That also allows to aleviate some size
concerns when adding new fields (see below)
@@ -279,6 +279,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) TupleTableSlot *innerslot; TupleTableSlot *outerslot; TupleTableSlot *scanslot; + MemoryContext oldContext; /* for EEOP_CACHEDEXPR_* */
I'd rather not have this on function scope - a) the stack pressure in
ExecInterpExpr is quite noticeable in profiles already b) this is going
to trigger warnings because of unused vars, because the compiler doesn't
understand that EEOP_CACHEDEXPR_IF_CACHED always follows
EEOP_CACHEDEXPR_SUBEXPR_END.
How about instead storing oldcontext in the expression itself?
I'm also not sure how acceptable it is to just assume it's ok to leave
stuff in per_query_memory, in some cases that could prove to be
problematic.
+ case T_CachedExpr: + { + CachedExpr *cachedexpr = (CachedExpr *) node; + Node *new_subexpr = eval_const_expressions_mutator( + get_subexpr(cachedexpr), context); + CachedExpr *new_cachedexpr; + + /* + * If unsafe transformations are used cached expression should + * be always simplified. + */ + if (context->estimate) + Assert(IsA(new_subexpr, Const)); + + if (IsA(new_subexpr, Const)) + { + /* successfully simplified it */ + return new_subexpr; + } + else + { + /* + * The expression cannot be simplified any further, so build + * and return a replacement CachedExpr node using the + * possibly-simplified arguments of subexpression. + */
Is this actually a meaningful path? Shouldn't always have done const
evaluation before adding CachedExpr's?
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
Hello!
I've not followed this thread, but just scanned this quickly because it
affects execExpr* stuff.
Thank you very much for your comments! Thanks to them I have made v4 of
the patches (as in the previous one, only planning and execution part is
changed).
Looks like having something like struct CachedExprState would be
better,
than these separate allocations? That also allows to aleviate some
size
concerns when adding new fields (see below)
I'd rather not have this on function scope - a) the stack pressure in
ExecInterpExpr is quite noticeable in profiles already b) this is going
to trigger warnings because of unused vars, because the compiler
doesn't
understand that EEOP_CACHEDEXPR_IF_CACHED always follows
EEOP_CACHEDEXPR_SUBEXPR_END.How about instead storing oldcontext in the expression itself?
Thanks, in new version I did all of it in this way.
I'm also not sure how acceptable it is to just assume it's ok to leave
stuff in per_query_memory, in some cases that could prove to be
problematic.
I agree with you and in new version context is changed only for copying
datum of result value (if it's a pointer, its data should be allocated
in per_query_memory, or we will lost it for next tuples).
Is this actually a meaningful path? Shouldn't always have done const
evaluation before adding CachedExpr's?
eval_const_expressions_mutator is used several times, and one of them in
functions for selectivity evaluation (set_baserel_size_estimates ->
clauselist_selectivity -> clause_selectivity -> restriction_selectivity
-> ... -> get_restriction_variable -> estimate_expression_value ->
eval_const_expressions_mutator). In set_baserel_size_estimates function
right after selectivity evaluation there's costs evaluation and cached
expressions should be replaced before costs. I'm not sure that it is a
good idea to insert cached expressions replacement in
set_baserel_size_estimates, because in comments to it it's said "The
rel's targetlist and restrictinfo list must have been constructed
already, and rel->tuples must be set." and its file costsize.c is
entitled as "Routines to compute (and set) relation sizes and path
costs". So I have inserted cached expressions replacement just before it
(but I'm not sure that I have seen all places where it should be
inserted). What do you think about all of this?
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v4-0001-Precalculate-stable-functions-infrastructure.patchtext/x-diff; name=v4-0001-Precalculate-stable-functions-infrastructure.patchDownload
From 02262b9f3a3215d3884b6ac188bafa6517ac543d Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 14:24:36 +0300
Subject: [PATCH v4 1/3] Precalculate stable functions, infrastructure
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- creation of CachedExpr node
- usual node functions for it
- mutator to replace nonovolatile functions' and operators' expressions by
appropriate cached expressions.
---
src/backend/nodes/copyfuncs.c | 31 +++++
src/backend/nodes/equalfuncs.c | 31 +++++
src/backend/nodes/nodeFuncs.c | 151 ++++++++++++++++++++
src/backend/nodes/outfuncs.c | 56 ++++++++
src/backend/nodes/readfuncs.c | 48 +++++++
src/backend/optimizer/plan/planner.c | 259 +++++++++++++++++++++++++++++++++++
src/include/nodes/nodeFuncs.h | 1 +
src/include/nodes/nodes.h | 1 +
src/include/nodes/primnodes.h | 38 +++++
9 files changed, 616 insertions(+)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6ad3844..f9f69a1 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1527,6 +1527,34 @@ _copyNullIfExpr(const NullIfExpr *from)
return newnode;
}
+static CachedExpr *
+_copyCachedExpr(const CachedExpr *from)
+{
+ CachedExpr *newnode = makeNode(CachedExpr);
+
+ COPY_SCALAR_FIELD(subexprtype);
+ switch(from->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ COPY_NODE_FIELD(subexpr.funcexpr);
+ break;
+ case CACHED_OPEXPR:
+ COPY_NODE_FIELD(subexpr.opexpr);
+ break;
+ case CACHED_DISTINCTEXPR:
+ COPY_NODE_FIELD(subexpr.distinctexpr);
+ break;
+ case CACHED_NULLIFEXPR:
+ COPY_NODE_FIELD(subexpr.nullifexpr);
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ COPY_NODE_FIELD(subexpr.saopexpr);
+ break;
+ }
+
+ return newnode;
+}
+
/*
* _copyScalarArrayOpExpr
*/
@@ -4867,6 +4895,9 @@ copyObjectImpl(const void *from)
case T_NullIfExpr:
retval = _copyNullIfExpr(from);
break;
+ case T_CachedExpr:
+ retval = _copyCachedExpr(from);
+ break;
case T_ScalarArrayOpExpr:
retval = _copyScalarArrayOpExpr(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index c9a8c34..8863759 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -384,6 +384,34 @@ _equalNullIfExpr(const NullIfExpr *a, const NullIfExpr *b)
}
static bool
+_equalCachedExpr(const CachedExpr *a, const CachedExpr *b)
+{
+ COMPARE_SCALAR_FIELD(subexprtype);
+
+ /* the same subexprtype for b because we have already compared it */
+ switch(a->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ COMPARE_NODE_FIELD(subexpr.funcexpr);
+ break;
+ case CACHED_OPEXPR:
+ COMPARE_NODE_FIELD(subexpr.opexpr);
+ break;
+ case CACHED_DISTINCTEXPR:
+ COMPARE_NODE_FIELD(subexpr.distinctexpr);
+ break;
+ case CACHED_NULLIFEXPR:
+ COMPARE_NODE_FIELD(subexpr.nullifexpr);
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ COMPARE_NODE_FIELD(subexpr.saopexpr);
+ break;
+ }
+
+ return true;
+}
+
+static bool
_equalScalarArrayOpExpr(const ScalarArrayOpExpr *a, const ScalarArrayOpExpr *b)
{
COMPARE_SCALAR_FIELD(opno);
@@ -3031,6 +3059,9 @@ equal(const void *a, const void *b)
case T_NullIfExpr:
retval = _equalNullIfExpr(a, b);
break;
+ case T_CachedExpr:
+ retval = _equalCachedExpr(a, b);
+ break;
case T_ScalarArrayOpExpr:
retval = _equalScalarArrayOpExpr(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 3e8189c..e3dd576 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -32,6 +32,7 @@ static bool planstate_walk_subplans(List *plans, bool (*walker) (),
void *context);
static bool planstate_walk_members(List *plans, PlanState **planstates,
bool (*walker) (), void *context);
+static const Node *get_const_subexpr(const CachedExpr *cachedexpr);
/*
@@ -92,6 +93,9 @@ exprType(const Node *expr)
case T_NullIfExpr:
type = ((const NullIfExpr *) expr)->opresulttype;
break;
+ case T_CachedExpr:
+ type = exprType(get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
type = BOOLOID;
break;
@@ -311,6 +315,8 @@ exprTypmod(const Node *expr)
return exprTypmod((Node *) linitial(nexpr->args));
}
break;
+ case T_CachedExpr:
+ return exprTypmod(get_const_subexpr((const CachedExpr *) expr));
case T_SubLink:
{
const SubLink *sublink = (const SubLink *) expr;
@@ -573,6 +579,10 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
return true;
}
+ if (expr && IsA(expr, CachedExpr))
+ return exprIsLengthCoercion(
+ get_const_subexpr((const CachedExpr *) expr), coercedTypmod);
+
return false;
}
@@ -655,6 +665,10 @@ strip_implicit_coercions(Node *node)
if (c->coercionformat == COERCE_IMPLICIT_CAST)
return strip_implicit_coercions((Node *) c->arg);
}
+ else if (IsA(node, CachedExpr))
+ {
+ return strip_implicit_coercions(get_subexpr((CachedExpr *) node));
+ }
return node;
}
@@ -727,6 +741,8 @@ expression_returns_set_walker(Node *node, void *context)
return false;
if (IsA(node, XmlExpr))
return false;
+ if (IsA(node, CachedExpr))
+ return false;
return expression_tree_walker(node, expression_returns_set_walker,
context);
@@ -790,6 +806,9 @@ exprCollation(const Node *expr)
case T_NullIfExpr:
coll = ((const NullIfExpr *) expr)->opcollid;
break;
+ case T_CachedExpr:
+ coll = exprCollation(get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
coll = InvalidOid; /* result is always boolean */
break;
@@ -973,6 +992,10 @@ exprInputCollation(const Node *expr)
case T_NullIfExpr:
coll = ((const NullIfExpr *) expr)->inputcollid;
break;
+ case T_CachedExpr:
+ coll = exprInputCollation(
+ get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
coll = ((const ScalarArrayOpExpr *) expr)->inputcollid;
break;
@@ -1034,6 +1057,9 @@ exprSetCollation(Node *expr, Oid collation)
case T_NullIfExpr:
((NullIfExpr *) expr)->opcollid = collation;
break;
+ case T_CachedExpr:
+ exprSetCollation(get_subexpr((CachedExpr *) expr), collation);
+ break;
case T_ScalarArrayOpExpr:
Assert(!OidIsValid(collation)); /* result is always boolean */
break;
@@ -1168,6 +1194,10 @@ exprSetInputCollation(Node *expr, Oid inputcollation)
case T_NullIfExpr:
((NullIfExpr *) expr)->inputcollid = inputcollation;
break;
+ case T_CachedExpr:
+ exprSetInputCollation(get_subexpr((CachedExpr *) expr),
+ inputcollation);
+ break;
case T_ScalarArrayOpExpr:
((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation;
break;
@@ -1277,6 +1307,9 @@ exprLocation(const Node *expr)
exprLocation((Node *) opexpr->args));
}
break;
+ case T_CachedExpr:
+ loc = exprLocation(get_const_subexpr((const CachedExpr *) expr));
+ break;
case T_ScalarArrayOpExpr:
{
const ScalarArrayOpExpr *saopexpr = (const ScalarArrayOpExpr *) expr;
@@ -1611,6 +1644,8 @@ fix_opfuncids_walker(Node *node, void *context)
{
if (node == NULL)
return false;
+ if (IsA(node, CachedExpr))
+ return fix_opfuncids_walker(get_subexpr((CachedExpr *) node), context);
if (IsA(node, OpExpr))
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
@@ -1710,6 +1745,9 @@ check_functions_in_node(Node *node, check_function_callback checker,
return true;
}
break;
+ case T_CachedExpr:
+ return check_functions_in_node(get_subexpr((CachedExpr *) node),
+ checker, context);
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -1980,6 +2018,17 @@ expression_tree_walker(Node *node,
return true;
}
break;
+ case T_CachedExpr:
+ {
+ /*
+ * cachedexpr is processed by my_walker, so its subexpr is
+ * processed too and we need to process sub-nodes of subexpr.
+ */
+ if (expression_tree_walker(get_subexpr((CachedExpr *) node),
+ walker, context))
+ return true;
+ }
+ break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -2617,6 +2666,54 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_CachedExpr:
+ {
+ CachedExpr *expr = (CachedExpr *) node;
+ CachedExpr *newnode;
+
+ FLATCOPY(newnode, expr, CachedExpr);
+
+ /*
+ * expr is already mutated, so its subexpr is already mutated
+ * too and we need to mutate sub-nodes of subexpr.
+ */
+ switch(newnode->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ newnode->subexpr.funcexpr = (FuncExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.funcexpr, mutator,
+ context);
+ break;
+ case CACHED_OPEXPR:
+ newnode->subexpr.opexpr = (OpExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.opexpr, mutator,
+ context);
+ break;
+ case CACHED_DISTINCTEXPR:
+ newnode->subexpr.distinctexpr = (DistinctExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.distinctexpr, mutator,
+ context);
+ break;
+ case CACHED_NULLIFEXPR:
+ newnode->subexpr.nullifexpr = (NullIfExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.nullifexpr, mutator,
+ context);
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ newnode->subexpr.saopexpr = (ScalarArrayOpExpr *)
+ expression_tree_mutator(
+ (Node *) expr->subexpr.saopexpr, mutator,
+ context);
+ break;
+ }
+
+ return (Node *) newnode;
+ }
+ break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -3838,3 +3935,57 @@ planstate_walk_members(List *plans, PlanState **planstates,
return false;
}
+
+/*
+ * get_const_subexpr
+ * Get const subexpression of given const cached expression.
+ */
+static const Node *
+get_const_subexpr(const CachedExpr *cachedexpr)
+{
+ if (cachedexpr == NULL)
+ return NULL;
+
+ switch (cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ return (const Node *) cachedexpr->subexpr.funcexpr;
+ case CACHED_OPEXPR:
+ return (const Node *) cachedexpr->subexpr.opexpr;
+ case CACHED_DISTINCTEXPR:
+ return (const Node *) cachedexpr->subexpr.distinctexpr;
+ case CACHED_NULLIFEXPR:
+ return (const Node *) cachedexpr->subexpr.nullifexpr;
+ case CACHED_SCALARARRAYOPEXPR:
+ return (const Node *) cachedexpr->subexpr.saopexpr;
+ }
+
+ return NULL;
+}
+
+/*
+ * get_subexpr
+ * Get subexpression of given cached expression.
+ */
+Node *
+get_subexpr(CachedExpr *cachedexpr)
+{
+ if (cachedexpr == NULL)
+ return NULL;
+
+ switch (cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ return (Node *) cachedexpr->subexpr.funcexpr;
+ case CACHED_OPEXPR:
+ return (Node *) cachedexpr->subexpr.opexpr;
+ case CACHED_DISTINCTEXPR:
+ return (Node *) cachedexpr->subexpr.distinctexpr;
+ case CACHED_NULLIFEXPR:
+ return (Node *) cachedexpr->subexpr.nullifexpr;
+ case CACHED_SCALARARRAYOPEXPR:
+ return (Node *) cachedexpr->subexpr.saopexpr;
+ }
+
+ return NULL;
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8d9ff63..c0c8363 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1237,6 +1237,59 @@ _outNullIfExpr(StringInfo str, const NullIfExpr *node)
}
static void
+_outCachedExpr(StringInfo str, const CachedExpr *node)
+{
+ WRITE_NODE_TYPE("CACHEDEXPR");
+
+ /* do-it-yourself enum representation; out subexprtype begin... */
+ appendStringInfoString(str, " :subexprtype ");
+
+ switch(node->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_funcexpr");
+
+ WRITE_NODE_FIELD(subexpr.funcexpr);
+ }
+ break;
+ case CACHED_OPEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_opexpr");
+
+ WRITE_NODE_FIELD(subexpr.opexpr);
+ }
+ break;
+ case CACHED_DISTINCTEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_distinctexpr");
+
+ WRITE_NODE_FIELD(subexpr.distinctexpr);
+ }
+ break;
+ case CACHED_NULLIFEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_nullifexpr");
+
+ WRITE_NODE_FIELD(subexpr.nullifexpr);
+ }
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ {
+ /* ... out subexprtype end */
+ outToken(str, "cached_scalararrayopexpr");
+
+ WRITE_NODE_FIELD(subexpr.saopexpr);
+ }
+ break;
+ }
+}
+
+static void
_outScalarArrayOpExpr(StringInfo str, const ScalarArrayOpExpr *node)
{
WRITE_NODE_TYPE("SCALARARRAYOPEXPR");
@@ -3767,6 +3820,9 @@ outNode(StringInfo str, const void *obj)
case T_NullIfExpr:
_outNullIfExpr(str, obj);
break;
+ case T_CachedExpr:
+ _outCachedExpr(str, obj);
+ break;
case T_ScalarArrayOpExpr:
_outScalarArrayOpExpr(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index e24f5d6..acb14f9 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -750,6 +750,52 @@ _readNullIfExpr(void)
}
/*
+ * _readCachedExpr
+ */
+static CachedExpr *
+_readCachedExpr(void)
+{
+ READ_LOCALS(CachedExpr);
+
+ /* do-it-yourself enum representation */
+ token = pg_strtok(&length); /* skip :subexprtype */
+ token = pg_strtok(&length); /* get field value */
+ if (strncmp(token, "cached_funcexpr", 15) == 0)
+ local_node->subexprtype = CACHED_FUNCEXPR;
+ else if (strncmp(token, "cached_opexpr", 13) == 0)
+ local_node->subexprtype = CACHED_OPEXPR;
+ else if (strncmp(token, "cached_distinctexpr", 19) == 0)
+ local_node->subexprtype = CACHED_DISTINCTEXPR;
+ else if (strncmp(token, "cached_nullifexpr", 17) == 0)
+ local_node->subexprtype = CACHED_NULLIFEXPR;
+ else if (strncmp(token, "cached_scalararrayopexpr", 24) == 0)
+ local_node->subexprtype = CACHED_SCALARARRAYOPEXPR;
+ else
+ elog(ERROR, "unrecognized subexprtype \"%.*s\"", length, token);
+
+ switch (local_node->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ READ_NODE_FIELD(subexpr.funcexpr);
+ break;
+ case CACHED_OPEXPR:
+ READ_NODE_FIELD(subexpr.opexpr);
+ break;
+ case CACHED_DISTINCTEXPR:
+ READ_NODE_FIELD(subexpr.distinctexpr);
+ break;
+ case CACHED_NULLIFEXPR:
+ READ_NODE_FIELD(subexpr.nullifexpr);
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ READ_NODE_FIELD(subexpr.saopexpr);
+ break;
+ }
+
+ READ_DONE();
+}
+
+/*
* _readScalarArrayOpExpr
*/
static ScalarArrayOpExpr *
@@ -2462,6 +2508,8 @@ parseNodeString(void)
return_value = _readDistinctExpr();
else if (MATCH("NULLIFEXPR", 10))
return_value = _readNullIfExpr();
+ else if (MATCH("CACHEDEXPR", 10))
+ return_value = _readCachedExpr();
else if (MATCH("SCALARARRAYOPEXPR", 17))
return_value = _readScalarArrayOpExpr();
else if (MATCH("BOOLEXPR", 8))
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index c4a5651..552b73d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -184,6 +184,7 @@ static PathTarget *make_sort_input_target(PlannerInfo *root,
bool *have_postponed_srfs);
static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel,
List *targets, List *targets_contain_srfs);
+static Node *replace_cached_expressions_mutator(Node *node);
/*****************************************************************************
@@ -6086,3 +6087,261 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
return result;
}
+
+static Node *
+replace_cached_expressions_mutator(Node *node)
+{
+ if (node == NULL)
+ return NULL;
+
+ /* mutate certain types of nodes */
+ if (IsA(node, RestrictInfo))
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) node;
+
+ /*
+ * For an OR clause, recurse into the marked-up tree so that we replace
+ * cached expressions for contained RestrictInfos too.
+ */
+ if (rinfo->orclause)
+ rinfo->orclause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->orclause);
+ else
+ rinfo->clause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->clause);
+
+ /* do NOT recurse into children */
+ return node;
+ }
+ else if (IsA(node, FuncExpr))
+ {
+ /*
+ * Function is cached if:
+ * 1) it doesn't return set,
+ * 2) it's not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ FuncExpr *funcexpr;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool func_returns_set;
+
+ /* firstly recurse into children */
+ funcexpr = (FuncExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ func_returns_set = funcexpr->funcretset ||
+ expression_returns_set((Node *) funcexpr->args);
+
+ foreach(arg, funcexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (func_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &funcexpr->xpr))
+ {
+ /* return FuncExpr, which will not be cached */
+ return (Node *) funcexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_FUNCEXPR;
+ new_node->subexpr.funcexpr = funcexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, OpExpr))
+ {
+ /*
+ * Operator is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ OpExpr *opexpr = (OpExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ /* firstly recurse into children */
+ opexpr = (OpExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ op_returns_set = opexpr->opretset ||
+ expression_returns_set((Node *) opexpr->args);
+
+ foreach(arg, opexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &opexpr->xpr))
+ {
+ /* return OpExpr, which will not be cached */
+ return (Node *) opexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_OPEXPR;
+ new_node->subexpr.opexpr = opexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, DistinctExpr))
+ {
+ /*
+ * Operator of DistinctExpr is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ DistinctExpr *distinctexpr = (DistinctExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid((OpExpr *) distinctexpr);
+
+ /* firstly recurse into children */
+ distinctexpr = (DistinctExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ op_returns_set = distinctexpr->opretset ||
+ expression_returns_set((Node *) distinctexpr->args);
+
+ foreach(arg, distinctexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &distinctexpr->xpr))
+ {
+ /* return DistinctExpr, which will not be cached */
+ return (Node *) distinctexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_DISTINCTEXPR;
+ new_node->subexpr.distinctexpr = distinctexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, NullIfExpr))
+ {
+ /*
+ * Operator of NullIfExpr is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ NullIfExpr *nullifexpr = (NullIfExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid((OpExpr *) nullifexpr);
+
+ /* firstly recurse into children */
+ nullifexpr = (NullIfExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+ op_returns_set = nullifexpr->opretset ||
+ expression_returns_set((Node *) nullifexpr->args);
+
+ foreach(arg, nullifexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &nullifexpr->xpr))
+ {
+ /* return NullIfExpr, which will not be cached */
+ return (Node *) nullifexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_NULLIFEXPR;
+ new_node->subexpr.nullifexpr = nullifexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ /*
+ * Operator of ScalarArrayOpExpr is cached if:
+ * 1) its function is not volatile itself,
+ * 2) its arguments are constants or cached expressions too.
+ * (it returns boolean so we don't need to check if it returns set)
+ */
+ ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ set_sa_opfuncid(saopexpr);
+
+ /* firstly recurse into children */
+ saopexpr = (ScalarArrayOpExpr *) expression_tree_mutator(node,
+ replace_cached_expressions_mutator,
+ NULL);
+
+ foreach(arg, saopexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) &saopexpr->xpr))
+ {
+ /* return ScalarArrayOpExpr, which will not be cached */
+ return (Node *) saopexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexprtype = CACHED_SCALARARRAYOPEXPR;
+ new_node->subexpr.saopexpr = saopexpr;
+
+ return (Node *) new_node;
+ }
+ }
+
+ /* otherwise recurse into children */
+ return expression_tree_mutator(node, replace_cached_expressions_mutator,
+ NULL);
+}
diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h
index b6c9b48..0dbfa12 100644
--- a/src/include/nodes/nodeFuncs.h
+++ b/src/include/nodes/nodeFuncs.h
@@ -76,5 +76,6 @@ extern bool raw_expression_tree_walker(Node *node, bool (*walker) (),
struct PlanState;
extern bool planstate_tree_walker(struct PlanState *planstate, bool (*walker) (),
void *context);
+extern Node * get_subexpr(CachedExpr *cachedexpr);
#endif /* NODEFUNCS_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f59d719..054bc61 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -155,6 +155,7 @@ typedef enum NodeTag
T_OpExpr,
T_DistinctExpr,
T_NullIfExpr,
+ T_CachedExpr,
T_ScalarArrayOpExpr,
T_BoolExpr,
T_SubLink,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 86ec82e..3f89653 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1498,4 +1498,42 @@ typedef struct OnConflictExpr
List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */
} OnConflictExpr;
+/*
+ * Discriminator for CachedExpr.
+ *
+ * Identifies the subexpression to be cached in execution (= executed only once
+ * and then used cached value) and which member in the CachedExpr->subexpr union
+ * is valid.
+ */
+typedef enum CachedSubExprType
+{
+ CACHED_FUNCEXPR, /* cached FuncExpr */
+ CACHED_OPEXPR, /* cached OpExpr */
+ CACHED_DISTINCTEXPR, /* cached DistinctExpr */
+ CACHED_NULLIFEXPR, /* cached NullIfExpr */
+ CACHED_SCALARARRAYOPEXPR /* cached ScalarArrayOpExpr */
+} CachedSubExprType;
+
+/*
+ * CachedExpr - expression node for precalculated stable and immutable functions
+ * (= they are calculated once for all output rows, but as many times as
+ * function is mentioned in query), if they don't return a set and their
+ * arguments are constants or recursively precalculated functions. The same for
+ * operators' functions.
+ */
+typedef struct CachedExpr
+{
+ Expr xpr;
+ CachedSubExprType subexprtype; /* expression to be cached */
+
+ union SubExpr
+ {
+ FuncExpr *funcexpr; /* for CACHED_FUNCEXPR */
+ OpExpr *opexpr; /* for CACHED_OPEXPR */
+ DistinctExpr *distinctexpr; /* for CACHED_DISTINCTEXPR */
+ NullIfExpr *nullifexpr; /* for CACHED_NULLIFEXPR */
+ ScalarArrayOpExpr *saopexpr; /* for CACHED_SCALARARRAYOPEXPR */
+ } subexpr;
+} CachedExpr;
+
#endif /* PRIMNODES_H */
--
1.9.1
v4-0002-Precalculate-stable-functions-planning-and-execut.patchtext/x-diff; name=v4-0002-Precalculate-stable-functions-planning-and-execut.patchDownload
From 537d8a2bb085efdfce695f148e614ed4611f9a6e Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 15:31:21 +0300
Subject: [PATCH v4 2/3] Precalculate stable functions, planning and execution
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- replacement nonvolatile functions and operators by appropriate cached
expressions
- planning and execution cached expressions
- regression tests
---
src/backend/executor/execExpr.c | 55 +
src/backend/executor/execExprInterp.c | 51 +
src/backend/optimizer/path/allpaths.c | 9 +-
src/backend/optimizer/path/clausesel.c | 13 +
src/backend/optimizer/plan/planagg.c | 1 +
src/backend/optimizer/plan/planner.c | 28 +
src/backend/optimizer/util/clauses.c | 55 +
src/backend/utils/adt/ruleutils.c | 5 +
src/include/executor/execExpr.h | 37 +
src/include/optimizer/planner.h | 3 +
src/include/optimizer/tlist.h | 8 +-
src/pl/plpgsql/src/pl_exec.c | 10 +
.../expected/precalculate_stable_functions.out | 2625 ++++++++++++++++++++
src/test/regress/serial_schedule | 1 +
.../regress/sql/precalculate_stable_functions.sql | 949 +++++++
15 files changed, 3847 insertions(+), 3 deletions(-)
create mode 100644 src/test/regress/expected/precalculate_stable_functions.out
create mode 100644 src/test/regress/sql/precalculate_stable_functions.sql
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 5a34a46..dc84975 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -865,6 +865,61 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
break;
}
+ case T_CachedExpr:
+ {
+ CachedExpr *cachedexpr = (CachedExpr *) node;
+
+ /*
+ * Allocate CachedExprState used by all steps of CachedExpr
+ * evaluation.
+ */
+ scratch.d.cachedexpr.state = (CachedExprState *) palloc(
+ sizeof(CachedExprState));
+ scratch.d.cachedexpr.state->isExecuted = false;
+ scratch.d.cachedexpr.state->resnull = false;
+ scratch.d.cachedexpr.state->resvalue = (Datum) 0;
+
+ switch(cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ scratch.d.cachedexpr.state->restypid =
+ cachedexpr->subexpr.funcexpr->funcresulttype;
+ break;
+ case CACHED_OPEXPR:
+ scratch.d.cachedexpr.state->restypid =
+ cachedexpr->subexpr.opexpr->opresulttype;
+ break;
+ case CACHED_DISTINCTEXPR:
+ scratch.d.cachedexpr.state->restypid =
+ cachedexpr->subexpr.distinctexpr->opresulttype;
+ break;
+ case CACHED_NULLIFEXPR:
+ scratch.d.cachedexpr.state->restypid =
+ cachedexpr->subexpr.nullifexpr->opresulttype;
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ scratch.d.cachedexpr.state->restypid = BOOLOID;
+ break;
+ }
+
+ /* add EEOP_CACHEDEXPR_IF_CACHED step */
+ scratch.opcode = EEOP_CACHEDEXPR_IF_CACHED;
+ ExprEvalPushStep(state, &scratch);
+
+ /* add subexpression steps */
+ ExecInitExprRec((Expr *) get_subexpr(cachedexpr), parent, state,
+ resv, resnull);
+
+ /* add EEOP_CACHEDEXPR_SUBEXPR_END step */
+ scratch.opcode = EEOP_CACHEDEXPR_SUBEXPR_END;
+ ExprEvalPushStep(state, &scratch);
+
+ /* adjust jump target */
+ scratch.d.cachedexpr.state->jumpdone = state->steps_len;
+
+ break;
+ }
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index fed0052..2cb10fd 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -70,6 +70,7 @@
#include "pgstat.h"
#include "utils/builtins.h"
#include "utils/date.h"
+#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -309,6 +310,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_FUNCEXPR_STRICT,
&&CASE_EEOP_FUNCEXPR_FUSAGE,
&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+ &&CASE_EEOP_CACHEDEXPR_IF_CACHED,
+ &&CASE_EEOP_CACHEDEXPR_SUBEXPR_END,
&&CASE_EEOP_BOOL_AND_STEP_FIRST,
&&CASE_EEOP_BOOL_AND_STEP,
&&CASE_EEOP_BOOL_AND_STEP_LAST,
@@ -721,6 +724,54 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_CACHEDEXPR_IF_CACHED)
+ {
+ if (op->d.cachedexpr.state->isExecuted)
+ {
+ /* use saved result and skip subexpression evaluation */
+ *op->resnull = op->d.cachedexpr.state->resnull;
+ if (!(*op->resnull))
+ *op->resvalue = op->d.cachedexpr.state->resvalue;
+
+ EEO_JUMP(op->d.cachedexpr.state->jumpdone);
+ }
+
+ /* we are ready for subexpression evaluation */
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHEDEXPR_SUBEXPR_END)
+ {
+ int16 restyplen;
+ bool restypbyval;
+
+ /* save result */
+ op->d.cachedexpr.state->resnull = *op->resnull;
+ if (!(*op->resnull))
+ {
+ get_typlenbyval(op->d.cachedexpr.state->restypid, &restyplen,
+ &restypbyval);
+
+ /*
+ * Switch per-query memory context. It is necessary to save the
+ * subexpression result value between all tuples if its datum is
+ * a pointer.
+ */
+ op->d.cachedexpr.state->oldContext = MemoryContextSwitchTo(
+ econtext->ecxt_per_query_memory);
+
+ op->d.cachedexpr.state->resvalue = datumCopy(*op->resvalue,
+ restypbyval,
+ restyplen);
+
+ /* switch memory context back */
+ MemoryContextSwitchTo(op->d.cachedexpr.state->oldContext);
+ }
+ op->d.cachedexpr.state->isExecuted = true;
+
+ EEO_NEXT();
+ }
+
/*
* If any of its clauses is FALSE, an AND's result is FALSE regardless
* of the states of the rest of the clauses, so we can stop evaluating
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index b93b4fc..a322255 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -378,7 +378,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
set_subquery_pathlist(root, rel, rti, rte);
break;
case RTE_FUNCTION:
- set_function_size_estimates(root, rel);
+ {
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo);
+ set_function_size_estimates(root, rel);
+ }
break;
case RTE_TABLEFUNC:
set_tablefunc_size_estimates(root, rel);
@@ -517,6 +521,9 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
*/
check_index_predicates(root, rel);
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo);
+
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
}
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 758ddea..fc799f1 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
@@ -825,6 +826,18 @@ clause_selectivity(PlannerInfo *root,
jointype,
sjinfo);
}
+ else if (IsA(clause, CachedExpr))
+ {
+ /*
+ * Not sure this case is needed, but it can't hurt.
+ * Calculate selectivity of subexpression.
+ */
+ s1 = clause_selectivity(root,
+ get_subexpr((CachedExpr *) clause),
+ varRelid,
+ jointype,
+ sjinfo);
+ }
else
{
/*
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 5565736..7a28764 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -38,6 +38,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 552b73d..7c68d6d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6088,6 +6088,34 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
return result;
}
+/*
+ * replace_pathtarget_cached_expressions
+ * Replace cached expresisons in a PathTarget tlist.
+ *
+ * As a notational convenience, returns the same PathTarget pointer passed in.
+ */
+PathTarget *
+replace_pathtarget_cached_expressions(PathTarget *target)
+{
+ target->exprs = (List *) replace_cached_expressions_mutator(
+ (Node *) target->exprs);
+
+ return target;
+}
+
+/*
+ * replace_qual_cached_expressions
+ * Replace cacehd expressions in a WHERE clause. The input can be either an
+ * implicitly-ANDed list of boolean expressions, or a list of RestrictInfo
+ * nodes.
+ */
+List *
+replace_qual_cached_expressions(List *quals)
+{
+ /* No setup needed for tree walk, so away we go */
+ return (List *) replace_cached_expressions_mutator((Node *) quals);
+}
+
static Node *
replace_cached_expressions_mutator(Node *node)
{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a1dafc8..0c0284a 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2758,6 +2758,61 @@ eval_const_expressions_mutator(Node *node,
newexpr->location = expr->location;
return (Node *) newexpr;
}
+ case T_CachedExpr:
+ {
+ CachedExpr *cachedexpr = (CachedExpr *) node;
+ Node *new_subexpr = eval_const_expressions_mutator(
+ get_subexpr(cachedexpr), context);
+ CachedExpr *new_cachedexpr;
+
+ /*
+ * If unsafe transformations are used cached expression should
+ * be always simplified.
+ */
+ if (context->estimate)
+ Assert(IsA(new_subexpr, Const));
+
+ if (IsA(new_subexpr, Const))
+ {
+ /* successfully simplified it */
+ return new_subexpr;
+ }
+ else
+ {
+ /*
+ * The expression cannot be simplified any further, so build
+ * and return a replacement CachedExpr node using the
+ * possibly-simplified arguments of subexpression.
+ */
+ new_cachedexpr = makeNode(CachedExpr);
+ new_cachedexpr->subexprtype = cachedexpr->subexprtype;
+ switch (new_cachedexpr->subexprtype)
+ {
+ case CACHED_FUNCEXPR:
+ new_cachedexpr->subexpr.funcexpr = (FuncExpr *)
+ new_subexpr;
+ break;
+ case CACHED_OPEXPR:
+ new_cachedexpr->subexpr.opexpr = (OpExpr *)
+ new_subexpr;
+ break;
+ case CACHED_DISTINCTEXPR:
+ new_cachedexpr->subexpr.distinctexpr =
+ (DistinctExpr *) new_subexpr;
+ break;
+ case CACHED_NULLIFEXPR:
+ new_cachedexpr->subexpr.nullifexpr = (NullIfExpr *)
+ new_subexpr;
+ break;
+ case CACHED_SCALARARRAYOPEXPR:
+ new_cachedexpr->subexpr.saopexpr =
+ (ScalarArrayOpExpr *) new_subexpr;
+ break;
+ }
+
+ return (Node *) new_cachedexpr;
+ }
+ }
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 43b1475..838389d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7720,6 +7720,11 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_CachedExpr:
+ get_rule_expr(get_subexpr((CachedExpr *) node), context,
+ showimplicit);
+ break;
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 86fdb33..ea37a36 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -86,6 +86,16 @@ typedef enum ExprEvalOp
EEOP_FUNCEXPR_STRICT_FUSAGE,
/*
+ * Evaluate CachedExpr. EEOP_CACHEDEXPR_IF_CACHED is used before
+ * subexpression evaluation (if subexpression was evaluated use cached value
+ * and jump to next state or get prepared to subexpression evaluation
+ * otherwise). EEOP_CACHEDEXPR_SUBEXPR_END is used after subexpression
+ * evaluation for caching its result.
+ */
+ EEOP_CACHEDEXPR_IF_CACHED,
+ EEOP_CACHEDEXPR_SUBEXPR_END,
+
+ /*
* Evaluate boolean AND expression, one step per subexpression. FIRST/LAST
* subexpressions are special-cased for performance. Since AND always has
* at least two subexpressions, FIRST and LAST never apply to the same
@@ -298,6 +308,13 @@ typedef struct ExprEvalStep
int nargs; /* number of arguments */
} func;
+ /* for EEOP_CACHEDEXPR_* */
+ struct
+ {
+ /* steps for evaluation the same CachedExpr have the same state */
+ struct CachedExprState *state;
+ } cachedexpr;
+
/* for EEOP_BOOL_*_STEP */
struct
{
@@ -600,6 +617,26 @@ typedef struct ArrayRefState
} ArrayRefState;
+/*
+ * Non-inline data for EEOP_CACHEDEXPR_* operations (steps for evaluation the
+ * same CachedExpr have the same state).
+ */
+typedef struct CachedExprState
+{
+ bool isExecuted;
+ bool resnull;
+ Datum resvalue;
+ Oid restypid; /* for copying resvalue of subexpression */
+ int jumpdone; /* jump here if result determined */
+
+ /*
+ * For switching per-query memory context. It is necessary to save the
+ * subexpression result between all tuples if its value datum is a pointer.
+ */
+ MemoryContext oldContext;
+} CachedExprState;
+
+
extern void ExecReadyInterpretedExpr(ExprState *state);
extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index f3aaa23..bbadcdd 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -59,4 +59,7 @@ extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);
extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti);
+extern PathTarget *replace_pathtarget_cached_expressions(PathTarget *target);
+extern List *replace_qual_cached_expressions(List *quals);
+
#endif /* PLANNER_H */
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index ccb93d8..7488bd2 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -65,8 +65,12 @@ extern void split_pathtarget_at_srfs(PlannerInfo *root,
PathTarget *target, PathTarget *input_target,
List **targets, List **targets_contain_srfs);
-/* Convenience macro to get a PathTarget with valid cost/width fields */
+/*
+ * Convenience macro to get a PathTarget with valid cost/width fields and
+ * cached expressions.
+ */
#define create_pathtarget(root, tlist) \
- set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist))
+ set_pathtarget_cost_width(root, replace_pathtarget_cached_expressions( \
+ make_pathtarget_from_tlist(tlist)))
#endif /* TLIST_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 7a40c99..2e27052 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -6535,6 +6535,16 @@ exec_simple_check_node(Node *node)
return TRUE;
}
+ case T_CachedExpr:
+ {
+ /*
+ * If CachedExpr will not be initialized by ExecInitCachedExpr
+ * possibly it will use cached value when it shouldn't (for
+ * example, snapshot has changed), so return false.
+ */
+ return FALSE;
+ }
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/test/regress/expected/precalculate_stable_functions.out b/src/test/regress/expected/precalculate_stable_functions.out
new file mode 100644
index 0000000..093e6f8
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions.out
@@ -0,0 +1,2625 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create types and tables for testing
+CREATE TYPE my_integer AS (value integer);
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+-- Create volatile functions for testing
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_array_int (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create stable functions for testing
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_array_int (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+-- Create immutable functions for testing
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create operators for testing
+CREATE operator === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE operator ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE operator ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE operator ====== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+CREATE operator ==== (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- Simple functions testing
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- Functions with constant arguments and nested functions testing
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Nested and strict operators testing
+-- (also partly mixed functions and operators testing)
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- IS DISTINCT FROM expression testing
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- IS DISTINCT FROM expressions with null arguments testing
+SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Nested IS DISTINCT FROM expression testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- NULLIF expressions with null arguments testing
+SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+
+
+
+
+(4 rows)
+
+-- Nested NULLIF expression testing
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+NOTICE: v my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing (also partly mixed functions and "scalar op ANY/ALL
+-- (array)" / "scalar IN (2 or more values)" expressions testing)
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and operators testing
+-- (most of it was earlier in Nested and strict operators testing)
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and IS DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer),
+ (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT equal_booleans_stl_strict(
+ (x_stl() IS DISTINCT FROM 1),
+ (x_stl() IS DISTINCT FROM 2)
+)
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF(x_stl_my_integer(), '(1)'::my_integer),
+ NULLIF(x_stl_my_integer(), '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: s my_integer
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2))
+FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal integers stable
+ equal_integers_stl
+--------------------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" /
+-- "scalar IN (2 or more values)" expressions testing)
+SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+NOTICE: v array_int
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x;
+NOTICE: s array_int
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x;
+NOTICE: s array_int
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed operators and IS DISTINCT FROM expressions testing
+-- should not be precalculated
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed operators and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+-- should not be precalculated
+SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed IS DISTINCT FROM and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ nullif
+--------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ nullif
+--------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or
+-- more values)" expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ (1 === ANY('{2, 3}')) IS DISTINCT FROM
+ (1 === ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ (1 ==== ANY('{2, 3}')) IS DISTINCT FROM
+ (1 ==== ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Tracking functions testing
+SET track_functions TO 'all';
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE: s2
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: equal booleans stable strict
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE: s2 boolean
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: v
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+NOTICE: s
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SET track_functions TO DEFAULT;
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+BEGIN;
+SELECT simple();
+ simple
+--------
+ 2
+(1 row)
+
+INSERT INTO two VALUES (3);
+SELECT simple();
+ simple
+--------
+ 3
+(1 row)
+
+ROLLBACK;
+-- Drop tables for testing
+DROP TABLE two;
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 04206c3..f2710b9 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -179,3 +179,4 @@ test: with
test: xml
test: event_trigger
test: stats
+test: precalculate_stable_functions
diff --git a/src/test/regress/sql/precalculate_stable_functions.sql b/src/test/regress/sql/precalculate_stable_functions.sql
new file mode 100644
index 0000000..a59791d
--- /dev/null
+++ b/src/test/regress/sql/precalculate_stable_functions.sql
@@ -0,0 +1,949 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+
+-- Create types and tables for testing
+
+CREATE TYPE my_integer AS (value integer);
+
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+
+-- Create volatile functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_array_int (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create stable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_array_int (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_int';
+ RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create immutable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create operators for testing
+
+CREATE operator === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE operator ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE operator ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE operator ====== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+
+CREATE operator ==== (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- Simple functions testing
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+-- WHERE clause testing
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+-- Functions with constant arguments and nested functions testing
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions testing
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions with null arguments testing
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+-- Operators testing
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+
+-- Nested and strict operators testing
+-- (also partly mixed functions and operators testing)
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x;
+SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x;
+
+-- IS DISTINCT FROM expression testing
+
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+
+-- IS DISTINCT FROM expressions with null arguments testing
+
+SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+
+-- Nested IS DISTINCT FROM expression testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+-- NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- NULLIF expressions with null arguments testing
+
+SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x;
+
+SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x;
+
+-- Nested NULLIF expression testing
+
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+
+SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x;
+SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+
+SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x;
+SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x;
+
+SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x;
+SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x;
+
+-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing (also partly mixed functions and "scalar op ANY/ALL
+-- (array)" / "scalar IN (2 or more values)" expressions testing)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and operators testing
+-- (most of it was earlier in Nested and strict operators testing)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+-- Mixed functions and IS DISTINCT FROM expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer),
+ (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT equal_booleans_stl_strict(
+ (x_stl() IS DISTINCT FROM 1),
+ (x_stl() IS DISTINCT FROM 2)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and NULLIF expressions testing
+
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF(x_stl_my_integer(), '(1)'::my_integer),
+ NULLIF(x_stl_my_integer(), '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2))
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" /
+-- "scalar IN (2 or more values)" expressions testing)
+
+SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x;
+SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x;
+
+-- Mixed operators and IS DISTINCT FROM expressions testing
+
+-- should not be precalculated
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT (
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+
+-- Mixed operators and NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x;
+
+-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+
+-- should not be precalculated
+SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- Mixed IS DISTINCT FROM and NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+ ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or
+-- more values)" expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ (1 === ANY('{2, 3}')) IS DISTINCT FROM
+ (1 === ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ (1 ==== ANY('{2, 3}')) IS DISTINCT FROM
+ (1 ==== ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+ TRUE
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+ ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(NULLIF(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+ TRUE
+))
+FROM generate_series(1, 4) x;
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+ ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+SELECT (
+ NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+ ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- Tracking functions testing
+
+SET track_functions TO 'all';
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+SET track_functions TO DEFAULT;
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+
+BEGIN;
+SELECT simple();
+INSERT INTO two VALUES (3);
+SELECT simple();
+ROLLBACK;
+
+-- Drop tables for testing
+
+DROP TABLE two;
--
1.9.1
v4-0003-Precalculate-stable-functions-costs.patchtext/x-diff; name=v4-0003-Precalculate-stable-functions-costs.patchDownload
From 2382fa68414f6bbed42ff66c7abbc3c9b200d244 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 16:05:38 +0300
Subject: [PATCH v4 3/3] Precalculate stable functions, costs
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- cost changes for cached expressions (according to their behaviour)
---
src/backend/optimizer/path/costsize.c | 89 ++++++++++++++++++++++++++---------
1 file changed, 67 insertions(+), 22 deletions(-)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 52643d0..505772a 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -140,6 +140,7 @@ static MergeScanSelCache *cached_scansel(PlannerInfo *root,
PathKey *pathkey);
static void cost_rescan(PlannerInfo *root, Path *path,
Cost *rescan_startup_cost, Cost *rescan_total_cost);
+static double cost_eval_cacheable_expr_per_tuple(Node *node);
static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
ParamPathInfo *param_info,
@@ -3464,6 +3465,59 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
*cost = context.total;
}
+/*
+ * cost_eval_cacheable_expr_per_tuple
+ * Evaluate per tuple cost for expressions that can be cacheable.
+ *
+ * This function was created to not duplicate code for some expression and
+ * cached some expression.
+ */
+static double
+cost_eval_cacheable_expr_per_tuple(Node *node)
+{
+ double result;
+
+ /*
+ * For each operator or function node in the given tree, we charge the
+ * estimated execution cost given by pg_proc.procost (remember to multiply
+ * this by cpu_operator_cost).
+ */
+ if (IsA(node, FuncExpr))
+ {
+ result = get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+ }
+ else if (IsA(node, OpExpr) ||
+ IsA(node, DistinctExpr) ||
+ IsA(node, NullIfExpr))
+ {
+ OpExpr *opexpr = (OpExpr *) node;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ result = get_func_cost(opexpr->opfuncid) * cpu_operator_cost;
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ /*
+ * Estimate that the operator will be applied to about half of the
+ * array elements before the answer is determined.
+ */
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
+ Node *arraynode = (Node *) lsecond(saop->args);
+
+ set_sa_opfuncid(saop);
+ result = get_func_cost(saop->opfuncid) * cpu_operator_cost *
+ estimate_array_length(arraynode) * 0.5;
+ }
+ else
+ {
+ elog(ERROR, "non cacheable expression node type: %d", (int) nodeTag(node));
+ }
+
+ return result;
+}
+
static bool
cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
{
@@ -3537,32 +3591,23 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
* moreover, since our rowcount estimates for functions tend to be pretty
* phony, the results would also be pretty phony.
*/
- if (IsA(node, FuncExpr))
+ if (IsA(node, FuncExpr) ||
+ IsA(node, OpExpr) ||
+ IsA(node, DistinctExpr) ||
+ IsA(node, NullIfExpr) ||
+ IsA(node, ScalarArrayOpExpr))
{
- context->total.per_tuple +=
- get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+ context->total.per_tuple += cost_eval_cacheable_expr_per_tuple(node);
}
- else if (IsA(node, OpExpr) ||
- IsA(node, DistinctExpr) ||
- IsA(node, NullIfExpr))
- {
- /* rely on struct equivalence to treat these all alike */
- set_opfuncid((OpExpr *) node);
- context->total.per_tuple +=
- get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost;
- }
- else if (IsA(node, ScalarArrayOpExpr))
- {
+ else if (IsA(node, CachedExpr))
+ {
/*
- * Estimate that the operator will be applied to about half of the
- * array elements before the answer is determined.
+ * Calculate subexpression cost per tuple as usual and add it to startup
+ * cost (because subexpression will be executed only once for all
+ * tuples).
*/
- ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
- Node *arraynode = (Node *) lsecond(saop->args);
-
- set_sa_opfuncid(saop);
- context->total.per_tuple += get_func_cost(saop->opfuncid) *
- cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
+ context->total.startup += cost_eval_cacheable_expr_per_tuple(
+ get_subexpr((CachedExpr *) node));
}
else if (IsA(node, Aggref) ||
IsA(node, WindowFunc))
--
1.9.1
Hi Marina,
I still don't see anything particularly wrong with your patch. It
applies, passes all test, it is well test-covered and even documented.
Also I've run `make installcheck` under Valgrind and didn't find any
memory-related errors.
Is there anything that you would like to change before we call it more
or less final?
Also I would advice to add your branch to our internal buildfarm just to
make sure everything is OK on exotic platforms like Windows ;)
On Mon, May 22, 2017 at 06:32:17PM +0300, Marina Polyakova wrote:
Hi,
Hello!
I've not followed this thread, but just scanned this quickly because it
affects execExpr* stuff.Thank you very much for your comments! Thanks to them I have made v4 of the
patches (as in the previous one, only planning and execution part is
changed).Looks like having something like struct CachedExprState would be better,
than these separate allocations? That also allows to aleviate some size
concerns when adding new fields (see below)I'd rather not have this on function scope - a) the stack pressure in
ExecInterpExpr is quite noticeable in profiles already b) this is going
to trigger warnings because of unused vars, because the compiler doesn't
understand that EEOP_CACHEDEXPR_IF_CACHED always follows
EEOP_CACHEDEXPR_SUBEXPR_END.How about instead storing oldcontext in the expression itself?
Thanks, in new version I did all of it in this way.
I'm also not sure how acceptable it is to just assume it's ok to leave
stuff in per_query_memory, in some cases that could prove to be
problematic.I agree with you and in new version context is changed only for copying
datum of result value (if it's a pointer, its data should be allocated in
per_query_memory, or we will lost it for next tuples).Is this actually a meaningful path? Shouldn't always have done const
evaluation before adding CachedExpr's?eval_const_expressions_mutator is used several times, and one of them in
functions for selectivity evaluation (set_baserel_size_estimates ->
clauselist_selectivity -> clause_selectivity -> restriction_selectivity ->
... -> get_restriction_variable -> estimate_expression_value ->
eval_const_expressions_mutator). In set_baserel_size_estimates function
right after selectivity evaluation there's costs evaluation and cached
expressions should be replaced before costs. I'm not sure that it is a good
idea to insert cached expressions replacement in set_baserel_size_estimates,
because in comments to it it's said "The rel's targetlist and restrictinfo
list must have been constructed already, and rel->tuples must be set." and
its file costsize.c is entitled as "Routines to compute (and set) relation
sizes and path costs". So I have inserted cached expressions replacement
just before it (but I'm not sure that I have seen all places where it should
be inserted). What do you think about all of this?--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
From 02262b9f3a3215d3884b6ac188bafa6517ac543d Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 14:24:36 +0300
Subject: [PATCH v4 1/3] Precalculate stable functions, infrastructureNow in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.This patch includes:
- creation of CachedExpr node
- usual node functions for it
- mutator to replace nonovolatile functions' and operators' expressions by
appropriate cached expressions.
---
src/backend/nodes/copyfuncs.c | 31 +++++
src/backend/nodes/equalfuncs.c | 31 +++++
src/backend/nodes/nodeFuncs.c | 151 ++++++++++++++++++++
src/backend/nodes/outfuncs.c | 56 ++++++++
src/backend/nodes/readfuncs.c | 48 +++++++
src/backend/optimizer/plan/planner.c | 259 +++++++++++++++++++++++++++++++++++
src/include/nodes/nodeFuncs.h | 1 +
src/include/nodes/nodes.h | 1 +
src/include/nodes/primnodes.h | 38 +++++
9 files changed, 616 insertions(+)diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 6ad3844..f9f69a1 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1527,6 +1527,34 @@ _copyNullIfExpr(const NullIfExpr *from) return newnode; }+static CachedExpr * +_copyCachedExpr(const CachedExpr *from) +{ + CachedExpr *newnode = makeNode(CachedExpr); + + COPY_SCALAR_FIELD(subexprtype); + switch(from->subexprtype) + { + case CACHED_FUNCEXPR: + COPY_NODE_FIELD(subexpr.funcexpr); + break; + case CACHED_OPEXPR: + COPY_NODE_FIELD(subexpr.opexpr); + break; + case CACHED_DISTINCTEXPR: + COPY_NODE_FIELD(subexpr.distinctexpr); + break; + case CACHED_NULLIFEXPR: + COPY_NODE_FIELD(subexpr.nullifexpr); + break; + case CACHED_SCALARARRAYOPEXPR: + COPY_NODE_FIELD(subexpr.saopexpr); + break; + } + + return newnode; +} + /* * _copyScalarArrayOpExpr */ @@ -4867,6 +4895,9 @@ copyObjectImpl(const void *from) case T_NullIfExpr: retval = _copyNullIfExpr(from); break; + case T_CachedExpr: + retval = _copyCachedExpr(from); + break; case T_ScalarArrayOpExpr: retval = _copyScalarArrayOpExpr(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c9a8c34..8863759 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -384,6 +384,34 @@ _equalNullIfExpr(const NullIfExpr *a, const NullIfExpr *b) }static bool +_equalCachedExpr(const CachedExpr *a, const CachedExpr *b) +{ + COMPARE_SCALAR_FIELD(subexprtype); + + /* the same subexprtype for b because we have already compared it */ + switch(a->subexprtype) + { + case CACHED_FUNCEXPR: + COMPARE_NODE_FIELD(subexpr.funcexpr); + break; + case CACHED_OPEXPR: + COMPARE_NODE_FIELD(subexpr.opexpr); + break; + case CACHED_DISTINCTEXPR: + COMPARE_NODE_FIELD(subexpr.distinctexpr); + break; + case CACHED_NULLIFEXPR: + COMPARE_NODE_FIELD(subexpr.nullifexpr); + break; + case CACHED_SCALARARRAYOPEXPR: + COMPARE_NODE_FIELD(subexpr.saopexpr); + break; + } + + return true; +} + +static bool _equalScalarArrayOpExpr(const ScalarArrayOpExpr *a, const ScalarArrayOpExpr *b) { COMPARE_SCALAR_FIELD(opno); @@ -3031,6 +3059,9 @@ equal(const void *a, const void *b) case T_NullIfExpr: retval = _equalNullIfExpr(a, b); break; + case T_CachedExpr: + retval = _equalCachedExpr(a, b); + break; case T_ScalarArrayOpExpr: retval = _equalScalarArrayOpExpr(a, b); break; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 3e8189c..e3dd576 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -32,6 +32,7 @@ static bool planstate_walk_subplans(List *plans, bool (*walker) (), void *context); static bool planstate_walk_members(List *plans, PlanState **planstates, bool (*walker) (), void *context); +static const Node *get_const_subexpr(const CachedExpr *cachedexpr);/* @@ -92,6 +93,9 @@ exprType(const Node *expr) case T_NullIfExpr: type = ((const NullIfExpr *) expr)->opresulttype; break; + case T_CachedExpr: + type = exprType(get_const_subexpr((const CachedExpr *) expr)); + break; case T_ScalarArrayOpExpr: type = BOOLOID; break; @@ -311,6 +315,8 @@ exprTypmod(const Node *expr) return exprTypmod((Node *) linitial(nexpr->args)); } break; + case T_CachedExpr: + return exprTypmod(get_const_subexpr((const CachedExpr *) expr)); case T_SubLink: { const SubLink *sublink = (const SubLink *) expr; @@ -573,6 +579,10 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod) return true; }+ if (expr && IsA(expr, CachedExpr)) + return exprIsLengthCoercion( + get_const_subexpr((const CachedExpr *) expr), coercedTypmod); + return false; }@@ -655,6 +665,10 @@ strip_implicit_coercions(Node *node) if (c->coercionformat == COERCE_IMPLICIT_CAST) return strip_implicit_coercions((Node *) c->arg); } + else if (IsA(node, CachedExpr)) + { + return strip_implicit_coercions(get_subexpr((CachedExpr *) node)); + } return node; }@@ -727,6 +741,8 @@ expression_returns_set_walker(Node *node, void *context) return false; if (IsA(node, XmlExpr)) return false; + if (IsA(node, CachedExpr)) + return false;return expression_tree_walker(node, expression_returns_set_walker, context); @@ -790,6 +806,9 @@ exprCollation(const Node *expr) case T_NullIfExpr: coll = ((const NullIfExpr *) expr)->opcollid; break; + case T_CachedExpr: + coll = exprCollation(get_const_subexpr((const CachedExpr *) expr)); + break; case T_ScalarArrayOpExpr: coll = InvalidOid; /* result is always boolean */ break; @@ -973,6 +992,10 @@ exprInputCollation(const Node *expr) case T_NullIfExpr: coll = ((const NullIfExpr *) expr)->inputcollid; break; + case T_CachedExpr: + coll = exprInputCollation( + get_const_subexpr((const CachedExpr *) expr)); + break; case T_ScalarArrayOpExpr: coll = ((const ScalarArrayOpExpr *) expr)->inputcollid; break; @@ -1034,6 +1057,9 @@ exprSetCollation(Node *expr, Oid collation) case T_NullIfExpr: ((NullIfExpr *) expr)->opcollid = collation; break; + case T_CachedExpr: + exprSetCollation(get_subexpr((CachedExpr *) expr), collation); + break; case T_ScalarArrayOpExpr: Assert(!OidIsValid(collation)); /* result is always boolean */ break; @@ -1168,6 +1194,10 @@ exprSetInputCollation(Node *expr, Oid inputcollation) case T_NullIfExpr: ((NullIfExpr *) expr)->inputcollid = inputcollation; break; + case T_CachedExpr: + exprSetInputCollation(get_subexpr((CachedExpr *) expr), + inputcollation); + break; case T_ScalarArrayOpExpr: ((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation; break; @@ -1277,6 +1307,9 @@ exprLocation(const Node *expr) exprLocation((Node *) opexpr->args)); } break; + case T_CachedExpr: + loc = exprLocation(get_const_subexpr((const CachedExpr *) expr)); + break; case T_ScalarArrayOpExpr: { const ScalarArrayOpExpr *saopexpr = (const ScalarArrayOpExpr *) expr; @@ -1611,6 +1644,8 @@ fix_opfuncids_walker(Node *node, void *context) { if (node == NULL) return false; + if (IsA(node, CachedExpr)) + return fix_opfuncids_walker(get_subexpr((CachedExpr *) node), context); if (IsA(node, OpExpr)) set_opfuncid((OpExpr *) node); else if (IsA(node, DistinctExpr)) @@ -1710,6 +1745,9 @@ check_functions_in_node(Node *node, check_function_callback checker, return true; } break; + case T_CachedExpr: + return check_functions_in_node(get_subexpr((CachedExpr *) node), + checker, context); case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; @@ -1980,6 +2018,17 @@ expression_tree_walker(Node *node, return true; } break; + case T_CachedExpr: + { + /* + * cachedexpr is processed by my_walker, so its subexpr is + * processed too and we need to process sub-nodes of subexpr. + */ + if (expression_tree_walker(get_subexpr((CachedExpr *) node), + walker, context)) + return true; + } + break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; @@ -2617,6 +2666,54 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_CachedExpr: + { + CachedExpr *expr = (CachedExpr *) node; + CachedExpr *newnode; + + FLATCOPY(newnode, expr, CachedExpr); + + /* + * expr is already mutated, so its subexpr is already mutated + * too and we need to mutate sub-nodes of subexpr. + */ + switch(newnode->subexprtype) + { + case CACHED_FUNCEXPR: + newnode->subexpr.funcexpr = (FuncExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.funcexpr, mutator, + context); + break; + case CACHED_OPEXPR: + newnode->subexpr.opexpr = (OpExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.opexpr, mutator, + context); + break; + case CACHED_DISTINCTEXPR: + newnode->subexpr.distinctexpr = (DistinctExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.distinctexpr, mutator, + context); + break; + case CACHED_NULLIFEXPR: + newnode->subexpr.nullifexpr = (NullIfExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.nullifexpr, mutator, + context); + break; + case CACHED_SCALARARRAYOPEXPR: + newnode->subexpr.saopexpr = (ScalarArrayOpExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.saopexpr, mutator, + context); + break; + } + + return (Node *) newnode; + } + break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; @@ -3838,3 +3935,57 @@ planstate_walk_members(List *plans, PlanState **planstates,return false; } + +/* + * get_const_subexpr + * Get const subexpression of given const cached expression. + */ +static const Node * +get_const_subexpr(const CachedExpr *cachedexpr) +{ + if (cachedexpr == NULL) + return NULL; + + switch (cachedexpr->subexprtype) + { + case CACHED_FUNCEXPR: + return (const Node *) cachedexpr->subexpr.funcexpr; + case CACHED_OPEXPR: + return (const Node *) cachedexpr->subexpr.opexpr; + case CACHED_DISTINCTEXPR: + return (const Node *) cachedexpr->subexpr.distinctexpr; + case CACHED_NULLIFEXPR: + return (const Node *) cachedexpr->subexpr.nullifexpr; + case CACHED_SCALARARRAYOPEXPR: + return (const Node *) cachedexpr->subexpr.saopexpr; + } + + return NULL; +} + +/* + * get_subexpr + * Get subexpression of given cached expression. + */ +Node * +get_subexpr(CachedExpr *cachedexpr) +{ + if (cachedexpr == NULL) + return NULL; + + switch (cachedexpr->subexprtype) + { + case CACHED_FUNCEXPR: + return (Node *) cachedexpr->subexpr.funcexpr; + case CACHED_OPEXPR: + return (Node *) cachedexpr->subexpr.opexpr; + case CACHED_DISTINCTEXPR: + return (Node *) cachedexpr->subexpr.distinctexpr; + case CACHED_NULLIFEXPR: + return (Node *) cachedexpr->subexpr.nullifexpr; + case CACHED_SCALARARRAYOPEXPR: + return (Node *) cachedexpr->subexpr.saopexpr; + } + + return NULL; +} diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 8d9ff63..c0c8363 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1237,6 +1237,59 @@ _outNullIfExpr(StringInfo str, const NullIfExpr *node) }static void +_outCachedExpr(StringInfo str, const CachedExpr *node) +{ + WRITE_NODE_TYPE("CACHEDEXPR"); + + /* do-it-yourself enum representation; out subexprtype begin... */ + appendStringInfoString(str, " :subexprtype "); + + switch(node->subexprtype) + { + case CACHED_FUNCEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_funcexpr"); + + WRITE_NODE_FIELD(subexpr.funcexpr); + } + break; + case CACHED_OPEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_opexpr"); + + WRITE_NODE_FIELD(subexpr.opexpr); + } + break; + case CACHED_DISTINCTEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_distinctexpr"); + + WRITE_NODE_FIELD(subexpr.distinctexpr); + } + break; + case CACHED_NULLIFEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_nullifexpr"); + + WRITE_NODE_FIELD(subexpr.nullifexpr); + } + break; + case CACHED_SCALARARRAYOPEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_scalararrayopexpr"); + + WRITE_NODE_FIELD(subexpr.saopexpr); + } + break; + } +} + +static void _outScalarArrayOpExpr(StringInfo str, const ScalarArrayOpExpr *node) { WRITE_NODE_TYPE("SCALARARRAYOPEXPR"); @@ -3767,6 +3820,9 @@ outNode(StringInfo str, const void *obj) case T_NullIfExpr: _outNullIfExpr(str, obj); break; + case T_CachedExpr: + _outCachedExpr(str, obj); + break; case T_ScalarArrayOpExpr: _outScalarArrayOpExpr(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e24f5d6..acb14f9 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -750,6 +750,52 @@ _readNullIfExpr(void) }/* + * _readCachedExpr + */ +static CachedExpr * +_readCachedExpr(void) +{ + READ_LOCALS(CachedExpr); + + /* do-it-yourself enum representation */ + token = pg_strtok(&length); /* skip :subexprtype */ + token = pg_strtok(&length); /* get field value */ + if (strncmp(token, "cached_funcexpr", 15) == 0) + local_node->subexprtype = CACHED_FUNCEXPR; + else if (strncmp(token, "cached_opexpr", 13) == 0) + local_node->subexprtype = CACHED_OPEXPR; + else if (strncmp(token, "cached_distinctexpr", 19) == 0) + local_node->subexprtype = CACHED_DISTINCTEXPR; + else if (strncmp(token, "cached_nullifexpr", 17) == 0) + local_node->subexprtype = CACHED_NULLIFEXPR; + else if (strncmp(token, "cached_scalararrayopexpr", 24) == 0) + local_node->subexprtype = CACHED_SCALARARRAYOPEXPR; + else + elog(ERROR, "unrecognized subexprtype \"%.*s\"", length, token); + + switch (local_node->subexprtype) + { + case CACHED_FUNCEXPR: + READ_NODE_FIELD(subexpr.funcexpr); + break; + case CACHED_OPEXPR: + READ_NODE_FIELD(subexpr.opexpr); + break; + case CACHED_DISTINCTEXPR: + READ_NODE_FIELD(subexpr.distinctexpr); + break; + case CACHED_NULLIFEXPR: + READ_NODE_FIELD(subexpr.nullifexpr); + break; + case CACHED_SCALARARRAYOPEXPR: + READ_NODE_FIELD(subexpr.saopexpr); + break; + } + + READ_DONE(); +} + +/* * _readScalarArrayOpExpr */ static ScalarArrayOpExpr * @@ -2462,6 +2508,8 @@ parseNodeString(void) return_value = _readDistinctExpr(); else if (MATCH("NULLIFEXPR", 10)) return_value = _readNullIfExpr(); + else if (MATCH("CACHEDEXPR", 10)) + return_value = _readCachedExpr(); else if (MATCH("SCALARARRAYOPEXPR", 17)) return_value = _readScalarArrayOpExpr(); else if (MATCH("BOOLEXPR", 8)) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index c4a5651..552b73d 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -184,6 +184,7 @@ static PathTarget *make_sort_input_target(PlannerInfo *root, bool *have_postponed_srfs); static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel, List *targets, List *targets_contain_srfs); +static Node *replace_cached_expressions_mutator(Node *node);/*****************************************************************************
@@ -6086,3 +6087,261 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)return result; } + +static Node * +replace_cached_expressions_mutator(Node *node) +{ + if (node == NULL) + return NULL; + + /* mutate certain types of nodes */ + if (IsA(node, RestrictInfo)) + { + RestrictInfo *rinfo = (RestrictInfo *) node; + + /* + * For an OR clause, recurse into the marked-up tree so that we replace + * cached expressions for contained RestrictInfos too. + */ + if (rinfo->orclause) + rinfo->orclause = (Expr *) replace_cached_expressions_mutator( + (Node *) rinfo->orclause); + else + rinfo->clause = (Expr *) replace_cached_expressions_mutator( + (Node *) rinfo->clause); + + /* do NOT recurse into children */ + return node; + } + else if (IsA(node, FuncExpr)) + { + /* + * Function is cached if: + * 1) it doesn't return set, + * 2) it's not volatile itself, + * 3) its arguments are constants or cached expressions too. + */ + FuncExpr *funcexpr; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + bool func_returns_set; + + /* firstly recurse into children */ + funcexpr = (FuncExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + func_returns_set = funcexpr->funcretset || + expression_returns_set((Node *) funcexpr->args); + + foreach(arg, funcexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (func_returns_set || + has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &funcexpr->xpr)) + { + /* return FuncExpr, which will not be cached */ + return (Node *) funcexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_FUNCEXPR; + new_node->subexpr.funcexpr = funcexpr; + + return (Node *) new_node; + } + } + else if (IsA(node, OpExpr)) + { + /* + * Operator is cached if: + * 1) its function doesn't return set, + * 1) its function is not volatile itself, + * 3) its arguments are constants or cached expressions too. + */ + OpExpr *opexpr = (OpExpr *) node; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + bool op_returns_set; + + /* rely on struct equivalence to treat these all alike */ + set_opfuncid(opexpr); + + /* firstly recurse into children */ + opexpr = (OpExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + op_returns_set = opexpr->opretset || + expression_returns_set((Node *) opexpr->args); + + foreach(arg, opexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (op_returns_set || + has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &opexpr->xpr)) + { + /* return OpExpr, which will not be cached */ + return (Node *) opexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_OPEXPR; + new_node->subexpr.opexpr = opexpr; + + return (Node *) new_node; + } + } + else if (IsA(node, DistinctExpr)) + { + /* + * Operator of DistinctExpr is cached if: + * 1) its function doesn't return set, + * 1) its function is not volatile itself, + * 3) its arguments are constants or cached expressions too. + */ + DistinctExpr *distinctexpr = (DistinctExpr *) node; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + bool op_returns_set; + + /* rely on struct equivalence to treat these all alike */ + set_opfuncid((OpExpr *) distinctexpr); + + /* firstly recurse into children */ + distinctexpr = (DistinctExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + op_returns_set = distinctexpr->opretset || + expression_returns_set((Node *) distinctexpr->args); + + foreach(arg, distinctexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (op_returns_set || + has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &distinctexpr->xpr)) + { + /* return DistinctExpr, which will not be cached */ + return (Node *) distinctexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_DISTINCTEXPR; + new_node->subexpr.distinctexpr = distinctexpr; + + return (Node *) new_node; + } + } + else if (IsA(node, NullIfExpr)) + { + /* + * Operator of NullIfExpr is cached if: + * 1) its function doesn't return set, + * 1) its function is not volatile itself, + * 3) its arguments are constants or cached expressions too. + */ + NullIfExpr *nullifexpr = (NullIfExpr *) node; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + bool op_returns_set; + + /* rely on struct equivalence to treat these all alike */ + set_opfuncid((OpExpr *) nullifexpr); + + /* firstly recurse into children */ + nullifexpr = (NullIfExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + op_returns_set = nullifexpr->opretset || + expression_returns_set((Node *) nullifexpr->args); + + foreach(arg, nullifexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (op_returns_set || + has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &nullifexpr->xpr)) + { + /* return NullIfExpr, which will not be cached */ + return (Node *) nullifexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_NULLIFEXPR; + new_node->subexpr.nullifexpr = nullifexpr; + + return (Node *) new_node; + } + } + else if (IsA(node, ScalarArrayOpExpr)) + { + /* + * Operator of ScalarArrayOpExpr is cached if: + * 1) its function is not volatile itself, + * 2) its arguments are constants or cached expressions too. + * (it returns boolean so we don't need to check if it returns set) + */ + ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + + set_sa_opfuncid(saopexpr); + + /* firstly recurse into children */ + saopexpr = (ScalarArrayOpExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + + foreach(arg, saopexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &saopexpr->xpr)) + { + /* return ScalarArrayOpExpr, which will not be cached */ + return (Node *) saopexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_SCALARARRAYOPEXPR; + new_node->subexpr.saopexpr = saopexpr; + + return (Node *) new_node; + } + } + + /* otherwise recurse into children */ + return expression_tree_mutator(node, replace_cached_expressions_mutator, + NULL); +} diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h index b6c9b48..0dbfa12 100644 --- a/src/include/nodes/nodeFuncs.h +++ b/src/include/nodes/nodeFuncs.h @@ -76,5 +76,6 @@ extern bool raw_expression_tree_walker(Node *node, bool (*walker) (), struct PlanState; extern bool planstate_tree_walker(struct PlanState *planstate, bool (*walker) (), void *context); +extern Node * get_subexpr(CachedExpr *cachedexpr);#endif /* NODEFUNCS_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f59d719..054bc61 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -155,6 +155,7 @@ typedef enum NodeTag T_OpExpr, T_DistinctExpr, T_NullIfExpr, + T_CachedExpr, T_ScalarArrayOpExpr, T_BoolExpr, T_SubLink, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 86ec82e..3f89653 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1498,4 +1498,42 @@ typedef struct OnConflictExpr List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */ } OnConflictExpr;+/* + * Discriminator for CachedExpr. + * + * Identifies the subexpression to be cached in execution (= executed only once + * and then used cached value) and which member in the CachedExpr->subexpr union + * is valid. + */ +typedef enum CachedSubExprType +{ + CACHED_FUNCEXPR, /* cached FuncExpr */ + CACHED_OPEXPR, /* cached OpExpr */ + CACHED_DISTINCTEXPR, /* cached DistinctExpr */ + CACHED_NULLIFEXPR, /* cached NullIfExpr */ + CACHED_SCALARARRAYOPEXPR /* cached ScalarArrayOpExpr */ +} CachedSubExprType; + +/* + * CachedExpr - expression node for precalculated stable and immutable functions + * (= they are calculated once for all output rows, but as many times as + * function is mentioned in query), if they don't return a set and their + * arguments are constants or recursively precalculated functions. The same for + * operators' functions. + */ +typedef struct CachedExpr +{ + Expr xpr; + CachedSubExprType subexprtype; /* expression to be cached */ + + union SubExpr + { + FuncExpr *funcexpr; /* for CACHED_FUNCEXPR */ + OpExpr *opexpr; /* for CACHED_OPEXPR */ + DistinctExpr *distinctexpr; /* for CACHED_DISTINCTEXPR */ + NullIfExpr *nullifexpr; /* for CACHED_NULLIFEXPR */ + ScalarArrayOpExpr *saopexpr; /* for CACHED_SCALARARRAYOPEXPR */ + } subexpr; +} CachedExpr; + #endif /* PRIMNODES_H */ -- 1.9.1
From 537d8a2bb085efdfce695f148e614ed4611f9a6e Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 15:31:21 +0300
Subject: [PATCH v4 2/3] Precalculate stable functions, planning and executionNow in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.This patch includes:
- replacement nonvolatile functions and operators by appropriate cached
expressions
- planning and execution cached expressions
- regression tests
---
src/backend/executor/execExpr.c | 55 +
src/backend/executor/execExprInterp.c | 51 +
src/backend/optimizer/path/allpaths.c | 9 +-
src/backend/optimizer/path/clausesel.c | 13 +
src/backend/optimizer/plan/planagg.c | 1 +
src/backend/optimizer/plan/planner.c | 28 +
src/backend/optimizer/util/clauses.c | 55 +
src/backend/utils/adt/ruleutils.c | 5 +
src/include/executor/execExpr.h | 37 +
src/include/optimizer/planner.h | 3 +
src/include/optimizer/tlist.h | 8 +-
src/pl/plpgsql/src/pl_exec.c | 10 +
.../expected/precalculate_stable_functions.out | 2625 ++++++++++++++++++++
src/test/regress/serial_schedule | 1 +
.../regress/sql/precalculate_stable_functions.sql | 949 +++++++
15 files changed, 3847 insertions(+), 3 deletions(-)
create mode 100644 src/test/regress/expected/precalculate_stable_functions.out
create mode 100644 src/test/regress/sql/precalculate_stable_functions.sqldiff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 5a34a46..dc84975 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -865,6 +865,61 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state, break; }+ case T_CachedExpr: + { + CachedExpr *cachedexpr = (CachedExpr *) node; + + /* + * Allocate CachedExprState used by all steps of CachedExpr + * evaluation. + */ + scratch.d.cachedexpr.state = (CachedExprState *) palloc( + sizeof(CachedExprState)); + scratch.d.cachedexpr.state->isExecuted = false; + scratch.d.cachedexpr.state->resnull = false; + scratch.d.cachedexpr.state->resvalue = (Datum) 0; + + switch(cachedexpr->subexprtype) + { + case CACHED_FUNCEXPR: + scratch.d.cachedexpr.state->restypid = + cachedexpr->subexpr.funcexpr->funcresulttype; + break; + case CACHED_OPEXPR: + scratch.d.cachedexpr.state->restypid = + cachedexpr->subexpr.opexpr->opresulttype; + break; + case CACHED_DISTINCTEXPR: + scratch.d.cachedexpr.state->restypid = + cachedexpr->subexpr.distinctexpr->opresulttype; + break; + case CACHED_NULLIFEXPR: + scratch.d.cachedexpr.state->restypid = + cachedexpr->subexpr.nullifexpr->opresulttype; + break; + case CACHED_SCALARARRAYOPEXPR: + scratch.d.cachedexpr.state->restypid = BOOLOID; + break; + } + + /* add EEOP_CACHEDEXPR_IF_CACHED step */ + scratch.opcode = EEOP_CACHEDEXPR_IF_CACHED; + ExprEvalPushStep(state, &scratch); + + /* add subexpression steps */ + ExecInitExprRec((Expr *) get_subexpr(cachedexpr), parent, state, + resv, resnull); + + /* add EEOP_CACHEDEXPR_SUBEXPR_END step */ + scratch.opcode = EEOP_CACHEDEXPR_SUBEXPR_END; + ExprEvalPushStep(state, &scratch); + + /* adjust jump target */ + scratch.d.cachedexpr.state->jumpdone = state->steps_len; + + break; + } + case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node; diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index fed0052..2cb10fd 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -70,6 +70,7 @@ #include "pgstat.h" #include "utils/builtins.h" #include "utils/date.h" +#include "utils/datum.h" #include "utils/lsyscache.h" #include "utils/timestamp.h" #include "utils/typcache.h" @@ -309,6 +310,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) &&CASE_EEOP_FUNCEXPR_STRICT, &&CASE_EEOP_FUNCEXPR_FUSAGE, &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEOP_CACHEDEXPR_IF_CACHED, + &&CASE_EEOP_CACHEDEXPR_SUBEXPR_END, &&CASE_EEOP_BOOL_AND_STEP_FIRST, &&CASE_EEOP_BOOL_AND_STEP, &&CASE_EEOP_BOOL_AND_STEP_LAST, @@ -721,6 +724,54 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) EEO_NEXT(); }+ EEO_CASE(EEOP_CACHEDEXPR_IF_CACHED) + { + if (op->d.cachedexpr.state->isExecuted) + { + /* use saved result and skip subexpression evaluation */ + *op->resnull = op->d.cachedexpr.state->resnull; + if (!(*op->resnull)) + *op->resvalue = op->d.cachedexpr.state->resvalue; + + EEO_JUMP(op->d.cachedexpr.state->jumpdone); + } + + /* we are ready for subexpression evaluation */ + EEO_NEXT(); + } + + EEO_CASE(EEOP_CACHEDEXPR_SUBEXPR_END) + { + int16 restyplen; + bool restypbyval; + + /* save result */ + op->d.cachedexpr.state->resnull = *op->resnull; + if (!(*op->resnull)) + { + get_typlenbyval(op->d.cachedexpr.state->restypid, &restyplen, + &restypbyval); + + /* + * Switch per-query memory context. It is necessary to save the + * subexpression result value between all tuples if its datum is + * a pointer. + */ + op->d.cachedexpr.state->oldContext = MemoryContextSwitchTo( + econtext->ecxt_per_query_memory); + + op->d.cachedexpr.state->resvalue = datumCopy(*op->resvalue, + restypbyval, + restyplen); + + /* switch memory context back */ + MemoryContextSwitchTo(op->d.cachedexpr.state->oldContext); + } + op->d.cachedexpr.state->isExecuted = true; + + EEO_NEXT(); + } + /* * If any of its clauses is FALSE, an AND's result is FALSE regardless * of the states of the rest of the clauses, so we can stop evaluating diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index b93b4fc..a322255 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -378,7 +378,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel, set_subquery_pathlist(root, rel, rti, rte); break; case RTE_FUNCTION: - set_function_size_estimates(root, rel); + { + rel->baserestrictinfo = replace_qual_cached_expressions( + rel->baserestrictinfo); + set_function_size_estimates(root, rel); + } break; case RTE_TABLEFUNC: set_tablefunc_size_estimates(root, rel); @@ -517,6 +521,9 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) */ check_index_predicates(root, rel);+ rel->baserestrictinfo = replace_qual_cached_expressions( + rel->baserestrictinfo); + /* Mark rel with estimated output rows, width, etc */ set_baserel_size_estimates(root, rel); } diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 758ddea..fc799f1 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -15,6 +15,7 @@ #include "postgres.h"#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" @@ -825,6 +826,18 @@ clause_selectivity(PlannerInfo *root, jointype, sjinfo); } + else if (IsA(clause, CachedExpr)) + { + /* + * Not sure this case is needed, but it can't hurt. + * Calculate selectivity of subexpression. + */ + s1 = clause_selectivity(root, + get_subexpr((CachedExpr *) clause), + varRelid, + jointype, + sjinfo); + } else { /* diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index 5565736..7a28764 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -38,6 +38,7 @@ #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" +#include "optimizer/planner.h" #include "optimizer/subselect.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 552b73d..7c68d6d 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -6088,6 +6088,34 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti) return result; }+/* + * replace_pathtarget_cached_expressions + * Replace cached expresisons in a PathTarget tlist. + * + * As a notational convenience, returns the same PathTarget pointer passed in. + */ +PathTarget * +replace_pathtarget_cached_expressions(PathTarget *target) +{ + target->exprs = (List *) replace_cached_expressions_mutator( + (Node *) target->exprs); + + return target; +} + +/* + * replace_qual_cached_expressions + * Replace cacehd expressions in a WHERE clause. The input can be either an + * implicitly-ANDed list of boolean expressions, or a list of RestrictInfo + * nodes. + */ +List * +replace_qual_cached_expressions(List *quals) +{ + /* No setup needed for tree walk, so away we go */ + return (List *) replace_cached_expressions_mutator((Node *) quals); +} + static Node * replace_cached_expressions_mutator(Node *node) { diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index a1dafc8..0c0284a 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -2758,6 +2758,61 @@ eval_const_expressions_mutator(Node *node, newexpr->location = expr->location; return (Node *) newexpr; } + case T_CachedExpr: + { + CachedExpr *cachedexpr = (CachedExpr *) node; + Node *new_subexpr = eval_const_expressions_mutator( + get_subexpr(cachedexpr), context); + CachedExpr *new_cachedexpr; + + /* + * If unsafe transformations are used cached expression should + * be always simplified. + */ + if (context->estimate) + Assert(IsA(new_subexpr, Const)); + + if (IsA(new_subexpr, Const)) + { + /* successfully simplified it */ + return new_subexpr; + } + else + { + /* + * The expression cannot be simplified any further, so build + * and return a replacement CachedExpr node using the + * possibly-simplified arguments of subexpression. + */ + new_cachedexpr = makeNode(CachedExpr); + new_cachedexpr->subexprtype = cachedexpr->subexprtype; + switch (new_cachedexpr->subexprtype) + { + case CACHED_FUNCEXPR: + new_cachedexpr->subexpr.funcexpr = (FuncExpr *) + new_subexpr; + break; + case CACHED_OPEXPR: + new_cachedexpr->subexpr.opexpr = (OpExpr *) + new_subexpr; + break; + case CACHED_DISTINCTEXPR: + new_cachedexpr->subexpr.distinctexpr = + (DistinctExpr *) new_subexpr; + break; + case CACHED_NULLIFEXPR: + new_cachedexpr->subexpr.nullifexpr = (NullIfExpr *) + new_subexpr; + break; + case CACHED_SCALARARRAYOPEXPR: + new_cachedexpr->subexpr.saopexpr = + (ScalarArrayOpExpr *) new_subexpr; + break; + } + + return (Node *) new_cachedexpr; + } + } case T_BoolExpr: { BoolExpr *expr = (BoolExpr *) node; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 43b1475..838389d 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -7720,6 +7720,11 @@ get_rule_expr(Node *node, deparse_context *context, } break;+ case T_CachedExpr: + get_rule_expr(get_subexpr((CachedExpr *) node), context, + showimplicit); + break; + case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 86fdb33..ea37a36 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -86,6 +86,16 @@ typedef enum ExprEvalOp EEOP_FUNCEXPR_STRICT_FUSAGE,/* + * Evaluate CachedExpr. EEOP_CACHEDEXPR_IF_CACHED is used before + * subexpression evaluation (if subexpression was evaluated use cached value + * and jump to next state or get prepared to subexpression evaluation + * otherwise). EEOP_CACHEDEXPR_SUBEXPR_END is used after subexpression + * evaluation for caching its result. + */ + EEOP_CACHEDEXPR_IF_CACHED, + EEOP_CACHEDEXPR_SUBEXPR_END, + + /* * Evaluate boolean AND expression, one step per subexpression. FIRST/LAST * subexpressions are special-cased for performance. Since AND always has * at least two subexpressions, FIRST and LAST never apply to the same @@ -298,6 +308,13 @@ typedef struct ExprEvalStep int nargs; /* number of arguments */ } func;+ /* for EEOP_CACHEDEXPR_* */ + struct + { + /* steps for evaluation the same CachedExpr have the same state */ + struct CachedExprState *state; + } cachedexpr; + /* for EEOP_BOOL_*_STEP */ struct { @@ -600,6 +617,26 @@ typedef struct ArrayRefState } ArrayRefState;+/* + * Non-inline data for EEOP_CACHEDEXPR_* operations (steps for evaluation the + * same CachedExpr have the same state). + */ +typedef struct CachedExprState +{ + bool isExecuted; + bool resnull; + Datum resvalue; + Oid restypid; /* for copying resvalue of subexpression */ + int jumpdone; /* jump here if result determined */ + + /* + * For switching per-query memory context. It is necessary to save the + * subexpression result between all tuples if its value datum is a pointer. + */ + MemoryContext oldContext; +} CachedExprState; + + extern void ExecReadyInterpretedExpr(ExprState *state);extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op); diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index f3aaa23..bbadcdd 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -59,4 +59,7 @@ extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti);
+extern PathTarget *replace_pathtarget_cached_expressions(PathTarget *target); +extern List *replace_qual_cached_expressions(List *quals); + #endif /* PLANNER_H */ diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h index ccb93d8..7488bd2 100644 --- a/src/include/optimizer/tlist.h +++ b/src/include/optimizer/tlist.h @@ -65,8 +65,12 @@ extern void split_pathtarget_at_srfs(PlannerInfo *root, PathTarget *target, PathTarget *input_target, List **targets, List **targets_contain_srfs);-/* Convenience macro to get a PathTarget with valid cost/width fields */ +/* + * Convenience macro to get a PathTarget with valid cost/width fields and + * cached expressions. + */ #define create_pathtarget(root, tlist) \ - set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist)) + set_pathtarget_cost_width(root, replace_pathtarget_cached_expressions( \ + make_pathtarget_from_tlist(tlist)))#endif /* TLIST_H */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 7a40c99..2e27052 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -6535,6 +6535,16 @@ exec_simple_check_node(Node *node) return TRUE; }+ case T_CachedExpr: + { + /* + * If CachedExpr will not be initialized by ExecInitCachedExpr + * possibly it will use cached value when it shouldn't (for + * example, snapshot has changed), so return false. + */ + return FALSE; + } + case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; diff --git a/src/test/regress/expected/precalculate_stable_functions.out b/src/test/regress/expected/precalculate_stable_functions.out new file mode 100644 index 0000000..093e6f8 --- /dev/null +++ b/src/test/regress/expected/precalculate_stable_functions.out @@ -0,0 +1,2625 @@ +-- +-- PRECALCULATE STABLE FUNCTIONS +-- +-- Create types and tables for testing +CREATE TYPE my_integer AS (value integer); +CREATE TABLE two (i integer); +INSERT INTO two VALUES (1), (2); +-- Create volatile functions for testing +CREATE OR REPLACE FUNCTION public.x_vlt ( +) +RETURNS integer VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'v'; + RETURN 1; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.equal_integers_vlt ( + integer, + integer +) +RETURNS boolean VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'equal integers volatile'; + RETURN $1 = $2; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.x_vlt_my_integer ( +) +RETURNS my_integer VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'v my_integer'; + RETURN '(1)'::my_integer; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt ( + my_integer, + my_integer +) +RETURNS boolean VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'equal my_integer volatile'; + RETURN $1.value = $2.value; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.x_vlt_array_int ( +) +RETURNS int[] VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'v array_int'; + RETURN '{2, 3}'::int[]; +END; +$body$ +LANGUAGE 'plpgsql'; +-- Create stable functions for testing +CREATE OR REPLACE FUNCTION public.x_stl ( +) +RETURNS integer STABLE AS +$body$ +BEGIN + RAISE NOTICE 's'; + RETURN 1; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.x_stl2 ( + integer +) +RETURNS integer STABLE AS +$body$ +BEGIN + RAISE NOTICE 's2'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.x_stl2_strict ( + integer +) +RETURNS integer STABLE STRICT AS +$body$ +BEGIN + RAISE NOTICE 's2 strict'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.equal_integers_stl ( + integer, + integer +) +RETURNS boolean STABLE AS +$body$ +BEGIN + RAISE NOTICE 'equal integers stable'; + RETURN $1 = $2; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.x_stl2_boolean ( + boolean +) +RETURNS boolean STABLE AS +$body$ +BEGIN + RAISE NOTICE 's2 boolean'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict ( + boolean, + boolean +) +RETURNS boolean STABLE STRICT AS +$body$ +BEGIN + RAISE NOTICE 'equal booleans stable strict'; + RETURN $1 = $2; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.x_stl_my_integer ( +) +RETURNS my_integer STABLE AS +$body$ +BEGIN + RAISE NOTICE 's my_integer'; + RETURN '(1)'::my_integer; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.equal_my_integer_stl ( + my_integer, + my_integer +) +RETURNS boolean STABLE AS +$body$ +BEGIN + RAISE NOTICE 'equal my_integer stable'; + RETURN $1.value = $2.value; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.x_stl_array_int ( +) +RETURNS int[] STABLE AS +$body$ +BEGIN + RAISE NOTICE 's array_int'; + RETURN '{2, 3}'::int[]; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.stable_max( +) +RETURNS integer STABLE AS +$body$ +BEGIN + RETURN (SELECT max(i) from two); +END +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.simple( +) +RETURNS integer STABLE AS +$body$ +BEGIN + RETURN stable_max(); +END +$body$ +LANGUAGE 'plpgsql'; +-- Create immutable functions for testing +CREATE OR REPLACE FUNCTION public.x_imm2 ( + integer +) +RETURNS integer IMMUTABLE AS +$body$ +BEGIN + RAISE NOTICE 'i2'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.x_imm2_strict ( + integer +) +RETURNS integer IMMUTABLE STRICT AS +$body$ +BEGIN + RAISE NOTICE 'i2 strict'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.equal_integers_imm ( + integer, + integer +) +RETURNS boolean IMMUTABLE AS +$body$ +BEGIN + RAISE NOTICE 'equal integers immutable'; + RETURN $1 = $2; +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION public.equal_my_integer_imm ( + my_integer, + my_integer +) +RETURNS boolean IMMUTABLE AS +$body$ +BEGIN + RAISE NOTICE 'equal my_integer immutable'; + RETURN $1.value = $2.value; +END; +$body$ +LANGUAGE 'plpgsql'; +-- Create operators for testing +CREATE operator === ( + PROCEDURE = equal_integers_vlt, + LEFTARG = integer, + RIGHTARG = integer +); +CREATE operator ==== ( + PROCEDURE = equal_integers_stl, + LEFTARG = integer, + RIGHTARG = integer +); +CREATE operator ===== ( + PROCEDURE = equal_integers_imm, + LEFTARG = integer, + RIGHTARG = integer +); +CREATE operator ====== ( + PROCEDURE = equal_booleans_stl_strict, + LEFTARG = boolean, + RIGHTARG = boolean +); +CREATE operator ==== ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- Simple functions testing +SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated +NOTICE: v +NOTICE: v +NOTICE: v + x_vlt +------- + 1 + 1 + 1 +(3 rows) + +SELECT x_stl() FROM generate_series(1, 3) x; +NOTICE: s + x_stl +------- + 1 + 1 + 1 +(3 rows) + +-- WHERE clause testing +SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated +NOTICE: v +NOTICE: v +NOTICE: v +NOTICE: v +NOTICE: v +NOTICE: v +NOTICE: v + x_vlt +------- + 1 + 1 + 1 +(3 rows) + +SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x; +NOTICE: s +NOTICE: s +NOTICE: s + x_stl +------- + 1 + 1 + 1 +(3 rows) + +-- Functions with constant arguments and nested functions testing +SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: s2 +NOTICE: v +NOTICE: s2 +NOTICE: v +NOTICE: s2 +NOTICE: v +NOTICE: s2 + x_stl2 +-------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: i2 +NOTICE: v +NOTICE: i2 +NOTICE: v +NOTICE: i2 +NOTICE: v +NOTICE: i2 + x_imm2 +-------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x; +NOTICE: s2 +NOTICE: s2 + x_stl2 +-------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x; +NOTICE: s2 +NOTICE: i2 + x_imm2 +-------- + 1 + 1 + 1 + 1 +(4 rows) + +-- Strict functions testing +SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: s2 strict +NOTICE: v +NOTICE: s2 strict +NOTICE: v +NOTICE: s2 strict +NOTICE: v +NOTICE: s2 strict + x_stl2_strict +--------------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: i2 strict +NOTICE: v +NOTICE: i2 strict +NOTICE: v +NOTICE: i2 strict +NOTICE: v +NOTICE: i2 strict + x_imm2_strict +--------------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x; +NOTICE: s2 strict +NOTICE: s2 strict + x_stl2_strict +--------------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x; +NOTICE: s2 strict +NOTICE: i2 strict + x_imm2_strict +--------------- + 1 + 1 + 1 + 1 +(4 rows) + +-- Strict functions with null arguments testing +SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 + x_stl2_strict +--------------- + + + + +(4 rows) + +SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 + x_imm2_strict +--------------- + + + + +(4 rows) + +-- Operators testing +SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT 1 ==== 2 FROM generate_series(1, 4) x; +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +-- Nested and strict operators testing +-- (also partly mixed functions and operators testing) +SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal booleans stable strict +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal booleans stable strict +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal booleans stable strict +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal booleans stable strict + ?column? +---------- + t + t + t + t +(4 rows) + +SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: equal integers stable +NOTICE: s +NOTICE: equal integers immutable +NOTICE: equal booleans stable strict + ?column? +---------- + t + t + t + t +(4 rows) + +SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: s2 boolean + ?column? +---------- + + + + +(4 rows) + +-- IS DISTINCT FROM expression testing +-- create operator here because we will drop and reuse it several times +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile + ?column? +---------- + t + t + t + t +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable + ?column? +---------- + t + t + t + t +(4 rows) + +-- IS DISTINCT FROM expressions with null arguments testing +SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL)) +FROM generate_series(1, 4) x; +NOTICE: s2 +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL)) +FROM generate_series(1, 4) x; +NOTICE: s2 +NOTICE: s2 +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- Nested IS DISTINCT FROM expression testing +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM + TRUE +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM + TRUE +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- NULLIF expressions testing +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile + nullif +-------- + (1) + (1) + (1) + (1) +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable + nullif +-------- + (1) + (1) + (1) + (1) +(4 rows) + +-- NULLIF expressions with null arguments testing +SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x; +NOTICE: s2 +NOTICE: s2 + x_stl2 +-------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x; +NOTICE: s2 +NOTICE: s2 +NOTICE: s2 + x_stl2 +-------- + + + + +(4 rows) + +-- Nested NULLIF expression testing +-- should not be precalculated +SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer) +FROM generate_series(1, 4) x; +NOTICE: v my_integer +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable +NOTICE: v my_integer +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable +NOTICE: v my_integer +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable +NOTICE: v my_integer +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable + nullif +-------- + (1) + (1) + (1) + (1) +(4 rows) + +SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer) +FROM generate_series(1, 4) x; +NOTICE: s my_integer +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable + nullif +-------- + (1) + (1) + (1) + (1) +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_imm, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer) +FROM generate_series(1, 4) x; +NOTICE: v my_integer +NOTICE: equal my_integer immutable +NOTICE: equal my_integer immutable +NOTICE: v my_integer +NOTICE: equal my_integer immutable +NOTICE: equal my_integer immutable +NOTICE: v my_integer +NOTICE: equal my_integer immutable +NOTICE: equal my_integer immutable +NOTICE: v my_integer +NOTICE: equal my_integer immutable +NOTICE: equal my_integer immutable + nullif +-------- + (1) + (1) + (1) + (1) +(4 rows) + +SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer) +FROM generate_series(1, 4) x; +NOTICE: s my_integer +NOTICE: equal my_integer immutable +NOTICE: equal my_integer immutable + nullif +-------- + (1) + (1) + (1) + (1) +(4 rows) + +-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions +-- testing +SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile + ?column? +---------- + f + f + f + f +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x; +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable + ?column? +---------- + f + f + f + f +(4 rows) + +-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with +-- null arguments testing +SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable + ?column? +---------- + + + + +(4 rows) + +SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 boolean + x_stl2_boolean +---------------- + + + + +(4 rows) + +SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable + ?column? +---------- + + + + +(4 rows) + +SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable + ?column? +---------- + + + + +(4 rows) + +SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 boolean + x_stl2_boolean +---------------- + + + + +(4 rows) + +SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x; +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 boolean + x_stl2_boolean +---------------- + + + + +(4 rows) + +SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable + ?column? +---------- + + + + +(4 rows) + +SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable + ?column? +---------- + + + + +(4 rows) + +SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 boolean + x_stl2_boolean +---------------- + + + + +(4 rows) + +SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 boolean + x_stl2_boolean +---------------- + + + + +(4 rows) + +SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x; +NOTICE: s2 boolean + x_stl2_boolean +---------------- + + + + +(4 rows) + +SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 boolean + x_stl2_boolean +---------------- + + + + +(4 rows) + +-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" +-- expressions testing (also partly mixed functions and "scalar op ANY/ALL +-- (array)" / "scalar IN (2 or more values)" expressions testing) +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: equal integers immutable +NOTICE: equal integers immutable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +-- Mixed functions and operators testing +-- (most of it was earlier in Nested and strict operators testing) +SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- Mixed functions and IS DISTINCT FROM expressions testing +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT equal_booleans_stl_strict( + (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer), + (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: s my_integer +NOTICE: equal my_integer volatile +NOTICE: s my_integer +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict + equal_booleans_stl_strict +--------------------------- + f + f + f + f +(4 rows) + +SELECT equal_booleans_stl_strict( + (x_stl() IS DISTINCT FROM 1), + (x_stl() IS DISTINCT FROM 2) +) +FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: s +NOTICE: equal booleans stable strict + equal_booleans_stl_strict +--------------------------- + f + f + f + f +(4 rows) + +-- Mixed functions and NULLIF expressions testing +-- should not be precalculated +SELECT equal_my_integer_stl( + NULLIF(x_stl_my_integer(), '(1)'::my_integer), + NULLIF(x_stl_my_integer(), '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: s my_integer +NOTICE: equal my_integer volatile +NOTICE: s my_integer +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable + equal_my_integer_stl +---------------------- + + + + +(4 rows) + +SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2)) +FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: s +NOTICE: equal integers stable + equal_integers_stl +-------------------- + + + + +(4 rows) + +-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more +-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" / +-- "scalar IN (2 or more values)" expressions testing) +SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v array_int +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: v array_int +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: v array_int +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: v array_int +NOTICE: equal integers stable +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v array_int +NOTICE: equal integers stable +NOTICE: v array_int +NOTICE: equal integers stable +NOTICE: v array_int +NOTICE: equal integers stable +NOTICE: v array_int +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x; +NOTICE: s array_int +NOTICE: equal integers stable +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x; +NOTICE: s array_int +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +-- Mixed operators and IS DISTINCT FROM expressions testing +-- should not be precalculated +SELECT ( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ====== + ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict + ?column? +---------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE) +FROM generate_series(1, 4) x; +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT ( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ====== + ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable +NOTICE: equal booleans stable strict + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE) +FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +-- Mixed operators and NULLIF expressions testing +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + NULLIF('(2)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable + ?column? +---------- + + + + +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x; +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + NULLIF('(2)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable + ?column? +---------- + + + + +(4 rows) + +SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more +-- values)" expressions testing +-- should not be precalculated +SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}')) +FROM generate_series(1, 4) x; +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal booleans stable strict +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal booleans stable strict +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal booleans stable strict +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal booleans stable strict + ?column? +---------- + t + t + t + t +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal booleans stable strict + ?column? +---------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}')) +FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: equal booleans stable strict + ?column? +---------- + t + t + t + t +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable +NOTICE: equal booleans stable strict + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +-- Mixed IS DISTINCT FROM and NULLIF expressions testing +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM + NULLIF('(2)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile + ?column? +---------- + t + t + t + t +(4 rows) + +-- should not be precalculated +SELECT NULLIF( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer), + ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile + nullif +-------- + t + t + t + t +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM + NULLIF('(2)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable + ?column? +---------- + t + t + t + t +(4 rows) + +SELECT NULLIF( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer), + ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable + nullif +-------- + t + t + t + t +(4 rows) + +-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or +-- more values)" expressions testing +-- should not be precalculated +SELECT x_stl2_boolean( + (1 === ANY('{2, 3}')) IS DISTINCT FROM + (1 === ALL('{2, 3}')) +) +FROM generate_series(1, 4) x; +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM + TRUE +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}') +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}') +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +SELECT x_stl2_boolean( + (1 ==== ANY('{2, 3}')) IS DISTINCT FROM + (1 ==== ALL('{2, 3}')) +) +FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT x_stl2_boolean( + ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM + TRUE +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}') +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}') +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + t + t + t + t +(4 rows) + +-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" +-- expressions testing +-- should not be precalculated +SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}'))) +FROM generate_series(1, 4) x; +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: s2 boolean +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + + + + +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +-- should not be precalculated +SELECT x_stl2_boolean(NULLIF( + '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer), + TRUE +)) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: s2 boolean +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + ANY('{(3)}'::my_integer[]) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable + ?column? +---------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + ALL('{(3)}'::my_integer[]) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable +NOTICE: equal my_integer volatile +NOTICE: equal my_integer stable + ?column? +---------- + f + f + f + f +(4 rows) + +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) IN + ('(3)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile +NOTICE: equal my_integer volatile + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}'))) +FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + + + + +(4 rows) + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); +SELECT x_stl2_boolean(NULLIF( + '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer), + TRUE +)) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + ANY('{(3)}'::my_integer[]) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + ALL('{(3)}'::my_integer[]) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) IN + ('(3)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable +NOTICE: equal my_integer stable + ?column? +---------- + f + f + f + f +(4 rows) + +-- Tracking functions testing +SET track_functions TO 'all'; +SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated +NOTICE: v +NOTICE: v +NOTICE: v + x_vlt +------- + 1 + 1 + 1 +(3 rows) + +SELECT x_stl() FROM generate_series(1, 3) x; +NOTICE: s + x_stl +------- + 1 + 1 + 1 +(3 rows) + +SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated +NOTICE: v +NOTICE: v +NOTICE: v +NOTICE: v +NOTICE: v +NOTICE: v +NOTICE: v + x_vlt +------- + 1 + 1 + 1 +(3 rows) + +SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x; +NOTICE: s +NOTICE: s +NOTICE: s + x_stl +------- + 1 + 1 + 1 +(3 rows) + +SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: s2 +NOTICE: v +NOTICE: s2 +NOTICE: v +NOTICE: s2 +NOTICE: v +NOTICE: s2 + x_stl2 +-------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: i2 +NOTICE: v +NOTICE: i2 +NOTICE: v +NOTICE: i2 +NOTICE: v +NOTICE: i2 + x_imm2 +-------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x; +NOTICE: s2 +NOTICE: s2 + x_stl2 +-------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x; +NOTICE: s2 +NOTICE: i2 + x_imm2 +-------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: s2 strict +NOTICE: v +NOTICE: s2 strict +NOTICE: v +NOTICE: s2 strict +NOTICE: v +NOTICE: s2 strict + x_stl2_strict +--------------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: i2 strict +NOTICE: v +NOTICE: i2 strict +NOTICE: v +NOTICE: i2 strict +NOTICE: v +NOTICE: i2 strict + x_imm2_strict +--------------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x; +NOTICE: s2 strict +NOTICE: s2 strict + x_stl2_strict +--------------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x; +NOTICE: s2 strict +NOTICE: i2 strict + x_imm2_strict +--------------- + 1 + 1 + 1 + 1 +(4 rows) + +SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 + x_stl2_strict +--------------- + + + + +(4 rows) + +SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x; +NOTICE: s2 + x_imm2_strict +--------------- + + + + +(4 rows) + +SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile +NOTICE: equal integers volatile + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT 1 ==== 2 FROM generate_series(1, 4) x; +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT 1 ===== 2 FROM generate_series(1, 4) x; +NOTICE: equal integers immutable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal booleans stable strict +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal booleans stable strict +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal booleans stable strict +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: equal booleans stable strict + ?column? +---------- + t + t + t + t +(4 rows) + +SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: equal integers stable +NOTICE: equal booleans stable strict + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x; +NOTICE: s2 boolean +NOTICE: equal integers stable + ?column? +---------- + + + + +(4 rows) + +SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers stable +NOTICE: v +NOTICE: equal integers stable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated +NOTICE: v +NOTICE: equal integers immutable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: v +NOTICE: equal integers immutable +NOTICE: v +NOTICE: equal integers immutable + ?column? +---------- + f + f + f + f +(4 rows) + +SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x; +NOTICE: s +NOTICE: s +NOTICE: equal integers stable + ?column? +---------- + t + t + t + t +(4 rows) + +SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x; +NOTICE: equal integers stable +NOTICE: s2 boolean + x_stl2_boolean +---------------- + f + f + f + f +(4 rows) + +SET track_functions TO DEFAULT; +-- PL/pgSQL Simple expressions +-- Make sure precalculated stable functions can't be simple expressions: these +-- expressions are only initialized once per transaction and then executed +-- multiple times. +BEGIN; +SELECT simple(); + simple +-------- + 2 +(1 row) + +INSERT INTO two VALUES (3); +SELECT simple(); + simple +-------- + 3 +(1 row) + +ROLLBACK; +-- Drop tables for testing +DROP TABLE two; diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 04206c3..f2710b9 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -179,3 +179,4 @@ test: with test: xml test: event_trigger test: stats +test: precalculate_stable_functions diff --git a/src/test/regress/sql/precalculate_stable_functions.sql b/src/test/regress/sql/precalculate_stable_functions.sql new file mode 100644 index 0000000..a59791d --- /dev/null +++ b/src/test/regress/sql/precalculate_stable_functions.sql @@ -0,0 +1,949 @@ +-- +-- PRECALCULATE STABLE FUNCTIONS +-- + +-- Create types and tables for testing + +CREATE TYPE my_integer AS (value integer); + +CREATE TABLE two (i integer); +INSERT INTO two VALUES (1), (2); + +-- Create volatile functions for testing + +CREATE OR REPLACE FUNCTION public.x_vlt ( +) +RETURNS integer VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'v'; + RETURN 1; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.equal_integers_vlt ( + integer, + integer +) +RETURNS boolean VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'equal integers volatile'; + RETURN $1 = $2; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.x_vlt_my_integer ( +) +RETURNS my_integer VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'v my_integer'; + RETURN '(1)'::my_integer; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt ( + my_integer, + my_integer +) +RETURNS boolean VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'equal my_integer volatile'; + RETURN $1.value = $2.value; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.x_vlt_array_int ( +) +RETURNS int[] VOLATILE AS +$body$ +BEGIN + RAISE NOTICE 'v array_int'; + RETURN '{2, 3}'::int[]; +END; +$body$ +LANGUAGE 'plpgsql'; + +-- Create stable functions for testing + +CREATE OR REPLACE FUNCTION public.x_stl ( +) +RETURNS integer STABLE AS +$body$ +BEGIN + RAISE NOTICE 's'; + RETURN 1; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.x_stl2 ( + integer +) +RETURNS integer STABLE AS +$body$ +BEGIN + RAISE NOTICE 's2'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.x_stl2_strict ( + integer +) +RETURNS integer STABLE STRICT AS +$body$ +BEGIN + RAISE NOTICE 's2 strict'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.equal_integers_stl ( + integer, + integer +) +RETURNS boolean STABLE AS +$body$ +BEGIN + RAISE NOTICE 'equal integers stable'; + RETURN $1 = $2; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.x_stl2_boolean ( + boolean +) +RETURNS boolean STABLE AS +$body$ +BEGIN + RAISE NOTICE 's2 boolean'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict ( + boolean, + boolean +) +RETURNS boolean STABLE STRICT AS +$body$ +BEGIN + RAISE NOTICE 'equal booleans stable strict'; + RETURN $1 = $2; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.x_stl_my_integer ( +) +RETURNS my_integer STABLE AS +$body$ +BEGIN + RAISE NOTICE 's my_integer'; + RETURN '(1)'::my_integer; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.equal_my_integer_stl ( + my_integer, + my_integer +) +RETURNS boolean STABLE AS +$body$ +BEGIN + RAISE NOTICE 'equal my_integer stable'; + RETURN $1.value = $2.value; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.x_stl_array_int ( +) +RETURNS int[] STABLE AS +$body$ +BEGIN + RAISE NOTICE 's array_int'; + RETURN '{2, 3}'::int[]; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.stable_max( +) +RETURNS integer STABLE AS +$body$ +BEGIN + RETURN (SELECT max(i) from two); +END +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.simple( +) +RETURNS integer STABLE AS +$body$ +BEGIN + RETURN stable_max(); +END +$body$ +LANGUAGE 'plpgsql'; + +-- Create immutable functions for testing + +CREATE OR REPLACE FUNCTION public.x_imm2 ( + integer +) +RETURNS integer IMMUTABLE AS +$body$ +BEGIN + RAISE NOTICE 'i2'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.x_imm2_strict ( + integer +) +RETURNS integer IMMUTABLE STRICT AS +$body$ +BEGIN + RAISE NOTICE 'i2 strict'; + RETURN $1; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.equal_integers_imm ( + integer, + integer +) +RETURNS boolean IMMUTABLE AS +$body$ +BEGIN + RAISE NOTICE 'equal integers immutable'; + RETURN $1 = $2; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION public.equal_my_integer_imm ( + my_integer, + my_integer +) +RETURNS boolean IMMUTABLE AS +$body$ +BEGIN + RAISE NOTICE 'equal my_integer immutable'; + RETURN $1.value = $2.value; +END; +$body$ +LANGUAGE 'plpgsql'; + +-- Create operators for testing + +CREATE operator === ( + PROCEDURE = equal_integers_vlt, + LEFTARG = integer, + RIGHTARG = integer +); + +CREATE operator ==== ( + PROCEDURE = equal_integers_stl, + LEFTARG = integer, + RIGHTARG = integer +); + +CREATE operator ===== ( + PROCEDURE = equal_integers_imm, + LEFTARG = integer, + RIGHTARG = integer +); + +CREATE operator ====== ( + PROCEDURE = equal_booleans_stl_strict, + LEFTARG = boolean, + RIGHTARG = boolean +); + +CREATE operator ==== ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- Simple functions testing + +SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated +SELECT x_stl() FROM generate_series(1, 3) x; + +-- WHERE clause testing + +SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated +SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x; + +-- Functions with constant arguments and nested functions testing + +SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated + +SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x; +SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x; + +-- Strict functions testing + +SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated + +SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x; +SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x; + +-- Strict functions with null arguments testing + +SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x; +SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x; + +-- Operators testing + +SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated +SELECT 1 ==== 2 FROM generate_series(1, 4) x; + +-- Nested and strict operators testing +-- (also partly mixed functions and operators testing) + +SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated +SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x; +SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x; + +-- IS DISTINCT FROM expression testing + +-- create operator here because we will drop and reuse it several times +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer +FROM generate_series(1, 4) x; + +-- IS DISTINCT FROM expressions with null arguments testing + +SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL)) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL)) +FROM generate_series(1, 4) x; + +-- Nested IS DISTINCT FROM expression testing + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM + TRUE +) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM + TRUE +) +FROM generate_series(1, 4) x; + +-- NULLIF expressions testing + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) +FROM generate_series(1, 4) x; + +-- NULLIF expressions with null arguments testing + +SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x; + +SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x; + +-- Nested NULLIF expression testing + +-- should not be precalculated +SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer) +FROM generate_series(1, 4) x; + +SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_imm, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer) +FROM generate_series(1, 4) x; + +SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer) +FROM generate_series(1, 4) x; + +-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions +-- testing + +SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated +SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) +FROM generate_series(1, 4) x; + +SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x; +SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) +FROM generate_series(1, 4) x; + +-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with +-- null arguments testing + +SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x; +SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x; +SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x; +SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x; +SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x; + +SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x; +SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x; +SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x; +SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x; +SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x; +SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x; +SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x; + +-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" +-- expressions testing (also partly mixed functions and "scalar op ANY/ALL +-- (array)" / "scalar IN (2 or more values)" expressions testing) + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; + +-- Mixed functions and operators testing +-- (most of it was earlier in Nested and strict operators testing) + +SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x; + +-- Mixed functions and IS DISTINCT FROM expressions testing + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT equal_booleans_stl_strict( + (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer), + (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +SELECT equal_booleans_stl_strict( + (x_stl() IS DISTINCT FROM 1), + (x_stl() IS DISTINCT FROM 2) +) +FROM generate_series(1, 4) x; + +-- Mixed functions and NULLIF expressions testing + +-- should not be precalculated +SELECT equal_my_integer_stl( + NULLIF(x_stl_my_integer(), '(1)'::my_integer), + NULLIF(x_stl_my_integer(), '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2)) +FROM generate_series(1, 4) x; + +-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more +-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" / +-- "scalar IN (2 or more values)" expressions testing) + +SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated +SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated + +SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x; +SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x; + +-- Mixed operators and IS DISTINCT FROM expressions testing + +-- should not be precalculated +SELECT ( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ====== + ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT ( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ====== + ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE) +FROM generate_series(1, 4) x; + +-- Mixed operators and NULLIF expressions testing + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + NULLIF('(2)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + NULLIF('(2)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x; + +-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more +-- values)" expressions testing + +-- should not be precalculated +SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}')) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; + +SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}')) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}')) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}')) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE)) +FROM generate_series(1, 4) x; + +-- Mixed IS DISTINCT FROM and NULLIF expressions testing + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM + NULLIF('(2)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT NULLIF( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer), + ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM + NULLIF('(2)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +SELECT NULLIF( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer), + ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or +-- more values)" expressions testing + +-- should not be precalculated +SELECT x_stl2_boolean( + (1 === ANY('{2, 3}')) IS DISTINCT FROM + (1 === ALL('{2, 3}')) +) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM + TRUE +) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}') +) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}') +) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE) +) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean( + (1 ==== ANY('{2, 3}')) IS DISTINCT FROM + (1 ==== ALL('{2, 3}')) +) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT x_stl2_boolean( + ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM + TRUE +) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}') +) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}') +) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean( + ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE) +) +FROM generate_series(1, 4) x; + +-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" +-- expressions testing + +-- should not be precalculated +SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}'))) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_vlt, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +-- should not be precalculated +SELECT x_stl2_boolean(NULLIF( + '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer), + TRUE +)) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + ANY('{(3)}'::my_integer[]) +) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + ALL('{(3)}'::my_integer[]) +) +FROM generate_series(1, 4) x; + +-- should not be precalculated +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) IN + ('(3)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}'))) +FROM generate_series(1, 4) x; + +DROP OPERATOR = (my_integer, my_integer); +CREATE OPERATOR = ( + PROCEDURE = equal_my_integer_stl, + LEFTARG = my_integer, + RIGHTARG = my_integer +); + +SELECT x_stl2_boolean(NULLIF( + '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer), + TRUE +)) +FROM generate_series(1, 4) x; + +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + ANY('{(3)}'::my_integer[]) +) +FROM generate_series(1, 4) x; + +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) ==== + ALL('{(3)}'::my_integer[]) +) +FROM generate_series(1, 4) x; + +SELECT ( + NULLIF('(1)'::my_integer, '(2)'::my_integer) IN + ('(3)'::my_integer, '(2)'::my_integer) +) +FROM generate_series(1, 4) x; + +-- Tracking functions testing + +SET track_functions TO 'all'; + +SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated +SELECT x_stl() FROM generate_series(1, 3) x; + +SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated +SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x; + +SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated + +SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x; +SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x; + +SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated +SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated + +SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x; +SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x; + +SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x; +SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x; + +SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated +SELECT 1 ==== 2 FROM generate_series(1, 4) x; +SELECT 1 ===== 2 FROM generate_series(1, 4) x; + +SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated +SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x; +SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x; + +SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated +SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated + +SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x; +SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x; + +SET track_functions TO DEFAULT; + +-- PL/pgSQL Simple expressions +-- Make sure precalculated stable functions can't be simple expressions: these +-- expressions are only initialized once per transaction and then executed +-- multiple times. + +BEGIN; +SELECT simple(); +INSERT INTO two VALUES (3); +SELECT simple(); +ROLLBACK; + +-- Drop tables for testing + +DROP TABLE two; -- 1.9.1
From 2382fa68414f6bbed42ff66c7abbc3c9b200d244 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 15 May 2017 16:05:38 +0300
Subject: [PATCH v4 3/3] Precalculate stable functions, costsNow in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.This patch includes:
- cost changes for cached expressions (according to their behaviour)
---
src/backend/optimizer/path/costsize.c | 89 ++++++++++++++++++++++++++---------
1 file changed, 67 insertions(+), 22 deletions(-)diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 52643d0..505772a 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -140,6 +140,7 @@ static MergeScanSelCache *cached_scansel(PlannerInfo *root, PathKey *pathkey); static void cost_rescan(PlannerInfo *root, Path *path, Cost *rescan_startup_cost, Cost *rescan_total_cost); +static double cost_eval_cacheable_expr_per_tuple(Node *node); static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context); static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *param_info, @@ -3464,6 +3465,59 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root) *cost = context.total; }+/* + * cost_eval_cacheable_expr_per_tuple + * Evaluate per tuple cost for expressions that can be cacheable. + * + * This function was created to not duplicate code for some expression and + * cached some expression. + */ +static double +cost_eval_cacheable_expr_per_tuple(Node *node) +{ + double result; + + /* + * For each operator or function node in the given tree, we charge the + * estimated execution cost given by pg_proc.procost (remember to multiply + * this by cpu_operator_cost). + */ + if (IsA(node, FuncExpr)) + { + result = get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost; + } + else if (IsA(node, OpExpr) || + IsA(node, DistinctExpr) || + IsA(node, NullIfExpr)) + { + OpExpr *opexpr = (OpExpr *) node; + + /* rely on struct equivalence to treat these all alike */ + set_opfuncid(opexpr); + + result = get_func_cost(opexpr->opfuncid) * cpu_operator_cost; + } + else if (IsA(node, ScalarArrayOpExpr)) + { + /* + * Estimate that the operator will be applied to about half of the + * array elements before the answer is determined. + */ + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node; + Node *arraynode = (Node *) lsecond(saop->args); + + set_sa_opfuncid(saop); + result = get_func_cost(saop->opfuncid) * cpu_operator_cost * + estimate_array_length(arraynode) * 0.5; + } + else + { + elog(ERROR, "non cacheable expression node type: %d", (int) nodeTag(node)); + } + + return result; +} + static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) { @@ -3537,32 +3591,23 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) * moreover, since our rowcount estimates for functions tend to be pretty * phony, the results would also be pretty phony. */ - if (IsA(node, FuncExpr)) + if (IsA(node, FuncExpr) || + IsA(node, OpExpr) || + IsA(node, DistinctExpr) || + IsA(node, NullIfExpr) || + IsA(node, ScalarArrayOpExpr)) { - context->total.per_tuple += - get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost; + context->total.per_tuple += cost_eval_cacheable_expr_per_tuple(node); } - else if (IsA(node, OpExpr) || - IsA(node, DistinctExpr) || - IsA(node, NullIfExpr)) - { - /* rely on struct equivalence to treat these all alike */ - set_opfuncid((OpExpr *) node); - context->total.per_tuple += - get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost; - } - else if (IsA(node, ScalarArrayOpExpr)) - { + else if (IsA(node, CachedExpr)) + { /* - * Estimate that the operator will be applied to about half of the - * array elements before the answer is determined. + * Calculate subexpression cost per tuple as usual and add it to startup + * cost (because subexpression will be executed only once for all + * tuples). */ - ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node; - Node *arraynode = (Node *) lsecond(saop->args); - - set_sa_opfuncid(saop); - context->total.per_tuple += get_func_cost(saop->opfuncid) * - cpu_operator_cost * estimate_array_length(arraynode) * 0.5; + context->total.startup += cost_eval_cacheable_expr_per_tuple( + get_subexpr((CachedExpr *) node)); } else if (IsA(node, Aggref) || IsA(node, WindowFunc)) -- 1.9.1
--
Best regards,
Aleksander Alekseev
Hi Marina,
Hello again!
I still don't see anything particularly wrong with your patch. It
applies, passes all test, it is well test-covered and even documented.
Also I've run `make installcheck` under Valgrind and didn't find any
memory-related errors.
Thank you very much as usual!
Is there anything that you would like to change before we call it more
or less final?
I would like to add some primitive nodes for precalculation if their
behaviour allows to do it and their arguments/inputs are constant or
precalculated too; and regression tests for it, of course. Also I would
like to add some notes about precalculation of stable functions in
documentation, for example, here [1]https://www.postgresql.org/docs/10/static/xfunc-volatility.html and here [2]https://www.postgresql.org/docs/10/static/sql-createfunction.html -- Marina Polyakova Postgres Professional: http://www.postgrespro.com The Russian Postgres Company.
Also I would advice to add your branch to our internal buildfarm just
to
make sure everything is OK on exotic platforms like Windows ;)
Thanks! Done)
[1]: https://www.postgresql.org/docs/10/static/xfunc-volatility.html
[2]: https://www.postgresql.org/docs/10/static/sql-createfunction.html -- Marina Polyakova Postgres Professional: http://www.postgrespro.com The Russian Postgres Company
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello, hackers!
Here I have made the 5th version of the patches. I have added the
precalculation of all primitive nodes that don't return set, are not
volatile themselves and their arguments are constant or precalculated
expressions too. There're regression tests for all of them and little
notes in the documentation. Like for the previous patches it seems that
there is no obvious performance degradation too on regular queries
(according to pgbench).
About functionality: precalculation doesn't work for parameters nodes in
plan. So it doesn't work, for example, in the generic plans of prepared
statements. There'll be an another patch for them.
About code:
* Infrastructure patch changes: no enum and union for all 25
precalculated node types. Instead of them there is a new CacheableExpr
node which only contains a NodeTag.
* There're some changes for CoerceToDomain, which constraints now are
checked not only in the executor but in the planner too.
Patches are attached. Any suggestions are welcome!
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v5-0001-Precalculate-stable-functions-infrastructure.patchtext/x-diff; charset=us-ascii; name=v5-0001-Precalculate-stable-functions-infrastructure.patchDownload
From 31b2415d75d263be44a7e3e5e0b67adbbb783312 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Fri, 14 Jul 2017 18:23:17 +0300
Subject: [PATCH v5 1/3] Precalculate stable functions, infrastructure
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- creation of CachedExpr node
- usual node functions for it
- mutator to replace nonovolatile expressions by appropriate cached expressions.
---
src/backend/executor/execExpr.c | 7 +-
src/backend/nodes/copyfuncs.c | 16 +
src/backend/nodes/equalfuncs.c | 11 +
src/backend/nodes/nodeFuncs.c | 76 +++
src/backend/nodes/outfuncs.c | 11 +
src/backend/nodes/readfuncs.c | 15 +
src/backend/optimizer/plan/planner.c | 1114 ++++++++++++++++++++++++++++++++++
src/backend/utils/adt/domains.c | 5 +-
src/backend/utils/cache/typcache.c | 56 +-
src/include/nodes/execnodes.h | 19 +-
src/include/nodes/nodes.h | 3 +
src/include/nodes/primnodes.h | 96 ++-
src/include/utils/typcache.h | 2 +
13 files changed, 1375 insertions(+), 56 deletions(-)
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index a298b92..3523543 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -1964,6 +1964,7 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
break;
}
+ /* note that DomainConstraintExpr nodes are handled within this block */
case T_CoerceToDomain:
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
@@ -2547,6 +2548,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
bool *domainnull = NULL;
Datum *save_innermost_domainval;
bool *save_innermost_domainnull;
+ List *constraints;
ListCell *l;
scratch->d.domaincheck.resulttype = ctest->resulttype;
@@ -2584,15 +2586,16 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
constraint_ref,
CurrentMemoryContext,
false);
+ constraints = GetDomainConstraintExprList(constraint_ref);
/*
* Compile code to check each domain constraint. NOTNULL constraints can
* just be applied on the resv/resnull value, but for CHECK constraints we
* need more pushups.
*/
- foreach(l, constraint_ref->constraints)
+ foreach(l, constraints)
{
- DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+ DomainConstraintExpr *con = (DomainConstraintExpr *) lfirst(l);
scratch->d.domaincheck.constraintname = con->name;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 67ac814..85f57bf 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1411,6 +1411,19 @@ _copyWindowFunc(const WindowFunc *from)
}
/*
+ * _copyCachedExpr
+ */
+static CachedExpr *
+_copyCachedExpr(const CachedExpr *from)
+{
+ CachedExpr *newnode = makeNode(CachedExpr);
+
+ COPY_NODE_FIELD(subexpr);
+
+ return newnode;
+}
+
+/*
* _copyArrayRef
*/
static ArrayRef *
@@ -4850,6 +4863,9 @@ copyObjectImpl(const void *from)
case T_WindowFunc:
retval = _copyWindowFunc(from);
break;
+ case T_CachedExpr:
+ retval = _copyCachedExpr(from);
+ break;
case T_ArrayRef:
retval = _copyArrayRef(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 91d64b7..1ea9af7 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -264,6 +264,14 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b)
}
static bool
+_equalCachedExpr(const CachedExpr *a, const CachedExpr *b)
+{
+ COMPARE_NODE_FIELD(subexpr);
+
+ return true;
+}
+
+static bool
_equalArrayRef(const ArrayRef *a, const ArrayRef *b)
{
COMPARE_SCALAR_FIELD(refarraytype);
@@ -3013,6 +3021,9 @@ equal(const void *a, const void *b)
case T_WindowFunc:
retval = _equalWindowFunc(a, b);
break;
+ case T_CachedExpr:
+ retval = _equalCachedExpr(a, b);
+ break;
case T_ArrayRef:
retval = _equalArrayRef(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 97ba25f..68d480e 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -66,6 +66,10 @@ exprType(const Node *expr)
case T_WindowFunc:
type = ((const WindowFunc *) expr)->wintype;
break;
+ case T_CachedExpr:
+ type =
+ exprType((const Node *) ((const CachedExpr *) expr)->subexpr);
+ break;
case T_ArrayRef:
{
const ArrayRef *arrayref = (const ArrayRef *) expr;
@@ -286,6 +290,9 @@ exprTypmod(const Node *expr)
return ((const Const *) expr)->consttypmod;
case T_Param:
return ((const Param *) expr)->paramtypmod;
+ case T_CachedExpr:
+ return
+ exprTypmod((const Node *) ((const CachedExpr *) expr)->subexpr);
case T_ArrayRef:
/* typmod is the same for array or element */
return ((const ArrayRef *) expr)->reftypmod;
@@ -573,6 +580,11 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
return true;
}
+ if (expr && IsA(expr, CachedExpr))
+ return exprIsLengthCoercion(
+ (const Node *) ((const CachedExpr *) expr)->subexpr,
+ coercedTypmod);
+
return false;
}
@@ -655,6 +667,11 @@ strip_implicit_coercions(Node *node)
if (c->coercionformat == COERCE_IMPLICIT_CAST)
return strip_implicit_coercions((Node *) c->arg);
}
+ else if (IsA(node, CachedExpr))
+ {
+ return strip_implicit_coercions(
+ (Node *) ((CachedExpr *) node)->subexpr);
+ }
return node;
}
@@ -699,6 +716,8 @@ expression_returns_set_walker(Node *node, void *context)
return false;
if (IsA(node, WindowFunc))
return false;
+ if (IsA(node, CachedExpr))
+ return false;
return expression_tree_walker(node, expression_returns_set_walker,
context);
@@ -744,6 +763,10 @@ exprCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->wincollid;
break;
+ case T_CachedExpr:
+ coll = exprCollation(
+ (const Node *) ((const CachedExpr *) expr)->subexpr);
+ break;
case T_ArrayRef:
coll = ((const ArrayRef *) expr)->refcollid;
break;
@@ -933,6 +956,10 @@ exprInputCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->inputcollid;
break;
+ case T_CachedExpr:
+ coll = exprInputCollation(
+ (const Node *) ((const CachedExpr *) expr)->subexpr);
+ break;
case T_FuncExpr:
coll = ((const FuncExpr *) expr)->inputcollid;
break;
@@ -988,6 +1015,10 @@ exprSetCollation(Node *expr, Oid collation)
case T_WindowFunc:
((WindowFunc *) expr)->wincollid = collation;
break;
+ case T_CachedExpr:
+ exprSetCollation((Node *) ((CachedExpr *) expr)->subexpr,
+ collation);
+ break;
case T_ArrayRef:
((ArrayRef *) expr)->refcollid = collation;
break;
@@ -1129,6 +1160,10 @@ exprSetInputCollation(Node *expr, Oid inputcollation)
case T_WindowFunc:
((WindowFunc *) expr)->inputcollid = inputcollation;
break;
+ case T_CachedExpr:
+ exprSetInputCollation((Node *) ((CachedExpr *) expr)->subexpr,
+ inputcollation);
+ break;
case T_FuncExpr:
((FuncExpr *) expr)->inputcollid = inputcollation;
break;
@@ -1217,6 +1252,10 @@ exprLocation(const Node *expr)
/* function name should always be the first thing */
loc = ((const WindowFunc *) expr)->location;
break;
+ case T_CachedExpr:
+ loc = exprLocation(
+ (const Node *) ((const CachedExpr *) expr)->subexpr);
+ break;
case T_ArrayRef:
/* just use array argument's location */
loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
@@ -1590,6 +1629,9 @@ fix_opfuncids_walker(Node *node, void *context)
{
if (node == NULL)
return false;
+ if (IsA(node, CachedExpr))
+ return fix_opfuncids_walker((Node *) ((CachedExpr *) node)->subexpr,
+ context);
if (IsA(node, OpExpr))
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
@@ -1669,6 +1711,9 @@ check_functions_in_node(Node *node, check_function_callback checker,
return true;
}
break;
+ case T_CachedExpr:
+ return check_functions_in_node(
+ (Node *) ((CachedExpr *) node)->subexpr, checker, context);
case T_FuncExpr:
{
FuncExpr *expr = (FuncExpr *) node;
@@ -1919,6 +1964,18 @@ expression_tree_walker(Node *node,
return true;
}
break;
+ case T_CachedExpr:
+ {
+ /*
+ * cachedexpr is processed by walker, so its subexpr is
+ * processed too and we need to process sub-nodes of subexpr.
+ */
+ if (expression_tree_walker(
+ (Node *) ((CachedExpr *) node)->subexpr,
+ walker, context))
+ return true;
+ }
+ break;
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
@@ -2529,6 +2586,25 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_CachedExpr:
+ {
+ CachedExpr *expr = (CachedExpr *) node;
+ CachedExpr *newnode;
+
+ FLATCOPY(newnode, expr, CachedExpr);
+
+ /*
+ * expr is already mutated, so its subexpr is already mutated
+ * too and we need to mutate sub-nodes of subexpr.
+ */
+ newnode->subexpr = (CacheableExpr *) expression_tree_mutator(
+ (Node *) expr->subexpr,
+ mutator,
+ context);
+
+ return (Node *) newnode;
+ }
+ break;
case T_ArrayRef:
{
ArrayRef *arrayref = (ArrayRef *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b0abe9e..3403cbb 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1168,6 +1168,14 @@ _outWindowFunc(StringInfo str, const WindowFunc *node)
}
static void
+_outCachedExpr(StringInfo str, const CachedExpr *node)
+{
+ WRITE_NODE_TYPE("CACHEDEXPR");
+
+ WRITE_NODE_FIELD(subexpr);
+}
+
+static void
_outArrayRef(StringInfo str, const ArrayRef *node)
{
WRITE_NODE_TYPE("ARRAYREF");
@@ -3770,6 +3778,9 @@ outNode(StringInfo str, const void *obj)
case T_WindowFunc:
_outWindowFunc(str, obj);
break;
+ case T_CachedExpr:
+ _outCachedExpr(str, obj);
+ break;
case T_ArrayRef:
_outArrayRef(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 1380703..918c4dd 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -633,6 +633,19 @@ _readWindowFunc(void)
}
/*
+ * _readCachedExpr
+ */
+static CachedExpr *
+_readCachedExpr(void)
+{
+ READ_LOCALS(CachedExpr);
+
+ READ_NODE_FIELD(subexpr);
+
+ READ_DONE();
+}
+
+/*
* _readArrayRef
*/
static ArrayRef *
@@ -2453,6 +2466,8 @@ parseNodeString(void)
return_value = _readGroupingFunc();
else if (MATCH("WINDOWFUNC", 10))
return_value = _readWindowFunc();
+ else if (MATCH("CACHEDEXPR", 10))
+ return_value = _readCachedExpr();
else if (MATCH("ARRAYREF", 8))
return_value = _readArrayRef();
else if (MATCH("FUNCEXPR", 8))
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 2988c11..bce4a41 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -56,6 +56,7 @@
#include "utils/selfuncs.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
/* GUC parameters */
@@ -108,6 +109,18 @@ typedef struct
int *tleref_to_colnum_map;
} grouping_sets_data;
+typedef struct replace_cached_expressions_context
+{
+ PlannerInfo *root;
+
+ /*
+ * Pointers are nulls if there're not any CaseExpr / CoerceToDomain
+ * respectively in parent nodes.
+ */
+ bool *innermost_caseexpr_nonconst_or_noncached_testvalue;
+ bool *innermost_coercetodomain_nonconst_or_noncached_value;
+} replace_cached_expressions_context;
+
/* Local functions */
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
@@ -184,6 +197,8 @@ static PathTarget *make_sort_input_target(PlannerInfo *root,
bool *have_postponed_srfs);
static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel,
List *targets, List *targets_contain_srfs);
+static Node *replace_cached_expressions_mutator(Node *node,
+ replace_cached_expressions_context *context);
/*****************************************************************************
@@ -6086,3 +6101,1102 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
return result;
}
+
+static Node *
+replace_cached_expressions_mutator(Node *node,
+ replace_cached_expressions_context *context)
+{
+ if (node == NULL)
+ return NULL;
+
+ /* mutate certain types of nodes */
+ if (IsA(node, RestrictInfo))
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) node;
+
+ /*
+ * For an OR clause, recurse into the marked-up tree so that we replace
+ * cached expressions for contained RestrictInfos too.
+ */
+ if (rinfo->orclause)
+ rinfo->orclause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->orclause, context);
+ else
+ rinfo->clause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->clause, context);
+
+ /* do NOT recurse into children */
+ return node;
+ }
+ else if (IsA(node, ArrayRef))
+ {
+ /*
+ * ArrayRef is cached if all its subexpressions (refupperindexpr etc.)
+ * are consts or cached expressions too. (it returns array or array
+ * single element so we don't need to check if it returns set; and
+ * knowing its inputs its behaviour is quite defined so we don't need to
+ * check volatility)
+ */
+ ArrayRef *aref = (ArrayRef *) node;
+ ListCell *indexpr;
+ Expr *refexpr;
+ Expr *refassgnexpr;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ aref = (ArrayRef *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ /* check expressions of upper array indexes */
+ foreach(indexpr, aref->refupperindexpr)
+ {
+ void *indexpr_lfirst = lfirst(indexpr);
+ if (indexpr_lfirst != NULL &&
+ !(IsA(indexpr_lfirst, Const) ||
+ IsA(indexpr_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ /* check expressions of lower array indexes */
+ foreach(indexpr, aref->reflowerindexpr)
+ {
+ void *indexpr_lfirst = lfirst(indexpr);
+ if (indexpr_lfirst != NULL &&
+ !(IsA(indexpr_lfirst, Const) ||
+ IsA(indexpr_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ /* check expression of array value */
+ refexpr = aref->refexpr;
+ if (!(IsA(refexpr, Const) || IsA(refexpr, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+
+ /* check expression of source value */
+ refassgnexpr = aref->refassgnexpr;
+ if (refassgnexpr != NULL &&
+ !(IsA(refassgnexpr, Const) || IsA(refassgnexpr, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return ArrayRef, which will not be cached */
+ return (Node *) aref;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) aref;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, FuncExpr))
+ {
+ /*
+ * Function is cached if:
+ * 1) it doesn't return set,
+ * 2) it's not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ FuncExpr *funcexpr;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool func_returns_set;
+
+ /* firstly recurse into children */
+ funcexpr = (FuncExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ func_returns_set = funcexpr->funcretset ||
+ expression_returns_set((Node *) funcexpr->args);
+
+ foreach(arg, funcexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (func_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) funcexpr))
+ {
+ /* return FuncExpr, which will not be cached */
+ return (Node *) funcexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) funcexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, OpExpr))
+ {
+ /*
+ * Operator is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ OpExpr *opexpr = (OpExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ /* firstly recurse into children */
+ opexpr = (OpExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ op_returns_set = opexpr->opretset ||
+ expression_returns_set((Node *) opexpr->args);
+
+ foreach(arg, opexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) opexpr))
+ {
+ /* return OpExpr, which will not be cached */
+ return (Node *) opexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) opexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, DistinctExpr))
+ {
+ /*
+ * Operator of DistinctExpr is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ DistinctExpr *distinctexpr = (DistinctExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid((OpExpr *) distinctexpr);
+
+ /* firstly recurse into children */
+ distinctexpr = (DistinctExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ op_returns_set = distinctexpr->opretset ||
+ expression_returns_set((Node *) distinctexpr->args);
+
+ foreach(arg, distinctexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) distinctexpr))
+ {
+ /* return DistinctExpr, which will not be cached */
+ return (Node *) distinctexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) distinctexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, NullIfExpr))
+ {
+ /*
+ * Operator of NullIfExpr is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ NullIfExpr *nullifexpr = (NullIfExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid((OpExpr *) nullifexpr);
+
+ /* firstly recurse into children */
+ nullifexpr = (NullIfExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ op_returns_set = nullifexpr->opretset ||
+ expression_returns_set((Node *) nullifexpr->args);
+
+ foreach(arg, nullifexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) nullifexpr))
+ {
+ /* return NullIfExpr, which will not be cached */
+ return (Node *) nullifexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) nullifexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ /*
+ * Operator of ScalarArrayOpExpr is cached if:
+ * 1) its function is not volatile itself,
+ * 2) its arguments are constants or cached expressions too.
+ * (it returns boolean so we don't need to check if it returns set)
+ */
+ ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ set_sa_opfuncid(saopexpr);
+
+ /* firstly recurse into children */
+ saopexpr = (ScalarArrayOpExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, saopexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) saopexpr))
+ {
+ /* return ScalarArrayOpExpr, which will not be cached */
+ return (Node *) saopexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) saopexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, BoolExpr))
+ {
+ /*
+ * BoolExpr is cached if its arguments are constants or cached
+ * expressions too. (it returns boolean so we don't need to check if it
+ * returns set; and its too simple for evaluation so we don't need to
+ * check volatility)
+ */
+ BoolExpr *boolexpr = (BoolExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ boolexpr = (BoolExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, boolexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return BoolExpr, which will not be cached */
+ return (Node *) boolexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) boolexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, FieldSelect))
+ {
+ /*
+ * FieldSelect is cached if its argument is const or cached expression
+ * too. (it returns one field from a tuple value so we don't need to
+ * check if it returns set; and knowing its argument its behaviour is
+ * quite defined so we don't need to check volatility)
+ */
+ FieldSelect *fselect = (FieldSelect *) node;
+ Expr *arg;
+
+ /* firstly recurse into children */
+ fselect = (FieldSelect *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ arg = fselect->arg;
+
+ if (!(IsA(arg, Const) || IsA(arg, CachedExpr)))
+ {
+ /* return FieldSelect, which will not be cached */
+ return (Node *) fselect;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) fselect;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, RelabelType))
+ {
+ /*
+ * RelabelType is cached if its argument is const or cached expression
+ * too. (it returns its argument so we don't need to check if it returns
+ * set; and it is a no-op at runtime so we don't need to check
+ * volatility)
+ */
+ RelabelType *relabel = (RelabelType *) node;
+ Expr *arg;
+
+ /* firstly recurse into children */
+ relabel = (RelabelType *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ arg = relabel->arg;
+
+ if (!(IsA(arg, Const) || IsA(arg, CachedExpr)))
+ {
+ /* return RelabelType, which will not be cached */
+ return (Node *) relabel;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) relabel;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CoerceViaIO))
+ {
+ /*
+ * CoerceViaIO is cached if:
+ * 1) its argument is const or cached expression too,
+ * 2) the source type's typoutput function and the destination type's
+ * typinput function are not volatile themselves.
+ * (it returns its argument with a type coercion so we don't need
+ * to check if it returns set)
+ */
+ CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+ Expr *arg;
+
+ /* firstly recurse into children */
+ iocoerce = (CoerceViaIO *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ arg = iocoerce->arg;
+
+ if (!(IsA(arg, Const) || IsA(arg, CachedExpr)) ||
+ contain_volatile_functions((Node *) iocoerce))
+ {
+ /* return CoerceViaIO, which will not be cached */
+ return (Node *) iocoerce;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) iocoerce;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ArrayCoerceExpr))
+ {
+ /*
+ * ArrayCoerceExpr is cached if:
+ * 1) its argument is const or cached expression too,
+ * 2) element-type coercion function (if it is needed) is not volatile
+ * itself.
+ * (it returns its argument with a type coercion so we don't need
+ * to check if it returns set)
+ */
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+ Expr *arg;
+
+ /* firstly recurse into children */
+ acoerce = (ArrayCoerceExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ arg = acoerce->arg;
+
+ if (!(IsA(arg, Const) || IsA(arg, CachedExpr)) ||
+ contain_volatile_functions((Node *) acoerce))
+ {
+ /* return ArrayCoerceExpr, which will not be cached */
+ return (Node *) acoerce;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) acoerce;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ConvertRowtypeExpr))
+ {
+ /*
+ * ConvertRowtypeExpr is cached if its argument is const or cached
+ * expression too. (it returns its argument (row) maybe without values
+ * of some columns so we don't need to check if it returns set; and
+ * knowing its argument its behaviour is quite defined so we don't need
+ * to check volatility)
+ */
+ ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node;
+ Expr *arg;
+
+ /* firstly recurse into children */
+ convexpr = (ConvertRowtypeExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ arg = convexpr->arg;
+
+ if (!(IsA(arg, Const) || IsA(arg, CachedExpr)))
+ {
+ /* return ConvertRowtypeExpr, which will not be cached */
+ return (Node *) convexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) convexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CaseExpr))
+ {
+ /*
+ * CaseExpr is cached if all its arguments (= implicit equality
+ * comparison argument and WHEN clauses) and the default result
+ * expression are consts or cached expressions too. (it returns one of
+ * its arguments or the default result so we don't need to check if it
+ * returns set; and knowing its arguments its behaviour is quite defined
+ * so we don't need to check volatility)
+ */
+ CaseExpr *caseexpr = (CaseExpr *) node;
+ CaseExpr *new_caseexpr;
+ Expr *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool nonconst_or_noncached_testvalue;
+ replace_cached_expressions_context new_context;
+ ListCell *cell;
+ Expr *defresult;
+
+ /*
+ * Recurse into node manually because we will need different context for
+ * its subnodes.
+ */
+ new_caseexpr = (CaseExpr *) palloc(sizeof(CaseExpr));
+ memcpy((new_caseexpr), (caseexpr), sizeof(CaseExpr));
+
+ /* recurse into arg */
+ new_caseexpr->arg = (Expr *) replace_cached_expressions_mutator(
+ (Node *) new_caseexpr->arg,
+ context);
+ arg = new_caseexpr->arg;
+
+ /* check arg and fill new context */
+ nonconst_or_noncached_testvalue =
+ (arg != NULL && !(IsA(arg, Const) || IsA(arg, CachedExpr)));
+ has_nonconst_or_noncached_input = nonconst_or_noncached_testvalue;
+
+ new_context.root = context->root;
+ new_context.innermost_caseexpr_nonconst_or_noncached_testvalue =
+ &nonconst_or_noncached_testvalue;
+ new_context.innermost_coercetodomain_nonconst_or_noncached_value =
+ context->innermost_coercetodomain_nonconst_or_noncached_value;
+
+ /*
+ * Recurse into args with new context (it will be used by CaseTestExpr
+ * if it's used in current WHEN clauses subtrees).
+ */
+ new_caseexpr->args = (List *) expression_tree_mutator(
+ (Node *) new_caseexpr->args,
+ replace_cached_expressions_mutator,
+ (void *) &new_context);
+
+ /* check args */
+ foreach(cell, new_caseexpr->args)
+ {
+ CaseWhen *casewhen = lfirst(cell);
+ Expr *expr = casewhen->expr;
+ Expr *result = casewhen->result;
+
+ if (!(IsA(expr, Const) || IsA(expr, CachedExpr)) ||
+ !(IsA(result, Const) || IsA(result, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ /* recurse into defresult */
+ new_caseexpr->defresult = (Expr *) replace_cached_expressions_mutator(
+ (Node *) new_caseexpr->defresult,
+ context);
+
+ /* check defresult */
+ defresult = new_caseexpr->defresult;
+ if (!(IsA(defresult, Const) || IsA(defresult, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return CaseExpr, which will not be cached */
+ return (Node *) new_caseexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) new_caseexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CaseTestExpr))
+ {
+ /*
+ * CaseTestExpr is cached if we got in context that it is in CaseExpr
+ * and arg of innermost CaseExpr is const or cached expression too. (it
+ * is a placeholder node for the test value of its CaseExpr so we don't
+ * need to check if it returns set and we don't need to check
+ * volatility)
+ */
+ CaseTestExpr *casetest = (CaseTestExpr *) node;
+
+ if (!context->innermost_caseexpr_nonconst_or_noncached_testvalue ||
+ *(context->innermost_caseexpr_nonconst_or_noncached_testvalue))
+ {
+ /* return CaseTestExpr, which will not be cached */
+ return (Node *) casetest;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) casetest;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ArrayExpr))
+ {
+ /*
+ * ArrayExpr is cached if its elements are consts or cached expressions
+ * too. (it returns array so we don't need to check if it returns set;
+ * and knowing its elements its behaviour is quite defined so we don't
+ * need to check volatility)
+ */
+ ArrayExpr *arrayexpr = (ArrayExpr *) node;
+ ListCell *element;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ arrayexpr = (ArrayExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(element, arrayexpr->elements)
+ {
+ void *element_lfirst = lfirst(element);
+ if (!(IsA(element_lfirst, Const) ||
+ IsA(element_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return ArrayExpr, which will not be cached */
+ return (Node *) arrayexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) arrayexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, RowExpr))
+ {
+ /*
+ * RowExpr is cached if its arguments are consts or cached expressions
+ * too. (it returns tuple so we don't need to check if it returns set;
+ * and knowing its arguments its behaviour is quite defined so we don't
+ * need to check volatility)
+ */
+ RowExpr *rowexpr = (RowExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ rowexpr = (RowExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, rowexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return RowExpr, which will not be cached */
+ return (Node *) rowexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) rowexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, RowCompareExpr))
+ {
+ /*
+ * RowCompareExpr is cached if:
+ * 1) its pairwise comparison operators are not volatile themselves,
+ * 2) its arguments are consts or cached expressions too.
+ * (it returns boolean so we don't need to check if it returns set)
+ */
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ rcexpr = (RowCompareExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, rcexpr->largs)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ foreach(arg, rcexpr->rargs)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) rcexpr))
+ {
+ /* return RowCompareExpr, which will not be cached */
+ return (Node *) rcexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) rcexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CoalesceExpr))
+ {
+ /*
+ * CoalesceExpr is cached if its arguments are consts or cached
+ * expressions too. (it returns one of its arguments so we don't need to
+ * check if it returns set; and knowing its arguments its behaviour is
+ * quite defined so we don't need to check volatility)
+ */
+ CoalesceExpr *coalesce = (CoalesceExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ coalesce = (CoalesceExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, coalesce->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return CoalesceExpr, which will not be cached */
+ return (Node *) coalesce;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) coalesce;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, MinMaxExpr))
+ {
+ /*
+ * MinMaxExpr is cached if its arguments are consts or cached
+ * expressions too. (it returns one of its arguments so we don't need to
+ * check if it returns set; and it uses btree comparison functions so we
+ * don't need to check volatility)
+ */
+ MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ minmaxexpr = (MinMaxExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, minmaxexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return MinMaxExpr, which will not be cached */
+ return (Node *) minmaxexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) minmaxexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, SQLValueFunction))
+ {
+ /*
+ * SQLValueFunction is cached if its type is:
+ * 1) SVFOP_CURRENT_ROLE or
+ * 2) SVFOP_CURRENT_USER or
+ * 3) SVFOP_USER or
+ * 4) SVFOP_SESSION_USER or
+ * 5) SVFOP_CURRENT_CATALOG or
+ * 6) SVFOP_CURRENT_SCHEMA.
+ * (all these functions don't return set and are stable)
+ */
+ SQLValueFunction *svf = (SQLValueFunction *) node;
+ SQLValueFunctionOp op = svf->op;
+
+ if (!(op == SVFOP_CURRENT_ROLE ||
+ op == SVFOP_CURRENT_USER ||
+ op == SVFOP_USER ||
+ op == SVFOP_SESSION_USER ||
+ op == SVFOP_CURRENT_CATALOG ||
+ op == SVFOP_CURRENT_SCHEMA))
+ {
+ /* return SQLValueFunction, which will not be cached */
+ return (Node *) svf;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) svf;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, XmlExpr))
+ {
+ /*
+ * XmlExpr is cached if all its arguments are consts or cached
+ * expressions too. (it returns values of different types so we don't
+ * need to check if it returns set; and knowing its arguments its
+ * behaviour is quite defined so we don't need to check volatility)
+ */
+ XmlExpr *xexpr = (XmlExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ xexpr = (XmlExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, xexpr->named_args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ foreach(arg, xexpr->args)
+ {
+ void *arg_lfirst = lfirst(arg);
+ if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return XmlExpr, which will not be cached */
+ return (Node *) xexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) xexpr;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, NullTest))
+ {
+ /*
+ * NullTest is cached if its argument is const or cached expression too.
+ * (it returns boolean so we don't need to check if it returns set; and
+ * knowing its argument its behaviour is quite defined so we don't need
+ * to check volatility)
+ */
+ NullTest *nulltest = (NullTest *) node;
+ Expr *arg;
+
+ /* firstly recurse into children */
+ nulltest = (NullTest *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ arg = nulltest->arg;
+
+ if (!(IsA(arg, Const) || IsA(arg, CachedExpr)))
+ {
+ /* return NullTest, which will not be cached */
+ return (Node *) nulltest;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) nulltest;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, BooleanTest))
+ {
+ /*
+ * BooleanTest is cached if its argument is const or cached expression
+ * too. (it returns boolean so we don't need to check if it returns set;
+ * and knowing its argument its behaviour is quite defined so we don't
+ * need to check volatility)
+ */
+ BooleanTest *btest = (BooleanTest *) node;
+ Expr *arg;
+
+ /* firstly recurse into children */
+ btest = (BooleanTest *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ arg = btest->arg;
+
+ if (!(IsA(arg, Const) || IsA(arg, CachedExpr)))
+ {
+ /* return BooleanTest, which will not be cached */
+ return (Node *) btest;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) btest;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CoerceToDomain))
+ {
+ /*
+ * CoerceToDomain is cached if:
+ * 1) its constraints can be cached,
+ * 2) its argument is const or cached expression too.
+ * (it returns its argument coercing a value to a domain type so we
+ * don't need to check if it returns set)
+ */
+ CoerceToDomain *ctest = (CoerceToDomain *) node;
+ Expr *arg = ctest->arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool nonconst_or_noncached_value;
+ replace_cached_expressions_context new_context;
+ DomainConstraintRef *constraint_ref;
+ List *constraints;
+ ListCell *cell;
+
+ /* firstly recurse into arg */
+ arg = (Expr *) replace_cached_expressions_mutator((Node *) arg,
+ context);
+
+ /* check arg and fill new context */
+ nonconst_or_noncached_value =
+ !(IsA(arg, Const) || IsA(arg, CachedExpr));
+ has_nonconst_or_noncached_input = nonconst_or_noncached_value;
+
+ new_context.root = context->root;
+ new_context.innermost_caseexpr_nonconst_or_noncached_testvalue =
+ context->innermost_caseexpr_nonconst_or_noncached_testvalue;
+ new_context.innermost_coercetodomain_nonconst_or_noncached_value =
+ &nonconst_or_noncached_value;
+
+ /* get constraints and recurse into them with new context */
+ constraint_ref = (DomainConstraintRef *)
+ palloc(sizeof(DomainConstraintRef));
+ InitDomainConstraintRef(ctest->resulttype,
+ constraint_ref,
+ context->root->planner_cxt,
+ false);
+ constraints = GetDomainConstraintExprList(constraint_ref);
+ foreach(cell, constraints)
+ {
+ DomainConstraintExpr *con = (DomainConstraintExpr *) lfirst(cell);
+ Expr *check_expr = con->check_expr;
+
+ switch (con->constrainttype)
+ {
+ case DOM_CONSTRAINT_NOTNULL:
+ /* OK */
+ break;
+ case DOM_CONSTRAINT_CHECK:
+ check_expr = (Expr *) replace_cached_expressions_mutator(
+ (Node *) check_expr,
+ &new_context);
+ if (!(IsA(check_expr, Const) ||
+ IsA(check_expr, CachedExpr)))
+ has_nonconst_or_noncached_input = true;
+ break;
+ default:
+ elog(ERROR, "unrecognized constraint type: %d",
+ (int) con->constrainttype);
+ break;
+ }
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return RowCompareExpr, which will not be cached */
+ return (Node *) ctest;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) ctest;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CoerceToDomainValue))
+ {
+ /*
+ * CoerceToDomainValue is cached if we got in context that it is in
+ * CoerceToDomain expression and arg of innermost CoerceToDomain
+ * expression is const or cached expression too. (it is a placeholder
+ * node for the value of its CoerceToDomain expression so we don't need
+ * to check if it returns set and we don't need to check volatility)
+ */
+ CoerceToDomainValue *domval = (CoerceToDomainValue *) node;
+
+ if (!context->innermost_coercetodomain_nonconst_or_noncached_value ||
+ *(context->innermost_coercetodomain_nonconst_or_noncached_value))
+ {
+ /* return CoerceToDomainValue, which will not be cached */
+ return (Node *) domval;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) domval;
+
+ return (Node *) new_node;
+ }
+ }
+
+ /* otherwise recurse into children */
+ return expression_tree_mutator(node, replace_cached_expressions_mutator,
+ (void *) context);
+}
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
index e61d91b..7028cce 100644
--- a/src/backend/utils/adt/domains.c
+++ b/src/backend/utils/adt/domains.c
@@ -137,7 +137,8 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
foreach(l, my_extra->constraint_ref.constraints)
{
- DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+ DomainConstraintState *con_state = (DomainConstraintState *) lfirst(l);
+ DomainConstraintExpr *con = con_state->expr;
switch (con->constrainttype)
{
@@ -177,7 +178,7 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
my_extra->constraint_ref.tcache->typlen);
econtext->domainValue_isNull = isnull;
- if (!ExecCheck(con->check_exprstate, econtext))
+ if (!ExecCheck(con_state->check_exprstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("value for domain %s violates check constraint \"%s\"",
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 7ec31eb..6f12cc3 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -737,7 +737,8 @@ load_domaintype_info(TypeCacheEntry *typentry)
bool isNull;
char *constring;
Expr *check_expr;
- DomainConstraintState *r;
+ DomainConstraintState *r_state;
+ DomainConstraintExpr *r;
/* Ignore non-CHECK constraints (presently, shouldn't be any) */
if (c->contype != CONSTRAINT_CHECK)
@@ -776,11 +777,14 @@ load_domaintype_info(TypeCacheEntry *typentry)
/* ExecInitExpr will assume we've planned the expression */
check_expr = expression_planner(check_expr);
- r = makeNode(DomainConstraintState);
+ r_state = makeNode(DomainConstraintState);
+ r_state->expr = makeNode(DomainConstraintExpr);
+ r = r_state->expr;
+
r->constrainttype = DOM_CONSTRAINT_CHECK;
r->name = pstrdup(NameStr(c->conname));
r->check_expr = check_expr;
- r->check_exprstate = NULL;
+ r_state->check_exprstate = NULL;
MemoryContextSwitchTo(oldcxt);
@@ -797,7 +801,7 @@ load_domaintype_info(TypeCacheEntry *typentry)
ccons = (DomainConstraintState **)
repalloc(ccons, cconslen * sizeof(DomainConstraintState *));
}
- ccons[nccons++] = r;
+ ccons[nccons++] = r_state;
}
systable_endscan(scan);
@@ -834,7 +838,8 @@ load_domaintype_info(TypeCacheEntry *typentry)
*/
if (notNull)
{
- DomainConstraintState *r;
+ DomainConstraintState *r_state;
+ DomainConstraintExpr *r;
/* Create the DomainConstraintCache object and context if needed */
if (dcc == NULL)
@@ -854,15 +859,17 @@ load_domaintype_info(TypeCacheEntry *typentry)
/* Create node trees in DomainConstraintCache's context */
oldcxt = MemoryContextSwitchTo(dcc->dccContext);
- r = makeNode(DomainConstraintState);
+ r_state = makeNode(DomainConstraintState);
+ r_state->expr = makeNode(DomainConstraintExpr);
+ r = r_state->expr;
r->constrainttype = DOM_CONSTRAINT_NOTNULL;
r->name = pstrdup("NOT NULL");
r->check_expr = NULL;
- r->check_exprstate = NULL;
+ r_state->check_exprstate = NULL;
/* lcons to apply the nullness check FIRST */
- dcc->constraints = lcons(r, dcc->constraints);
+ dcc->constraints = lcons(r_state, dcc->constraints);
MemoryContextSwitchTo(oldcxt);
}
@@ -891,7 +898,7 @@ dcs_cmp(const void *a, const void *b)
const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
- return strcmp((*ca)->name, (*cb)->name);
+ return strcmp((*ca)->expr->name, (*cb)->expr->name);
}
/*
@@ -941,16 +948,21 @@ prep_domain_constraints(List *constraints, MemoryContext execctx)
foreach(lc, constraints)
{
- DomainConstraintState *r = (DomainConstraintState *) lfirst(lc);
- DomainConstraintState *newr;
+ DomainConstraintState *r_state = (DomainConstraintState *) lfirst(lc);
+ DomainConstraintExpr *r = r_state->expr;
+ DomainConstraintState *newr_state;
+ DomainConstraintExpr *newr;
+
+ newr_state = makeNode(DomainConstraintState);
+ newr_state->expr = makeNode(DomainConstraintExpr);
+ newr = newr_state->expr;
- newr = makeNode(DomainConstraintState);
newr->constrainttype = r->constrainttype;
newr->name = r->name;
newr->check_expr = r->check_expr;
- newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
+ newr_state->check_exprstate = ExecInitExpr(r->check_expr, NULL);
- result = lappend(result, newr);
+ result = lappend(result, newr_state);
}
MemoryContextSwitchTo(oldcxt);
@@ -1069,6 +1081,22 @@ DomainHasConstraints(Oid type_id)
return (typentry->domainData != NULL);
}
+/*
+ * Return list of DomainConstraintExpr of DomainConstraintState elements of the
+ * given list.
+ */
+List *
+GetDomainConstraintExprList(DomainConstraintRef *ref)
+{
+ List *result = NIL;
+ ListCell *lc;
+
+ foreach(lc, ref->constraints)
+ result = lappend(result, ((DomainConstraintState *) lfirst(lc))->expr);
+
+ return result;
+}
+
/*
* array_element_has_equality and friends are helper routines to check
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 85fac8a..5053abb 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -787,25 +787,14 @@ typedef struct AlternativeSubPlanState
int active; /* list index of the one we're using */
} AlternativeSubPlanState;
-/*
- * DomainConstraintState - one item to check during CoerceToDomain
- *
- * Note: we consider this to be part of an ExprState tree, so we give it
- * a name following the xxxState convention. But there's no directly
- * associated plan-tree node.
+/* ----------------
+ * DomainConstraintState node
+ * ----------------
*/
-typedef enum DomainConstraintType
-{
- DOM_CONSTRAINT_NOTNULL,
- DOM_CONSTRAINT_CHECK
-} DomainConstraintType;
-
typedef struct DomainConstraintState
{
NodeTag type;
- DomainConstraintType constrainttype; /* constraint type */
- char *name; /* name of constraint (for error msgs) */
- Expr *check_expr; /* for CHECK, a boolean expression */
+ DomainConstraintExpr *expr; /* expression plan node */
ExprState *check_exprstate; /* check_expr's eval state, or NULL */
} DomainConstraintState;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 0152739..a95b133 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -149,6 +149,8 @@ typedef enum NodeTag
T_Aggref,
T_GroupingFunc,
T_WindowFunc,
+ T_CacheableExpr,
+ T_CachedExpr,
T_ArrayRef,
T_FuncExpr,
T_NamedArgExpr,
@@ -180,6 +182,7 @@ typedef enum NodeTag
T_NullTest,
T_BooleanTest,
T_CoerceToDomain,
+ T_DomainConstraintExpr,
T_CoerceToDomainValue,
T_SetToDefault,
T_CurrentOfExpr,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 38015ed..495c0a5 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -364,6 +364,38 @@ typedef struct WindowFunc
int location; /* token location, or -1 if unknown */
} WindowFunc;
+/*
+ * CacheableExpr - generic suberclass for expressions that can be cacheable.
+ *
+ * All expression node types that can be cacheable should derive from
+ * CacheableExpr (that is, have CacheableExpr as their first field). Since
+ * CacheableExpr only contains NodeTag, this is a formality, but it is an easy
+ * form of documentation.
+ *
+ * Expression is cached (= is are calculated once for all output rows, but as
+ * many times as expression is mentioned in query), if:
+ * - it doesn't return a set
+ * - it is not volatile itself
+ * - its arguments are constants or recursively precalculated expressions.
+ *
+ * In planner if expression can be cached it becomes a part of CachedExpr node.
+ */
+typedef struct CacheableExpr
+{
+ NodeTag type;
+} CacheableExpr;
+
+/*
+ * CachedExpr - expression node for cached expressions (= they are calculated
+ * once for all output rows, but as many times as function is mentioned in
+ * query).
+ */
+typedef struct CachedExpr
+{
+ Expr xpr;
+ CacheableExpr *subexpr; /* expression to be cached */
+} CachedExpr;
+
/* ----------------
* ArrayRef: describes an array subscripting operation
*
@@ -395,7 +427,7 @@ typedef struct WindowFunc
*/
typedef struct ArrayRef
{
- Expr xpr;
+ CacheableExpr xpr;
Oid refarraytype; /* type of the array proper */
Oid refelemtype; /* type of the array elements */
int32 reftypmod; /* typmod of the array (and elements too) */
@@ -445,7 +477,7 @@ typedef enum CoercionForm
*/
typedef struct FuncExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid funcid; /* PG_PROC OID of the function */
Oid funcresulttype; /* PG_TYPE OID of result value */
bool funcretset; /* true if function returns set */
@@ -492,7 +524,7 @@ typedef struct NamedArgExpr
*/
typedef struct OpExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid opno; /* PG_OPERATOR OID of the operator */
Oid opfuncid; /* PG_PROC OID of underlying function */
Oid opresulttype; /* PG_TYPE OID of result value */
@@ -535,7 +567,7 @@ typedef OpExpr NullIfExpr;
*/
typedef struct ScalarArrayOpExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid opno; /* PG_OPERATOR OID of the operator */
Oid opfuncid; /* PG_PROC OID of underlying function */
bool useOr; /* true for ANY, false for ALL */
@@ -558,7 +590,7 @@ typedef enum BoolExprType
typedef struct BoolExpr
{
- Expr xpr;
+ CacheableExpr xpr;
BoolExprType boolop;
List *args; /* arguments to this expression */
int location; /* token location, or -1 if unknown */
@@ -738,7 +770,7 @@ typedef struct AlternativeSubPlan
typedef struct FieldSelect
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
AttrNumber fieldnum; /* attribute number of field to extract */
Oid resulttype; /* type of the field (result type of this
@@ -787,7 +819,7 @@ typedef struct FieldStore
typedef struct RelabelType
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion expression */
int32 resulttypmod; /* output typmod (usually -1) */
@@ -807,7 +839,7 @@ typedef struct RelabelType
typedef struct CoerceViaIO
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion */
/* output typmod is not stored, but is presumed -1 */
@@ -830,7 +862,7 @@ typedef struct CoerceViaIO
typedef struct ArrayCoerceExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression (yields an array) */
Oid elemfuncid; /* OID of element coercion function, or 0 */
Oid resulttype; /* output type of coercion (an array type) */
@@ -855,7 +887,7 @@ typedef struct ArrayCoerceExpr
typedef struct ConvertRowtypeExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type (always a composite type) */
/* Like RowExpr, we deliberately omit a typmod and collation here */
@@ -902,7 +934,7 @@ typedef struct CollateExpr
*/
typedef struct CaseExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid casetype; /* type of expression result */
Oid casecollid; /* OID of collation, or InvalidOid if none */
Expr *arg; /* implicit equality comparison argument */
@@ -932,7 +964,7 @@ typedef struct CaseWhen
*/
typedef struct CaseTestExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid typeId; /* type for substituted value */
int32 typeMod; /* typemod for substituted value */
Oid collation; /* collation for the substituted value */
@@ -948,7 +980,7 @@ typedef struct CaseTestExpr
*/
typedef struct ArrayExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid array_typeid; /* type of expression result */
Oid array_collid; /* OID of collation, or InvalidOid if none */
Oid element_typeid; /* common type of array elements */
@@ -982,7 +1014,7 @@ typedef struct ArrayExpr
*/
typedef struct RowExpr
{
- Expr xpr;
+ CacheableExpr xpr;
List *args; /* the fields */
Oid row_typeid; /* RECORDOID or a composite type's ID */
@@ -1027,7 +1059,7 @@ typedef enum RowCompareType
typedef struct RowCompareExpr
{
- Expr xpr;
+ CacheableExpr xpr;
RowCompareType rctype; /* LT LE GE or GT, never EQ or NE */
List *opnos; /* OID list of pairwise comparison ops */
List *opfamilies; /* OID list of containing operator families */
@@ -1041,7 +1073,7 @@ typedef struct RowCompareExpr
*/
typedef struct CoalesceExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid coalescetype; /* type of expression result */
Oid coalescecollid; /* OID of collation, or InvalidOid if none */
List *args; /* the arguments */
@@ -1059,7 +1091,7 @@ typedef enum MinMaxOp
typedef struct MinMaxExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid minmaxtype; /* common type of arguments and result */
Oid minmaxcollid; /* OID of collation of result */
Oid inputcollid; /* OID of collation that function should use */
@@ -1100,7 +1132,7 @@ typedef enum SQLValueFunctionOp
typedef struct SQLValueFunction
{
- Expr xpr;
+ CacheableExpr xpr;
SQLValueFunctionOp op; /* which function this is */
Oid type; /* result type/typmod */
int32 typmod;
@@ -1138,7 +1170,7 @@ typedef enum
typedef struct XmlExpr
{
- Expr xpr;
+ CacheableExpr xpr;
XmlExprOp op; /* xml function ID */
char *name; /* name in xml(NAME foo ...) syntaxes */
List *named_args; /* non-XML expressions for xml_attributes */
@@ -1176,7 +1208,7 @@ typedef enum NullTestType
typedef struct NullTest
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
NullTestType nulltesttype; /* IS NULL, IS NOT NULL */
bool argisrow; /* T to perform field-by-field null checks */
@@ -1199,7 +1231,7 @@ typedef enum BoolTestType
typedef struct BooleanTest
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
BoolTestType booltesttype; /* test type */
int location; /* token location, or -1 if unknown */
@@ -1216,7 +1248,7 @@ typedef struct BooleanTest
*/
typedef struct CoerceToDomain
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* domain type ID (result type) */
int32 resulttypmod; /* output typmod (currently always -1) */
@@ -1226,6 +1258,24 @@ typedef struct CoerceToDomain
} CoerceToDomain;
/*
+ * DomainConstraintExpr - one item to check during CoerceToDomain
+ */
+
+typedef enum DomainConstraintType
+{
+ DOM_CONSTRAINT_NOTNULL,
+ DOM_CONSTRAINT_CHECK
+} DomainConstraintType;
+
+typedef struct DomainConstraintExpr
+{
+ Expr xpr;
+ DomainConstraintType constrainttype; /* constraint type */
+ char *name; /* name of constraint (for error msgs) */
+ Expr *check_expr; /* for CHECK, a boolean expression */
+} DomainConstraintExpr;
+
+/*
* Placeholder node for the value to be processed by a domain's check
* constraint. This is effectively like a Param, but can be implemented more
* simply since we need only one replacement value at a time.
@@ -1236,7 +1286,7 @@ typedef struct CoerceToDomain
*/
typedef struct CoerceToDomainValue
{
- Expr xpr;
+ CacheableExpr xpr;
Oid typeId; /* type for substituted value */
int32 typeMod; /* typemod for substituted value */
Oid collation; /* collation for the substituted value */
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index c12631d..d8d842e 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -149,6 +149,8 @@ extern void UpdateDomainConstraintRef(DomainConstraintRef *ref);
extern bool DomainHasConstraints(Oid type_id);
+extern List * GetDomainConstraintExprList(DomainConstraintRef *ref);
+
extern TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod);
extern TupleDesc lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod,
--
1.9.1
v5-0002-Precalculate-stable-functions-planning-and-execut.patchtext/x-diff; charset=us-ascii; name=v5-0002-Precalculate-stable-functions-planning-and-execut.patchDownload
From 6cf52e350abf475901e52c3436f015e5020d17c9 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 17 Jul 2017 11:08:42 +0300
Subject: [PATCH v5 2/3] Precalculate stable functions, planning and execution
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- replacement nonvolatile expressions by appropriate cached expressions
- planning and execution cached expressions
- regression tests
- changed documentation
---
doc/src/sgml/ref/create_function.sgml | 14 +
doc/src/sgml/xfunc.sgml | 14 +-
src/backend/executor/execExpr.c | 32 +
src/backend/executor/execExprInterp.c | 52 +
src/backend/optimizer/path/allpaths.c | 9 +-
src/backend/optimizer/path/clausesel.c | 12 +
src/backend/optimizer/plan/planagg.c | 1 +
src/backend/optimizer/plan/planner.c | 40 +
src/backend/optimizer/util/clauses.c | 26 +
src/backend/utils/adt/ruleutils.c | 5 +
src/include/executor/execExpr.h | 31 +
src/include/optimizer/planner.h | 5 +
src/include/optimizer/tlist.h | 8 +-
src/pl/plpgsql/src/pl_exec.c | 10 +
.../expected/precalculate_stable_functions.out | 6306 ++++++++++++++++++++
src/test/regress/serial_schedule | 1 +
.../regress/sql/precalculate_stable_functions.sql | 1843 ++++++
17 files changed, 8405 insertions(+), 4 deletions(-)
create mode 100644 src/test/regress/expected/precalculate_stable_functions.out
create mode 100644 src/test/regress/sql/precalculate_stable_functions.sql
diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml
index 072e033..dfdc3b5 100644
--- a/doc/src/sgml/ref/create_function.sgml
+++ b/doc/src/sgml/ref/create_function.sgml
@@ -337,6 +337,20 @@ CREATE [ OR REPLACE ] FUNCTION
<literal>setval()</>.
</para>
+ <note>
+ <para>
+ Stable, immutable functions and other nonovolatile expressions are
+ precalculated (= calculated once for all output rows, but as many times
+ as expression is mentioned in query), if they don't return a set and
+ their arguments are constants or recursively precalculated expressions.
+ </para>
+
+ <para>
+ Now this feature is not supported for the generic plans of prepared
+ statements (see <xref linkend="SQL-PREPARE">).
+ </para>
+ </note>
+
<para>
For additional details see <xref linkend="xfunc-volatility">.
</para>
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index cd6dd84..f97b637 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1467,9 +1467,21 @@ CREATE FUNCTION test(int, int) RETURNS int
<para>
For best optimization results, you should label your functions with the
- strictest volatility category that is valid for them.
+ strictest volatility category that is valid for them. Stable, immutable
+ functions and other nonovolatile expressions are precalculated (= calculated
+ once for all output rows, but as many times as expression is mentioned in
+ query), if they don't return a set and their arguments are constants or
+ recursively precalculated expressions.
</para>
+ <note>
+ <para>
+ Now nonvolatile expressions with constant/precalculated arguments are not
+ precalculated in the generic plans of prepared statements (see <xref
+ linkend="SQL-PREPARE">).
+ </para>
+ </note>
+
<para>
Any function with side-effects <emphasis>must</> be labeled
<literal>VOLATILE</>, so that calls to it cannot be optimized away.
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 3523543..247b43f 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -791,6 +791,38 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
break;
}
+ case T_CachedExpr:
+ {
+ /*
+ * Allocate CachedExprState used by all steps of CachedExpr
+ * evaluation.
+ */
+ scratch.d.cachedexpr.state = (CachedExprState *) palloc(
+ sizeof(CachedExprState));
+ scratch.d.cachedexpr.state->isExecuted = false;
+ scratch.d.cachedexpr.state->resnull = false;
+ scratch.d.cachedexpr.state->resvalue = (Datum) 0;
+ scratch.d.cachedexpr.state->restypid = exprType(
+ (const Node *) node);
+
+ /* add EEOP_CACHEDEXPR_IF_CACHED step */
+ scratch.opcode = EEOP_CACHEDEXPR_IF_CACHED;
+ ExprEvalPushStep(state, &scratch);
+
+ /* add subexpression steps */
+ ExecInitExprRec((Expr *) ((CachedExpr *) node)->subexpr, parent,
+ state, resv, resnull);
+
+ /* add EEOP_CACHEDEXPR_SUBEXPR_END step */
+ scratch.opcode = EEOP_CACHEDEXPR_SUBEXPR_END;
+ ExprEvalPushStep(state, &scratch);
+
+ /* adjust jump target */
+ scratch.d.cachedexpr.state->jumpdone = state->steps_len;
+
+ break;
+ }
+
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c227d9b..1a810e9 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -70,6 +70,7 @@
#include "pgstat.h"
#include "utils/builtins.h"
#include "utils/date.h"
+#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -363,6 +364,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_WINDOW_FUNC,
&&CASE_EEOP_SUBPLAN,
&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
+ &&CASE_EEOP_CACHEDEXPR_IF_CACHED,
+ &&CASE_EEOP_CACHEDEXPR_SUBEXPR_END,
&&CASE_EEOP_LAST
};
@@ -1500,6 +1503,55 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_CACHEDEXPR_IF_CACHED)
+ {
+ if (op->d.cachedexpr.state->isExecuted)
+ {
+ /* use saved result and skip subexpression evaluation */
+ *op->resnull = op->d.cachedexpr.state->resnull;
+ if (!(*op->resnull))
+ *op->resvalue = op->d.cachedexpr.state->resvalue;
+
+ EEO_JUMP(op->d.cachedexpr.state->jumpdone);
+ }
+
+ /* we are ready for subexpression evaluation */
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHEDEXPR_SUBEXPR_END)
+ {
+ int16 restyplen;
+ bool restypbyval;
+ MemoryContext oldContext;
+
+ /* save result */
+ op->d.cachedexpr.state->resnull = *op->resnull;
+ if (!(*op->resnull))
+ {
+ get_typlenbyval(op->d.cachedexpr.state->restypid, &restyplen,
+ &restypbyval);
+
+ /*
+ * Switch per-query memory context. It is necessary to save the
+ * subexpression result between all tuples if its value datum is
+ * a pointer.
+ */
+ oldContext = MemoryContextSwitchTo(
+ econtext->ecxt_per_query_memory);
+
+ op->d.cachedexpr.state->resvalue = datumCopy(*op->resvalue,
+ restypbyval,
+ restyplen);
+
+ /* switch memory context back */
+ MemoryContextSwitchTo(oldContext);
+ }
+ op->d.cachedexpr.state->isExecuted = true;
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_SUBPLAN)
{
/* too complex for an inline implementation */
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index f087ddb..341b519 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -378,7 +378,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
set_subquery_pathlist(root, rel, rti, rte);
break;
case RTE_FUNCTION:
- set_function_size_estimates(root, rel);
+ {
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo, root);
+ set_function_size_estimates(root, rel);
+ }
break;
case RTE_TABLEFUNC:
set_tablefunc_size_estimates(root, rel);
@@ -517,6 +521,9 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
*/
check_index_predicates(root, rel);
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo, root);
+
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
}
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 9d34025..a504336 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -825,6 +825,18 @@ clause_selectivity(PlannerInfo *root,
jointype,
sjinfo);
}
+ else if (IsA(clause, CachedExpr))
+ {
+ /*
+ * Not sure this case is needed, but it can't hurt.
+ * Calculate selectivity of subexpression.
+ */
+ s1 = clause_selectivity(root,
+ (Node *) ((CachedExpr *) clause)->subexpr,
+ varRelid,
+ jointype,
+ sjinfo);
+ }
else
{
/*
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index bba8a1f..a8b7991 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -38,6 +38,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index bce4a41..0490d56 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6102,6 +6102,46 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
return result;
}
+/*
+ * replace_pathtarget_cached_expressions
+ * Replace cached expresisons in a PathTarget tlist.
+ *
+ * As a notational convenience, returns the same PathTarget pointer passed in.
+ */
+PathTarget *
+replace_pathtarget_cached_expressions(PathTarget *target, PlannerInfo *root)
+{
+ replace_cached_expressions_context context;
+
+ context.root = root;
+ context.innermost_caseexpr_nonconst_or_noncached_testvalue = NULL;
+ context.innermost_coercetodomain_nonconst_or_noncached_value = NULL;
+
+ target->exprs = (List *) replace_cached_expressions_mutator(
+ (Node *) target->exprs, &context);
+
+ return target;
+}
+
+/*
+ * replace_qual_cached_expressions
+ * Replace cacehd expressions in a WHERE clause. The input can be either an
+ * implicitly-ANDed list of boolean expressions, or a list of RestrictInfo
+ * nodes.
+ */
+List *
+replace_qual_cached_expressions(List *quals, PlannerInfo *root)
+{
+ replace_cached_expressions_context context;
+
+ context.root = root;
+ context.innermost_caseexpr_nonconst_or_noncached_testvalue = NULL;
+ context.innermost_coercetodomain_nonconst_or_noncached_value = NULL;
+
+ return (List *) replace_cached_expressions_mutator((Node *) quals,
+ &context);
+}
+
static Node *
replace_cached_expressions_mutator(Node *node,
replace_cached_expressions_context *context)
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8961ed8..7b72335 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2553,6 +2553,32 @@ eval_const_expressions_mutator(Node *node,
return (Node *) newexpr;
}
+ case T_CachedExpr:
+ {
+ CachedExpr *cachedexpr = (CachedExpr *) node;
+ CacheableExpr *new_subexpr = (CacheableExpr *)
+ eval_const_expressions_mutator((Node *) cachedexpr->subexpr,
+ context);
+ CachedExpr *new_cachedexpr;
+
+ if (IsA(new_subexpr, Const))
+ {
+ /* successfully simplified it */
+ return (Node *) new_subexpr;
+ }
+ else
+ {
+ /*
+ * The expression cannot be simplified any further, so build
+ * and return a replacement CachedExpr node using the
+ * possibly-simplified arguments of subexpression.
+ */
+ new_cachedexpr = makeNode(CachedExpr);
+ new_cachedexpr->subexpr = new_subexpr;
+
+ return (Node *) new_cachedexpr;
+ }
+ }
case T_FuncExpr:
{
FuncExpr *expr = (FuncExpr *) node;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5cfb916..80dd539 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7629,6 +7629,11 @@ get_rule_expr(Node *node, deparse_context *context,
get_windowfunc_expr((WindowFunc *) node, context);
break;
+ case T_CachedExpr:
+ get_rule_expr((Node *) ((CachedExpr *) node)->subexpr, context,
+ showimplicit);
+ break;
+
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 7a65339..da0c74e 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -212,6 +212,16 @@ typedef enum ExprEvalOp
EEOP_SUBPLAN,
EEOP_ALTERNATIVE_SUBPLAN,
+ /*
+ * Evaluate CachedExpr. EEOP_CACHEDEXPR_IF_CACHED is used before
+ * subexpression evaluation (if subexpression was evaluated use cached value
+ * and jump to next state or get prepared to subexpression evaluation
+ * otherwise). EEOP_CACHEDEXPR_SUBEXPR_END is used after subexpression
+ * evaluation for caching its result.
+ */
+ EEOP_CACHEDEXPR_IF_CACHED,
+ EEOP_CACHEDEXPR_SUBEXPR_END,
+
/* non-existent operation, used e.g. to check array lengths */
EEOP_LAST
} ExprEvalOp;
@@ -560,6 +570,13 @@ typedef struct ExprEvalStep
/* out-of-line state, created by nodeSubplan.c */
AlternativeSubPlanState *asstate;
} alternative_subplan;
+
+ /* for EEOP_CACHEDEXPR_* */
+ struct
+ {
+ /* steps for evaluation the same CachedExpr have the same state */
+ struct CachedExprState *state;
+ } cachedexpr;
} d;
} ExprEvalStep;
@@ -600,6 +617,20 @@ typedef struct ArrayRefState
} ArrayRefState;
+/*
+ * Non-inline data for EEOP_CACHEDEXPR_* operations (steps for evaluation the
+ * same CachedExpr have the same state).
+ */
+typedef struct CachedExprState
+{
+ bool isExecuted;
+ bool resnull;
+ Datum resvalue;
+ Oid restypid; /* for copying resvalue of subexpression */
+ int jumpdone; /* jump here if result determined */
+} CachedExprState;
+
+
extern void ExecReadyInterpretedExpr(ExprState *state);
extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index 2a4cf71..1a64696 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -59,4 +59,9 @@ extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);
extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti);
+extern PathTarget *replace_pathtarget_cached_expressions(PathTarget *target,
+ PlannerInfo *root);
+
+extern List *replace_qual_cached_expressions(List *quals, PlannerInfo *root);
+
#endif /* PLANNER_H */
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index 0d3ec92..1b4fd8f 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -65,8 +65,12 @@ extern void split_pathtarget_at_srfs(PlannerInfo *root,
PathTarget *target, PathTarget *input_target,
List **targets, List **targets_contain_srfs);
-/* Convenience macro to get a PathTarget with valid cost/width fields */
+/*
+ * Convenience macro to get a PathTarget with valid cost/width fields and
+ * cached expressions.
+ */
#define create_pathtarget(root, tlist) \
- set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist))
+ set_pathtarget_cost_width(root, replace_pathtarget_cached_expressions( \
+ make_pathtarget_from_tlist(tlist), root))
#endif /* TLIST_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index c98492b..16199f1 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -6471,6 +6471,16 @@ exec_simple_check_node(Node *node)
case T_Param:
return TRUE;
+ case T_CachedExpr:
+ {
+ /*
+ * If CachedExpr will not be initialized by ExecInitCachedExpr
+ * possibly it will use cached value when it shouldn't (for
+ * example, snapshot has changed), so return false.
+ */
+ return FALSE;
+ }
+
case T_ArrayRef:
{
ArrayRef *expr = (ArrayRef *) node;
diff --git a/src/test/regress/expected/precalculate_stable_functions.out b/src/test/regress/expected/precalculate_stable_functions.out
new file mode 100644
index 0000000..fd187f5
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions.out
@@ -0,0 +1,6306 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create types and tables for testing
+CREATE TYPE my_integer AS (value integer);
+CREATE TYPE composite_type AS (first integer, second integer[], third boolean);
+CREATE TABLE x (x integer);
+INSERT INTO x SELECT generate_series(1, 4) x;
+CREATE TABLE wxyz (w integer, x integer[], y boolean, z integer);
+CREATE TABLE wxyz_child () INHERITS (wxyz);
+CREATE TABLE wxyz_child2 (a integer, b integer) INHERITS (wxyz);
+CREATE TABLE no_columns ();
+CREATE TABLE no_columns_child () INHERITS (no_columns);
+CREATE TABLE no_columns_child2 (a integer, b integer) INHERITS (no_columns);
+-- Create volatile functions for testing
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_array_integer (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_boolean (
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v boolean';
+ RETURN TRUE;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz (
+)
+RETURNS wxyz VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child (
+)
+RETURNS wxyz_child VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child2 (
+)
+RETURNS wxyz_child2 VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_oid (
+)
+RETURNS oid VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_my_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_instruction_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_xml (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_xml_content (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt2 (
+ integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_vlt (
+ integer
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer volatile';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_vlt (
+ my_integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer volatile';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create stable functions for testing
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_array_integer (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz (
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child (
+)
+RETURNS wxyz_child STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child2 (
+)
+RETURNS wxyz_child2 STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_oid (
+)
+RETURNS oid STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_my_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_instruction_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_xml (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_xml_content (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_integer (
+ integer[]
+)
+RETURNS integer[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_oid (
+ oid[]
+)
+RETURNS oid[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_oid';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_wxyz (
+ wxyz
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 wxyz';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_composite_type (
+ composite_type
+)
+RETURNS composite_type STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 composite_type';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer (
+ my_integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_no_columns (
+ no_columns
+)
+RETURNS no_columns STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 no_columns';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_name (
+ name
+)
+RETURNS name STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 name';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_xml (
+ xml
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 xml';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_text (
+ text
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 text';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_stl (
+ integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer stable';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_stl (
+ my_integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer stable';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(x) from x);
+END
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+-- Create immutable functions for testing
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_my_integer (
+ my_integer
+)
+RETURNS my_integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_imm (
+ boolean,
+ boolean
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create operators for testing
+CREATE OPERATOR === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_booleans_imm,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+-- Create domains for testing
+CREATE DOMAIN my_integer_no_check AS integer;
+CREATE DOMAIN my_integer_not_null AS integer;
+CREATE DOMAIN my_integer_vlt_check AS integer CHECK (VALUE === 1);
+CREATE DOMAIN my_integer_stl_check AS integer CHECK (VALUE ==== 1);
+CREATE DOMAIN my_integer_imm_check AS integer CHECK (VALUE ===== 1);
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_no_check (
+ my_integer_no_check
+)
+RETURNS my_integer_no_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_no_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_not_null (
+ my_integer_not_null
+)
+RETURNS my_integer_not_null STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_not_null';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_vlt_check (
+ my_integer_vlt_check
+)
+RETURNS my_integer_vlt_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_vlt_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_stl_check (
+ my_integer_stl_check
+)
+RETURNS my_integer_stl_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_stl_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_imm_check (
+ my_integer_imm_check
+)
+RETURNS my_integer_imm_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_imm_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Functions testing
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl() FROM x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- JOIN/ON clause testing
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+NOTICE: s
+NOTICE: s
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+ x_vlt2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(1) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+NOTICE: s2 boolean
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+NOTICE: s2 boolean
+NOTICE: equal booleans immutable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl() ==== 1 FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- IS (NOT) DISTINCT FROM expression testing
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x; -- should not be precalculated
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x; -- should not be precalculated
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- IS (NOT) DISTINCT FROM expressions with null arguments testing
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM 1) FROM x;
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM 1) FROM x;
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL)) FROM x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM x_stl2(NULL)) FROM x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+NOTICE: equal my_integer immutable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- NULLIF expressions with null arguments testing
+SELECT x_stl2(NULLIF(1, NULL)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(NULLIF(NULL::integer, NULL)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+SELECT 1 === ANY ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 === ALL ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY ('{2, 3}') FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL ('{2, 3}') FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== ANY ('{2, 3}') FROM x;
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== ALL ('{2, 3}') FROM x;
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+SELECT 1 ==== ANY ('{2, NULL}') FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY ('{2, 3}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY ('{2, NULL}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::integer ==== ANY (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT 1 ==== ALL ('{2, NULL}') FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL ('{2, 3}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL ('{2, NULL}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::integer ==== ALL (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Boolean expressions testing
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- ARRAY[] expressions testing
+SELECT x_stl2_array_integer(ARRAY[x_vlt(), 2]) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+SELECT x_stl2_array_integer(ARRAY[x_stl(), 2]) FROM x;
+NOTICE: s
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+-- Multidimensional ARRAY[] expressions testing
+SELECT x_stl2_array_integer(ARRAY[[x_vlt(), 2], [3, 4]]) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+(4 rows)
+
+SELECT x_stl2_array_integer(ARRAY[[x_stl(), 2], [3, 4]]) FROM x;
+NOTICE: s
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+(4 rows)
+
+-- Array subscripting operations testing
+SELECT x_stl2(('{1, 2}'::integer[])[1]) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_array_integer(('{1, 2}'::integer[])[:]) FROM x;
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+-- Mixed functions and array subscripting operations testing
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+NOTICE: s array_integer
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- FieldSelect expressions testing
+SELECT x_stl2(('(1, {2}, TRUE, 3)'::wxyz).w) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(('(1)'::my_integer).value) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+NOTICE: s wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+NOTICE: s my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- ROW() expressions testing
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE, 3)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_composite_type((1, '{2}', TRUE)) FROM x;
+NOTICE: s2 composite_type
+ x_stl2_composite_type
+-----------------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_composite_type(ROW(1, '{2}', TRUE)) FROM x;
+NOTICE: s2 composite_type
+ x_stl2_composite_type
+-----------------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_composite_type((1, '{2}', TRUE)::composite_type) FROM x;
+NOTICE: s2 composite_type
+ x_stl2_composite_type
+-----------------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+-- Mixed functions and ROW() expressions testing
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- RelabelType expressions testing
+SELECT x_stl2(x_vlt_oid()::integer) FROM x; -- should not be precalculated
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+NOTICE: s oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- CoerceViaIO expressions testing
+SELECT x_stl2_my_integer('(1)'::text::my_integer) FROM x;
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x; -- should not be precalculated
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+NOTICE: s text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and CoerceViaIO expressions testing
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x; -- should not be precalculated
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+NOTICE: s text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- ArrayCoerce expressions testing
+-- Binary-coercible types:
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_vlt;
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x; -- should not be precalculated
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+ my_integer
+------------
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+(4 rows)
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x;
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ my_integer
+------------
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+(4 rows)
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x; -- should not be precalculated
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer stable
+NOTICE: cast my_integer as integer stable
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+SELECT x_vlt_array_integer()::my_integer[] FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_vlt_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+NOTICE: s array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_stl_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+-- ConvertRowtypeExpr testing
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3)'::wxyz_child::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_no_columns('()'::no_columns_child::no_columns) FROM x;
+NOTICE: s2 no_columns
+ x_stl2_no_columns
+-------------------
+ ()
+ ()
+ ()
+ ()
+(4 rows)
+
+SELECT x_stl2_no_columns('(1, 2)'::no_columns_child2::no_columns) FROM x;
+NOTICE: s2 no_columns
+ x_stl2_no_columns
+-------------------
+ ()
+ ()
+ ()
+ ()
+(4 rows)
+
+-- Mixed functions and ConvertRowtypeExpr testing
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x; -- should not be precalculated
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x; -- should not be precalculated
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: s wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: s wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- CASE expressions testing
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+NOTICE: s2 boolean
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- RowCompareExpr testing
+SELECT x_stl2_boolean((1, 2) < (1, 3)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and RowCompareExpr testing
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- COALESCE expressions testing
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x; -- should not be precalculated
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, 1, 3)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, 1, 3)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- SQLValueFunction testing
+CREATE ROLE regress_testrol2 SUPERUSER;
+CREATE ROLE regress_testrol1 SUPERUSER LOGIN IN ROLE regress_testrol2;
+\c -
+SET SESSION AUTHORIZATION regress_testrol1;
+SET ROLE regress_testrol2;
+SELECT x_stl2_name(current_role) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+(4 rows)
+
+SELECT x_stl2_name(current_user) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+(4 rows)
+
+SELECT x_stl2_name(user) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+(4 rows)
+
+SELECT x_stl2_name(session_user) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol1
+ regress_testrol1
+ regress_testrol1
+ regress_testrol1
+(4 rows)
+
+SELECT x_stl2_name(current_catalog) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+-------------
+ regression
+ regression
+ regression
+ regression
+(4 rows)
+
+SELECT x_stl2_name(current_schema) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+-------------
+ public
+ public
+ public
+ public
+(4 rows)
+
+\c
+DROP ROLE regress_testrol1, regress_testrol2;
+-- Xml expressions testing
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', '<bar>foo</bar>')) FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), 'cont', 'ent')
+)
+FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------
+ <foo bar="bar">content</foo>
+ <foo bar="bar">content</foo>
+ <foo bar="bar">content</foo>
+ <foo bar="bar">content</foo>
+(4 rows)
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, 123 AS bar)) FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------
+ <foo>abc</foo><bar>123</bar>
+ <foo>abc</foo><bar>123</bar>
+ <foo>abc</foo><bar>123</bar>
+ <foo>abc</foo><bar>123</bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>'
+))
+FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT 'abc<foo>bar</foo><bar>foo</bar>')) FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPI(name php, 'echo "hello world";')) FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+SELECT x_stl2_xml(XMLROOT(
+ '<?xml version="1.0"?><content>abc</content>',
+ version '1.0',
+ standalone yes
+))
+FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+--------------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><content>abc</content>
+ <?xml version="1.0" standalone="yes"?><content>abc</content>
+ <?xml version="1.0" standalone="yes"?><content>abc</content>
+ <?xml version="1.0" standalone="yes"?><content>abc</content>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>' AS text
+))
+FROM x;
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------------------------------
+ <?xml version="1.0"?><book><title>Manual</title></book>
+ <?xml version="1.0"?><book><title>Manual</title></book>
+ <?xml version="1.0"?><book><title>Manual</title></book>
+ <?xml version="1.0"?><book><title>Manual</title></book>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(
+ CONTENT 'abc<foo>bar</foo><bar>foo</bar>' AS text
+))
+FROM x;
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_boolean('abc<foo>bar</foo><bar>foo</bar>' IS DOCUMENT) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+-------------------------------------
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------------------
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+ x_stl2_text
+----------------
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+-------------------------------------
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+(4 rows)
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------------------
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+NOTICE: s text xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+NOTICE: s xml content
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+NOTICE: s text xml instruction content
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+NOTICE: s xml
+NOTICE: s2 text
+ x_stl2_text
+----------------
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+NOTICE: s xml content
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+NOTICE: s xml content
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- NullTest expressions testing
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- BooleanTest expressions testing
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- CoerceToDomain expressions testing
+SELECT x_stl2_my_integer_no_check(1::my_integer_no_check) FROM x;
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(1::my_integer_not_null) FROM x;
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_vlt_check(1::my_integer_vlt_check) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+ x_stl2_my_integer_vlt_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(1::my_integer_stl_check) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(1::my_integer_imm_check) FROM x;
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and CoerceToDomain expressions testing
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Tracking functions testing
+SET track_functions TO 'all';
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl() FROM x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- JOIN/ON clause testing
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+NOTICE: s
+NOTICE: s
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+ x_vlt2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(1) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+NOTICE: s2 boolean
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+NOTICE: s2 boolean
+NOTICE: equal booleans immutable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl() ==== 1 FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and boolean expressions testing
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and ARRAY[] expressions testing
+SELECT x_stl2_array_integer(ARRAY[x_vlt()]) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1}
+ {1}
+ {1}
+ {1}
+(4 rows)
+
+SELECT x_stl2_array_integer(ARRAY[x_stl()]) FROM x;
+NOTICE: s
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1}
+ {1}
+ {1}
+ {1}
+(4 rows)
+
+-- Mixed functions and array subscripting operations testing
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+NOTICE: s array_integer
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+NOTICE: s wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+NOTICE: s my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and ROW() expressions testing
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- Mixed functions and RelabelType expressions testing
+SELECT x_stl2(x_vlt_oid()::integer) FROM x; -- should not be precalculated
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+NOTICE: s oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and CoerceViaIO expressions testing
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x; -- should not be precalculated
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+NOTICE: s text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x; -- should not be precalculated
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+NOTICE: s text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Binary-coercible types:
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- Not binary-coercible types:
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x; -- should not be precalculated
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer stable
+NOTICE: cast my_integer as integer stable
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+SELECT x_vlt_array_integer()::my_integer[] FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_vlt_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+NOTICE: s array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_stl_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+-- Mixed functions and ConvertRowtypeExpr testing
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x; -- should not be precalculated
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x; -- should not be precalculated
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: s wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: s wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- Mixed functions and CASE expressions testing
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+NOTICE: s2 boolean
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and RowCompareExpr testing
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and COALESCE expressions testing
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x; -- should not be precalculated
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- Mixed functions and GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+-------------------------------------
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------------------
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+ x_stl2_text
+----------------
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+-------------------------------------
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+(4 rows)
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------------------
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+NOTICE: s text xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+NOTICE: s xml content
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+NOTICE: s text xml instruction content
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+NOTICE: s xml
+NOTICE: s2 text
+ x_stl2_text
+----------------
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+NOTICE: s xml content
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+NOTICE: s xml content
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and NullTest expressions testing
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and BooleanTest expressions testing
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x; -- should not be precalculated
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and CoerceToDomain expressions testing
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SET track_functions TO DEFAULT;
+-- ROW() expressions with dropped columns testing
+ALTER TABLE wxyz DROP COLUMN z;
+VACUUM FULL;
+-- ROW() expressions testing
+SELECT x_stl2_wxyz((1, '{2}', TRUE)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_wxyz((1, '{2}', TRUE)::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+-- Mixed functions and ROW() expressions testing
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE)) FROM x;
+NOTICE: s
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+BEGIN;
+SELECT simple();
+ simple
+--------
+ 4
+(1 row)
+
+INSERT INTO x VALUES (5);
+SELECT simple();
+ simple
+--------
+ 5
+(1 row)
+
+ROLLBACK;
+-- Drop tables and domains for testing
+DROP TABLE x;
+DROP FUNCTION x_vlt_wxyz, x_vlt_wxyz_child, x_vlt_wxyz_child2;
+DROP FUNCTION x_stl_wxyz, x_stl_wxyz_child, x_stl_wxyz_child2, x_stl2_wxyz;
+DROP TABLE wxyz, wxyz_child, wxyz_child2;
+DROP FUNCTION x_stl2_no_columns;
+DROP TABLE no_columns, no_columns_child, no_columns_child2;
+DROP FUNCTION x_stl2_my_integer_no_check;
+DROP DOMAIN my_integer_no_check;
+DROP FUNCTION x_stl2_my_integer_not_null;
+DROP DOMAIN my_integer_not_null;
+DROP FUNCTION x_stl2_my_integer_vlt_check;
+DROP DOMAIN my_integer_vlt_check;
+DROP FUNCTION x_stl2_my_integer_stl_check;
+DROP DOMAIN my_integer_stl_check;
+DROP FUNCTION x_stl2_my_integer_imm_check;
+DROP DOMAIN my_integer_imm_check;
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 5e8b7e9..f73a64f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -180,3 +180,4 @@ test: with
test: xml
test: event_trigger
test: stats
+test: precalculate_stable_functions
diff --git a/src/test/regress/sql/precalculate_stable_functions.sql b/src/test/regress/sql/precalculate_stable_functions.sql
new file mode 100644
index 0000000..43444b8
--- /dev/null
+++ b/src/test/regress/sql/precalculate_stable_functions.sql
@@ -0,0 +1,1843 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create types and tables for testing
+
+CREATE TYPE my_integer AS (value integer);
+CREATE TYPE composite_type AS (first integer, second integer[], third boolean);
+
+CREATE TABLE x (x integer);
+INSERT INTO x SELECT generate_series(1, 4) x;
+
+CREATE TABLE wxyz (w integer, x integer[], y boolean, z integer);
+CREATE TABLE wxyz_child () INHERITS (wxyz);
+CREATE TABLE wxyz_child2 (a integer, b integer) INHERITS (wxyz);
+
+CREATE TABLE no_columns ();
+CREATE TABLE no_columns_child () INHERITS (no_columns);
+CREATE TABLE no_columns_child2 (a integer, b integer) INHERITS (no_columns);
+
+-- Create volatile functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_array_integer (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_boolean (
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v boolean';
+ RETURN TRUE;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz (
+)
+RETURNS wxyz VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child (
+)
+RETURNS wxyz_child VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child2 (
+)
+RETURNS wxyz_child2 VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_oid (
+)
+RETURNS oid VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_my_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_instruction_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_xml (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_xml_content (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt2 (
+ integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_vlt (
+ integer
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer volatile';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_vlt (
+ my_integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer volatile';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create stable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_array_integer (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz (
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child (
+)
+RETURNS wxyz_child STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child2 (
+)
+RETURNS wxyz_child2 STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_oid (
+)
+RETURNS oid STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_my_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_instruction_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_xml (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_xml_content (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_array_integer (
+ integer[]
+)
+RETURNS integer[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_array_oid (
+ oid[]
+)
+RETURNS oid[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_oid';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_wxyz (
+ wxyz
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 wxyz';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_composite_type (
+ composite_type
+)
+RETURNS composite_type STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 composite_type';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer (
+ my_integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_no_columns (
+ no_columns
+)
+RETURNS no_columns STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 no_columns';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_name (
+ name
+)
+RETURNS name STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 name';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_xml (
+ xml
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 xml';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_text (
+ text
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 text';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_stl (
+ integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer stable';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_stl (
+ my_integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer stable';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(x) from x);
+END
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create immutable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_imm2_my_integer (
+ my_integer
+)
+RETURNS my_integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_booleans_imm (
+ boolean,
+ boolean
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create operators for testing
+
+CREATE OPERATOR === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_booleans_imm,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+
+-- Create domains for testing
+
+CREATE DOMAIN my_integer_no_check AS integer;
+CREATE DOMAIN my_integer_not_null AS integer;
+CREATE DOMAIN my_integer_vlt_check AS integer CHECK (VALUE === 1);
+CREATE DOMAIN my_integer_stl_check AS integer CHECK (VALUE ==== 1);
+CREATE DOMAIN my_integer_imm_check AS integer CHECK (VALUE ===== 1);
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_no_check (
+ my_integer_no_check
+)
+RETURNS my_integer_no_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_no_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_not_null (
+ my_integer_not_null
+)
+RETURNS my_integer_not_null STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_not_null';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_vlt_check (
+ my_integer_vlt_check
+)
+RETURNS my_integer_vlt_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_vlt_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_stl_check (
+ my_integer_stl_check
+)
+RETURNS my_integer_stl_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_stl_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_imm_check (
+ my_integer_imm_check
+)
+RETURNS my_integer_imm_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_imm_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Functions testing
+
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+SELECT x_stl() FROM x;
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM x WHERE x_stl() < x;
+
+-- JOIN/ON clause testing
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x; -- should not be precalculated
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+SELECT x_stl2(1) FROM x;
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+
+SELECT x_stl2(x_stl()) FROM x;
+SELECT x_imm2(x_stl()) FROM x;
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+
+-- Operators testing
+
+SELECT 1 === 2 FROM x; -- should not be precalculated
+SELECT 1 ==== 2 FROM x;
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+SELECT x_stl() ==== 1 FROM x;
+
+-- IS (NOT) DISTINCT FROM expression testing
+
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x; -- should not be precalculated
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x;
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x;
+
+-- IS (NOT) DISTINCT FROM expressions with null arguments testing
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM 1) FROM x;
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM 1) FROM x;
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL)) FROM x;
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM x_stl2(NULL)) FROM x;
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x; -- should not be precalculated
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x; -- should not be precalculated
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+
+-- NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+
+-- NULLIF expressions with null arguments testing
+SELECT x_stl2(NULLIF(1, NULL)) FROM x;
+SELECT x_stl2(NULLIF(NULL::integer, NULL)) FROM x;
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x; -- should not be precalculated
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+
+SELECT 1 === ANY ('{2, 3}') FROM x; -- should not be precalculated
+SELECT 1 === ALL ('{2, 3}') FROM x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x; -- should not be precalculated
+
+SELECT 1 ==== ANY ('{2, 3}') FROM x;
+SELECT 1 ==== ALL ('{2, 3}') FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+SELECT 1 ===== ANY ('{2, 3}') FROM x;
+SELECT 1 ===== ALL ('{2, 3}') FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+SELECT 1 ==== ANY ('{2, NULL}') FROM x;
+SELECT x_stl2_boolean(1 ==== ANY (NULL)) FROM x;
+SELECT NULL ==== ANY ('{2, 3}'::integer[]) FROM x;
+SELECT NULL ==== ANY ('{2, NULL}'::integer[]) FROM x;
+SELECT x_stl2_boolean(NULL::integer ==== ANY (NULL)) FROM x;
+
+SELECT 1 ==== ALL ('{2, NULL}') FROM x;
+SELECT x_stl2_boolean(1 ==== ALL (NULL)) FROM x;
+SELECT NULL ==== ALL ('{2, 3}'::integer[]) FROM x;
+SELECT NULL ==== ALL ('{2, NULL}'::integer[]) FROM x;
+SELECT x_stl2_boolean(NULL::integer ==== ALL (NULL)) FROM x;
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM x;
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM x;
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM x;
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x; -- should not be precalculated
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x; -- should not be precalculated
+
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x; -- should not be precalculated
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x; -- should not be precalculated
+
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x; -- should not be precalculated
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+-- Boolean expressions testing
+
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x; -- should not be precalculated
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+
+-- ARRAY[] expressions testing
+
+SELECT x_stl2_array_integer(ARRAY[x_vlt(), 2]) FROM x; -- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[x_stl(), 2]) FROM x;
+
+-- Multidimensional ARRAY[] expressions testing
+
+SELECT x_stl2_array_integer(ARRAY[[x_vlt(), 2], [3, 4]]) FROM x; -- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[[x_stl(), 2], [3, 4]]) FROM x;
+
+-- Array subscripting operations testing
+
+SELECT x_stl2(('{1, 2}'::integer[])[1]) FROM x;
+SELECT x_stl2_array_integer(('{1, 2}'::integer[])[:]) FROM x;
+
+-- Mixed functions and array subscripting operations testing
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x; -- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x; -- should not be precalculated
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x; -- should not be precalculated
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x; -- should not be precalculated
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+
+-- FieldSelect expressions testing
+
+SELECT x_stl2(('(1, {2}, TRUE, 3)'::wxyz).w) FROM x;
+SELECT x_stl2(('(1)'::my_integer).value) FROM x;
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+
+-- ROW() expressions testing
+
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)) FROM x;
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE, 3)) FROM x;
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)::wxyz) FROM x;
+
+SELECT x_stl2_composite_type((1, '{2}', TRUE)) FROM x;
+SELECT x_stl2_composite_type(ROW(1, '{2}', TRUE)) FROM x;
+SELECT x_stl2_composite_type((1, '{2}', TRUE)::composite_type) FROM x;
+
+-- Mixed functions and ROW() expressions testing
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x; -- should not be precalculated
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+
+-- RelabelType expressions testing
+
+SELECT x_stl2(x_vlt_oid()::integer) FROM x; -- should not be precalculated
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+
+-- CoerceViaIO expressions testing
+
+SELECT x_stl2_my_integer('(1)'::text::my_integer) FROM x;
+
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x; -- should not be precalculated
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+
+-- Mixed functions and CoerceViaIO expressions testing
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x; -- should not be precalculated
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+
+-- ArrayCoerce expressions testing
+
+-- Binary-coercible types:
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x; -- should not be precalculated
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_vlt;
+
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x; -- should not be precalculated
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x;
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x; -- should not be precalculated
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+
+SELECT x_vlt_array_integer()::my_integer[] FROM x; -- should not be precalculated
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+
+-- ConvertRowtypeExpr testing
+
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3)'::wxyz_child::wxyz) FROM x;
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2::wxyz) FROM x;
+
+SELECT x_stl2_no_columns('()'::no_columns_child::no_columns) FROM x;
+SELECT x_stl2_no_columns('(1, 2)'::no_columns_child2::no_columns) FROM x;
+
+-- Mixed functions and ConvertRowtypeExpr testing
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x; -- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x; -- should not be precalculated
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+
+-- CASE expressions testing
+
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x; -- should not be precalculated
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x; -- should not be precalculated
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+
+-- RowCompareExpr testing
+
+SELECT x_stl2_boolean((1, 2) < (1, 3)) FROM x;
+
+-- Mixed functions and RowCompareExpr testing
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+
+-- COALESCE expressions testing
+
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x; -- should not be precalculated
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+
+-- GREATEST and LEAST functions testing
+
+SELECT x_stl2(GREATEST(2, 1, 3)) FROM x;
+SELECT x_stl2(LEAST(2, 1, 3)) FROM x;
+
+-- Mixed functions and GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+
+-- SQLValueFunction testing
+
+CREATE ROLE regress_testrol2 SUPERUSER;
+CREATE ROLE regress_testrol1 SUPERUSER LOGIN IN ROLE regress_testrol2;
+
+\c -
+SET SESSION AUTHORIZATION regress_testrol1;
+SET ROLE regress_testrol2;
+
+SELECT x_stl2_name(current_role) FROM x;
+SELECT x_stl2_name(current_user) FROM x;
+SELECT x_stl2_name(user) FROM x;
+SELECT x_stl2_name(session_user) FROM x;
+SELECT x_stl2_name(current_catalog) FROM x;
+SELECT x_stl2_name(current_schema) FROM x;
+
+\c
+DROP ROLE regress_testrol1, regress_testrol2;
+
+-- Xml expressions testing
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', '<bar>foo</bar>')) FROM x;
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), 'cont', 'ent')
+)
+FROM x;
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, 123 AS bar)) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>'
+))
+FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT 'abc<foo>bar</foo><bar>foo</bar>')) FROM x;
+
+SELECT x_stl2_xml(XMLPI(name php, 'echo "hello world";')) FROM x;
+
+SELECT x_stl2_xml(XMLROOT(
+ '<?xml version="1.0"?><content>abc</content>',
+ version '1.0',
+ standalone yes
+))
+FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>' AS text
+))
+FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(
+ CONTENT 'abc<foo>bar</foo><bar>foo</bar>' AS text
+))
+FROM x;
+
+SELECT x_stl2_boolean('abc<foo>bar</foo><bar>foo</bar>' IS DOCUMENT) FROM x;
+
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+
+-- NullTest expressions testing
+
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x; -- should not be precalculated
+
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x; -- should not be precalculated
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+
+-- BooleanTest expressions testing
+
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x; -- should not be precalculated
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+
+-- CoerceToDomain expressions testing
+
+SELECT x_stl2_my_integer_no_check(1::my_integer_no_check) FROM x;
+SELECT x_stl2_my_integer_not_null(1::my_integer_not_null) FROM x;
+
+SELECT x_stl2_my_integer_vlt_check(1::my_integer_vlt_check) FROM x; -- should not be precalculated
+SELECT x_stl2_my_integer_stl_check(1::my_integer_stl_check) FROM x;
+SELECT x_stl2_my_integer_imm_check(1::my_integer_imm_check) FROM x;
+
+-- Mixed functions and CoerceToDomain expressions testing
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x; -- should not be precalculated
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x; -- should not be precalculated
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x; -- should not be precalculated
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x; -- should not be precalculated
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+
+-- Tracking functions testing
+
+SET track_functions TO 'all';
+
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+SELECT x_stl() FROM x;
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM x WHERE x_stl() < x;
+
+-- JOIN/ON clause testing
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x; -- should not be precalculated
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+SELECT x_stl2(1) FROM x;
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+
+SELECT x_stl2(x_stl()) FROM x;
+SELECT x_imm2(x_stl()) FROM x;
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+
+-- Operators testing
+SELECT 1 === 2 FROM x; -- should not be precalculated
+SELECT 1 ==== 2 FROM x;
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+SELECT x_stl() ==== 1 FROM x;
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x; -- should not be precalculated
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x; -- should not be precalculated
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x; -- should not be precalculated
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x; -- should not be precalculated
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x; -- should not be precalculated
+
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x; -- should not be precalculated
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x; -- should not be precalculated
+
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x; -- should not be precalculated
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+-- Mixed functions and boolean expressions testing
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x; -- should not be precalculated
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+
+-- Mixed functions and ARRAY[] expressions testing
+SELECT x_stl2_array_integer(ARRAY[x_vlt()]) FROM x; -- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[x_stl()]) FROM x;
+
+-- Mixed functions and array subscripting operations testing
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x; -- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x; -- should not be precalculated
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x; -- should not be precalculated
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x; -- should not be precalculated
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+
+-- Mixed functions and ROW() expressions testing
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x; -- should not be precalculated
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+
+-- Mixed functions and RelabelType expressions testing
+SELECT x_stl2(x_vlt_oid()::integer) FROM x; -- should not be precalculated
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+
+-- Mixed functions and CoerceViaIO expressions testing
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x; -- should not be precalculated
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x; -- should not be precalculated
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Binary-coercible types:
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x; -- should not be precalculated
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+
+-- Not binary-coercible types:
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x; -- should not be precalculated
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+
+SELECT x_vlt_array_integer()::my_integer[] FROM x; -- should not be precalculated
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+
+-- Mixed functions and ConvertRowtypeExpr testing
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x; -- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x; -- should not be precalculated
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+
+-- Mixed functions and CASE expressions testing
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x; -- should not be precalculated
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x; -- should not be precalculated
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+
+-- Mixed functions and RowCompareExpr testing
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+
+-- Mixed functions and COALESCE expressions testing
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x; -- should not be precalculated
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+
+-- Mixed functions and GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+
+-- Mixed functions and NullTest expressions testing
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x; -- should not be precalculated
+
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x; -- should not be precalculated
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+
+-- Mixed functions and BooleanTest expressions testing
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x; -- should not be precalculated
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+
+-- Mixed functions and CoerceToDomain expressions testing
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x; -- should not be precalculated
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x; -- should not be precalculated
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x; -- should not be precalculated
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x; -- should not be precalculated
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+
+SET track_functions TO DEFAULT;
+
+-- ROW() expressions with dropped columns testing
+
+ALTER TABLE wxyz DROP COLUMN z;
+VACUUM FULL;
+
+-- ROW() expressions testing
+SELECT x_stl2_wxyz((1, '{2}', TRUE)) FROM x;
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE)) FROM x;
+SELECT x_stl2_wxyz((1, '{2}', TRUE)::wxyz) FROM x;
+
+-- Mixed functions and ROW() expressions testing
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE)) FROM x; -- should not be precalculated
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE)) FROM x;
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+
+BEGIN;
+SELECT simple();
+INSERT INTO x VALUES (5);
+SELECT simple();
+ROLLBACK;
+
+-- Drop tables and domains for testing
+
+DROP TABLE x;
+
+DROP FUNCTION x_vlt_wxyz, x_vlt_wxyz_child, x_vlt_wxyz_child2;
+DROP FUNCTION x_stl_wxyz, x_stl_wxyz_child, x_stl_wxyz_child2, x_stl2_wxyz;
+DROP TABLE wxyz, wxyz_child, wxyz_child2;
+
+DROP FUNCTION x_stl2_no_columns;
+DROP TABLE no_columns, no_columns_child, no_columns_child2;
+
+DROP FUNCTION x_stl2_my_integer_no_check;
+DROP DOMAIN my_integer_no_check;
+
+DROP FUNCTION x_stl2_my_integer_not_null;
+DROP DOMAIN my_integer_not_null;
+
+DROP FUNCTION x_stl2_my_integer_vlt_check;
+DROP DOMAIN my_integer_vlt_check;
+
+DROP FUNCTION x_stl2_my_integer_stl_check;
+DROP DOMAIN my_integer_stl_check;
+
+DROP FUNCTION x_stl2_my_integer_imm_check;
+DROP DOMAIN my_integer_imm_check;
--
1.9.1
v5-0003-Precalculate-stable-functions-costs.patchtext/x-diff; charset=us-ascii; name=v5-0003-Precalculate-stable-functions-costs.patchDownload
From 68af739040a57f35de1e0084067b76218befcd22 Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Mon, 17 Jul 2017 13:08:36 +0300
Subject: [PATCH v5 3/3] Precalculate stable functions, costs
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
This patch includes:
- cost changes for cached expressions (according to their behaviour)
---
src/backend/optimizer/path/costsize.c | 194 +++++++++++++++++++++++-----------
1 file changed, 134 insertions(+), 60 deletions(-)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index eb653cf..0ce57d5 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -140,6 +140,7 @@ static MergeScanSelCache *cached_scansel(PlannerInfo *root,
PathKey *pathkey);
static void cost_rescan(PlannerInfo *root, Path *path,
Cost *rescan_startup_cost, Cost *rescan_total_cost);
+static double cost_eval_cacheable_expr_per_tuple(CacheableExpr *node);
static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
ParamPathInfo *param_info,
@@ -3474,6 +3475,123 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
*cost = context.total;
}
+/*
+ * cost_eval_cacheable_expr_per_tuple
+ * Evaluate per tuple cost for expressions that can be cacheable.
+ *
+ * This function was created to not duplicate code for some expression and
+ * cached some expression.
+ */
+static double
+cost_eval_cacheable_expr_per_tuple(CacheableExpr *node)
+{
+ double result;
+
+ /*
+ * For each operator or function node in the given tree, we charge the
+ * estimated execution cost given by pg_proc.procost (remember to multiply
+ * this by cpu_operator_cost).
+ *
+ * Vars and Consts are charged zero, and so are boolean operators (AND,
+ * OR, NOT). Simplistic, but a lot better than no model at all.
+ */
+ if (IsA(node, ArrayRef) ||
+ IsA(node, BoolExpr) ||
+ IsA(node, FieldSelect) ||
+ IsA(node, RelabelType) ||
+ IsA(node, ConvertRowtypeExpr) ||
+ IsA(node, CaseExpr) ||
+ IsA(node, CaseTestExpr) ||
+ IsA(node, ArrayExpr) ||
+ IsA(node, RowExpr) ||
+ IsA(node, CoalesceExpr) ||
+ IsA(node, MinMaxExpr) ||
+ IsA(node, SQLValueFunction) ||
+ IsA(node, XmlExpr) ||
+ IsA(node, NullTest) ||
+ IsA(node, BooleanTest) ||
+ IsA(node, CoerceToDomain) ||
+ IsA(node, CoerceToDomainValue))
+ {
+ result = 0;
+ }
+ else if (IsA(node, FuncExpr))
+ {
+ result = get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+ }
+ else if (IsA(node, OpExpr) ||
+ IsA(node, DistinctExpr) ||
+ IsA(node, NullIfExpr))
+ {
+ OpExpr *opexpr = (OpExpr *) node;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ result = get_func_cost(opexpr->opfuncid) * cpu_operator_cost;
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ /*
+ * Estimate that the operator will be applied to about half of the
+ * array elements before the answer is determined.
+ */
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
+ Node *arraynode = (Node *) lsecond(saop->args);
+
+ set_sa_opfuncid(saop);
+ result = get_func_cost(saop->opfuncid) * cpu_operator_cost *
+ estimate_array_length(arraynode) * 0.5;
+ }
+ else if (IsA(node, CoerceViaIO))
+ {
+ CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+ Oid iofunc;
+ Oid typioparam;
+ bool typisvarlena;
+
+ /* check the result type's input function */
+ getTypeInputInfo(iocoerce->resulttype,
+ &iofunc, &typioparam);
+ result = get_func_cost(iofunc) * cpu_operator_cost;
+ /* check the input type's output function */
+ getTypeOutputInfo(exprType((Node *) iocoerce->arg),
+ &iofunc, &typisvarlena);
+ result += get_func_cost(iofunc) * cpu_operator_cost;
+ }
+ else if (IsA(node, ArrayCoerceExpr))
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+ Node *arraynode = (Node *) acoerce->arg;
+
+ if (OidIsValid(acoerce->elemfuncid))
+ result = get_func_cost(acoerce->elemfuncid) * cpu_operator_cost *
+ estimate_array_length(arraynode);
+ else
+ result = 0;
+ }
+ else if (IsA(node, RowCompareExpr))
+ {
+ /* Conservatively assume we will check all the columns */
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *lc;
+
+ result = 0;
+ foreach(lc, rcexpr->opnos)
+ {
+ Oid opid = lfirst_oid(lc);
+
+ result += get_func_cost(get_opcode(opid)) * cpu_operator_cost;
+ }
+ }
+ else
+ {
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+ }
+
+ return result;
+}
+
static bool
cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
{
@@ -3547,32 +3665,27 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
* moreover, since our rowcount estimates for functions tend to be pretty
* phony, the results would also be pretty phony.
*/
- if (IsA(node, FuncExpr))
+ if (IsA(node, FuncExpr) ||
+ IsA(node, OpExpr) ||
+ IsA(node, DistinctExpr) ||
+ IsA(node, NullIfExpr) ||
+ IsA(node, ScalarArrayOpExpr) ||
+ IsA(node, CoerceViaIO) ||
+ IsA(node, ArrayCoerceExpr) ||
+ IsA(node, RowCompareExpr))
{
- context->total.per_tuple +=
- get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+ context->total.per_tuple += cost_eval_cacheable_expr_per_tuple(
+ (CacheableExpr *) node);
}
- else if (IsA(node, OpExpr) ||
- IsA(node, DistinctExpr) ||
- IsA(node, NullIfExpr))
- {
- /* rely on struct equivalence to treat these all alike */
- set_opfuncid((OpExpr *) node);
- context->total.per_tuple +=
- get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost;
- }
- else if (IsA(node, ScalarArrayOpExpr))
+ else if (IsA(node, CachedExpr))
{
/*
- * Estimate that the operator will be applied to about half of the
- * array elements before the answer is determined.
+ * Calculate subexpression cost per tuple as usual and add it to startup
+ * cost (because subexpression will be executed only once for all
+ * tuples).
*/
- ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
- Node *arraynode = (Node *) lsecond(saop->args);
-
- set_sa_opfuncid(saop);
- context->total.per_tuple += get_func_cost(saop->opfuncid) *
- cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
+ context->total.startup += cost_eval_cacheable_expr_per_tuple(
+ ((CachedExpr *) node)->subexpr);
}
else if (IsA(node, Aggref) ||
IsA(node, WindowFunc))
@@ -3588,45 +3701,6 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
*/
return false; /* don't recurse into children */
}
- else if (IsA(node, CoerceViaIO))
- {
- CoerceViaIO *iocoerce = (CoerceViaIO *) node;
- Oid iofunc;
- Oid typioparam;
- bool typisvarlena;
-
- /* check the result type's input function */
- getTypeInputInfo(iocoerce->resulttype,
- &iofunc, &typioparam);
- context->total.per_tuple += get_func_cost(iofunc) * cpu_operator_cost;
- /* check the input type's output function */
- getTypeOutputInfo(exprType((Node *) iocoerce->arg),
- &iofunc, &typisvarlena);
- context->total.per_tuple += get_func_cost(iofunc) * cpu_operator_cost;
- }
- else if (IsA(node, ArrayCoerceExpr))
- {
- ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
- Node *arraynode = (Node *) acoerce->arg;
-
- if (OidIsValid(acoerce->elemfuncid))
- context->total.per_tuple += get_func_cost(acoerce->elemfuncid) *
- cpu_operator_cost * estimate_array_length(arraynode);
- }
- else if (IsA(node, RowCompareExpr))
- {
- /* Conservatively assume we will check all the columns */
- RowCompareExpr *rcexpr = (RowCompareExpr *) node;
- ListCell *lc;
-
- foreach(lc, rcexpr->opnos)
- {
- Oid opid = lfirst_oid(lc);
-
- context->total.per_tuple += get_func_cost(get_opcode(opid)) *
- cpu_operator_cost;
- }
- }
else if (IsA(node, CurrentOfExpr))
{
/* Report high cost to prevent selection of anything but TID scan */
--
1.9.1
On Tue, Jul 18, 2017 at 9:16 AM, Marina Polyakova
<m.polyakova@postgrespro.ru> wrote:
Here I have made the 5th version of the patches. I have added the
precalculation of all primitive nodes that don't return set, are not
volatile themselves and their arguments are constant or precalculated
expressions too. There're regression tests for all of them and little notes
in the documentation. Like for the previous patches it seems that there is
no obvious performance degradation too on regular queries (according to
pgbench).
pgbench probably isn't a very good test for this sort of thing - it
only issues very short-running queries where the cost of evaluating
expressions is a relatively small part of the total cost. Even if
things get worse, I'm not sure if you'd see it. I'm not sure exactly
how you could construct a test case that could be harmed by this patch
- I guess you'd want to initialize lots of CacheExprs but never make
use of the caching usefully?
It could also be useful to test things like TPC-H to see if you get an
improvement.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Like for the previous patches it seems that there is
no obvious performance degradation too on regular queries (according
to
pgbench).pgbench probably isn't a very good test for this sort of thing - it
only issues very short-running queries where the cost of evaluating
expressions is a relatively small part of the total cost. Even if
things get worse, I'm not sure if you'd see it.
If there's a mistake, for example, more than 1 try to replace cached
expressions in the whole query tree, results of "in buffer test" or
"mostly cache test" can different a little..
I'm not sure exactly
how you could construct a test case that could be harmed by this patch
- I guess you'd want to initialize lots of CacheExprs but never make
use of the caching usefully?
As I mentioned in the first letter about this feature it will be useful
for such text search queries [1]/messages/by-id/ba261b9fc25dea4069d8ba9a8fcadf35@postgrespro.ru:
SELECT COUNT(*) FROM messages WHERE body_tsvector @@
to_tsquery('postgres');
And I'm not sure that it is logical to precalculate stable and immutable
functions themselves, but not to precalculate expressions that behave
like stable/immutable functions; precalculate some types of operators
and not to precalculate others (ScalarArrayOpExpr, RowCompareExpr). My
patch solves the problem that not all nodes are simplified in
eval_const_expressions_mutator (for example, ScalarArrayOpExpr) and
consts of other types now behave more like ordinary consts (for example,
composite types, coerce expressions, ConvertRowtypeExpr).
It could also be useful to test things like TPC-H to see if you get an
improvement.
I saw the examples of queries in TPC-H tests. If I'm not wrong they are
not the target tests for this functionality (nothing will be
precalculated). But it's a good idea to check that there's no a
performance degradation on them too.
[1]: /messages/by-id/ba261b9fc25dea4069d8ba9a8fcadf35@postgrespro.ru
/messages/by-id/ba261b9fc25dea4069d8ba9a8fcadf35@postgrespro.ru
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
The following review has been posted through the commitfest application:
make installcheck-world: not tested
Implements feature: not tested
Spec compliant: not tested
Documentation: not tested
Hi Marina,
I'm sorry to inform you that the v5 path set become a little outdated:
```
$ git apply v5-0002-Precalculate-stable-functions-planning-and-execut.patch
error: patch failed: src/pl/plpgsql/src/pl_exec.c:6471
error: src/pl/plpgsql/src/pl_exec.c: patch does not apply
```
If it's not too much trouble could you please fix the conflicts with the current master branch?
The new status of this patch is: Waiting on Author
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello, hackers!
First of all, happy New Year!
Secondly, here there's a sixth version of the patch for the
precalculation of stable or immutable functions, stable or immutable
operators and other nonvolatile expressions.
The basic idea: the expression is precalculated (= calculated once for
all output rows, but as many times as the expression is mentioned in the
query) if:
1) it doesn't return a set,
2) it's not volatile itself,
3) its arguments are also constants or precalculated expressions.
Differences from the previous version:
* rebased, including changes for ArrayCoerce expressions;
* support for prepared statements (including tests, but only for
immutable functions);
* fix the caching of SQLValueFunctions (all of them are stable even
date/time functions);
* added the expected output for the tests in case the xml functions are
not supported;
* the tests are also performed in make check, not just in make
check-world;
* code cleanup.
Like for the previous patches it seems that there is no obvious
performance degradation too on regular queries (according to pgbench).
pgbench probably isn't a very good test for this sort of thing - it
only issues very short-running queries where the cost of evaluating
expressions is a relatively small part of the total cost. Even if
things get worse, I'm not sure if you'd see it. I'm not sure exactly
how you could construct a test case that could be harmed by this patch
- I guess you'd want to initialize lots of CacheExprs but never make
use of the caching usefully?It could also be useful to test things like TPC-H to see if you get an
improvement.
I'm sorry, the TPC-H comparative tests will be later..
Patch is attached. Any suggestions are welcome!
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v6-0001-Precalculate-stable-and-immutable-functions.patchtext/plain; name=v6-0001-Precalculate-stable-and-immutable-functions.patchDownload
From a172312064c91ca49bd9a5bbb7fc7dd6924dcd1d Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Sun, 31 Dec 2017 08:44:08 +0300
Subject: [PATCH v6] Precalculate stable and immutable functions
Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.
In this patch the function / operator / another expression is precalculated (=
calculated once for all output rows, but as many times as the expression is
mentioned in the query) if:
1) it doesn't return a set,
2) it's not volatile itself,
3) its arguments are also constants or precalculated expressions.
Costs are changed to reflect the changed behaviour.
Tests, small notes in the documentation and support for the prepared statements
are included.
---
doc/src/sgml/ref/create_function.sgml | 9 +
doc/src/sgml/xfunc.sgml | 6 +-
src/backend/commands/createas.c | 3 +-
src/backend/commands/explain.c | 3 +-
src/backend/commands/foreigncmds.c | 1 +
src/backend/commands/portalcmds.c | 2 +-
src/backend/commands/prepare.c | 7 +-
src/backend/commands/schemacmds.c | 1 +
src/backend/commands/trigger.c | 1 +
src/backend/executor/execExpr.c | 39 +-
src/backend/executor/execExprInterp.c | 52 +
src/backend/executor/execParallel.c | 1 +
src/backend/executor/functions.c | 2 +
src/backend/executor/spi.c | 9 +-
src/backend/nodes/copyfuncs.c | 17 +
src/backend/nodes/equalfuncs.c | 11 +
src/backend/nodes/nodeFuncs.c | 76 +
src/backend/nodes/outfuncs.c | 12 +
src/backend/nodes/params.c | 2 +
src/backend/nodes/readfuncs.c | 16 +
src/backend/optimizer/path/allpaths.c | 9 +-
src/backend/optimizer/path/clausesel.c | 12 +
src/backend/optimizer/path/costsize.c | 218 +-
src/backend/optimizer/plan/planagg.c | 2 +
src/backend/optimizer/plan/planner.c | 1226 +++-
src/backend/optimizer/prep/prepjointree.c | 1 +
src/backend/optimizer/util/clauses.c | 41 +-
src/backend/parser/parse_agg.c | 1 +
src/backend/tcop/postgres.c | 11 +-
src/backend/tcop/utility.c | 2 +
src/backend/utils/adt/domains.c | 5 +-
src/backend/utils/adt/ruleutils.c | 5 +
src/backend/utils/cache/plancache.c | 180 +-
src/backend/utils/cache/typcache.c | 56 +-
src/include/executor/execExpr.h | 31 +
src/include/nodes/execnodes.h | 19 +-
src/include/nodes/nodes.h | 3 +
src/include/nodes/params.h | 22 +
src/include/nodes/plannodes.h | 2 +
src/include/nodes/primnodes.h | 96 +-
src/include/nodes/relation.h | 4 +-
src/include/optimizer/planner.h | 11 +-
src/include/optimizer/tlist.h | 8 +-
src/include/tcop/tcopprot.h | 4 +-
src/include/utils/plancache.h | 12 +-
src/include/utils/typcache.h | 2 +
src/pl/plpgsql/src/pl_exec.c | 18 +
.../expected/precalculate_stable_functions.out | 6645 ++++++++++++++++++++
.../expected/precalculate_stable_functions_1.out | 6291 ++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
.../regress/sql/precalculate_stable_functions.sql | 2117 +++++++
52 files changed, 17162 insertions(+), 165 deletions(-)
create mode 100644 src/test/regress/expected/precalculate_stable_functions.out
create mode 100644 src/test/regress/expected/precalculate_stable_functions_1.out
create mode 100644 src/test/regress/sql/precalculate_stable_functions.sql
diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml
index fd229d1..7079ec2 100644
--- a/doc/src/sgml/ref/create_function.sgml
+++ b/doc/src/sgml/ref/create_function.sgml
@@ -337,6 +337,15 @@ CREATE [ OR REPLACE ] FUNCTION
<literal>setval()</literal>.
</para>
+ <note>
+ <para>
+ Stable, immutable functions and other nonovolatile expressions are
+ precalculated (= calculated once for all output rows, but as many times
+ as expression is mentioned in query), if they don't return a set and
+ their arguments are constants or recursively precalculated expressions.
+ </para>
+ </note>
+
<para>
For additional details see <xref linkend="xfunc-volatility"/>.
</para>
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index bbc3766..4825d98 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1533,7 +1533,11 @@ CREATE FUNCTION test(int, int) RETURNS int
<para>
For best optimization results, you should label your functions with the
- strictest volatility category that is valid for them.
+ strictest volatility category that is valid for them. Stable, immutable
+ functions and other nonovolatile expressions are precalculated (= calculated
+ once for all output rows, but as many times as expression is mentioned in
+ query), if they don't return a set and their arguments are constants or
+ recursively precalculated expressions.
</para>
<para>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4d77411..9f2b2e5 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -327,7 +327,8 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
Assert(query->commandType == CMD_SELECT);
/* plan the query */
- plan = pg_plan_query(query, CURSOR_OPT_PARALLEL_OK, params);
+ plan = pg_plan_query(query, CURSOR_OPT_PARALLEL_OK,
+ (ParamListInfoCommon) params);
/*
* Use a snapshot with an updated command ID to ensure this query sees
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 7e4fbaf..b8baae3 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -361,7 +361,8 @@ ExplainOneQuery(Query *query, int cursorOptions,
INSTR_TIME_SET_CURRENT(planstart);
/* plan the query */
- plan = pg_plan_query(query, cursorOptions, params);
+ plan = pg_plan_query(query, cursorOptions,
+ (ParamListInfoCommon) params);
INSTR_TIME_SET_CURRENT(planduration);
INSTR_TIME_SUBTRACT(planduration, planstart);
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 9ad9915..4435ab5 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -1619,6 +1619,7 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
pstmt->utilityStmt = (Node *) cstmt;
pstmt->stmt_location = rs->stmt_location;
pstmt->stmt_len = rs->stmt_len;
+ pstmt->hasCachedExpr = false;
/* Execute statement */
ProcessUtility(pstmt,
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 76d6cf1..e66bead 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -89,7 +89,7 @@ PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
/* Plan the query, applying the specified options */
- plan = pg_plan_query(query, cstmt->options, params);
+ plan = pg_plan_query(query, cstmt->options, (ParamListInfoCommon) params);
/*
* Create a portal and copy the plan and queryString into its memory.
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 6e90912..0ad37b3 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -243,7 +243,8 @@ ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
entry->plansource->query_string);
/* Replan if needed, and increment plan refcount for portal */
- cplan = GetCachedPlan(entry->plansource, paramLI, false, NULL);
+ cplan = GetCachedPlan(entry->plansource, (ParamListInfoCommon) paramLI,
+ false, NULL, true);
plan_list = cplan->stmt_list;
/*
@@ -397,6 +398,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
palloc(offsetof(ParamListInfoData, params) +
num_params * sizeof(ParamExternData));
/* we have static list of params, so no hooks needed */
+ paramLI->common.type = PARAM_LIST_INFO_DATA;
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->paramCompile = NULL;
@@ -670,7 +672,8 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
}
/* Replan if needed, and acquire a transient refcount */
- cplan = GetCachedPlan(entry->plansource, paramLI, true, queryEnv);
+ cplan = GetCachedPlan(entry->plansource, (ParamListInfoCommon) paramLI,
+ true, queryEnv, true);
INSTR_TIME_SET_CURRENT(planduration);
INSTR_TIME_SUBTRACT(planduration, planstart);
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index f9ea73f..0a20ad7 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -188,6 +188,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
wrapper->utilityStmt = stmt;
wrapper->stmt_location = stmt_location;
wrapper->stmt_len = stmt_len;
+ wrapper->hasCachedExpr = false;
/* do this step */
ProcessUtility(wrapper,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 92ae382..7515860 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1254,6 +1254,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
wrapper->utilityStmt = (Node *) atstmt;
wrapper->stmt_location = -1;
wrapper->stmt_len = -1;
+ wrapper->hasCachedExpr = false;
/* ... and execute it */
ProcessUtility(wrapper,
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 55bb925..fc68782 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -858,6 +858,38 @@ ExecInitExprRec(Expr *node, ExprState *state,
break;
}
+ case T_CachedExpr:
+ {
+ /*
+ * Allocate CachedExprState used by all steps of CachedExpr
+ * evaluation.
+ */
+ scratch.d.cachedexpr.state = (CachedExprState *) palloc(
+ sizeof(CachedExprState));
+ scratch.d.cachedexpr.state->isExecuted = false;
+ scratch.d.cachedexpr.state->resnull = false;
+ scratch.d.cachedexpr.state->resvalue = (Datum) 0;
+ scratch.d.cachedexpr.state->restypid = exprType(
+ (const Node *) node);
+
+ /* add EEOP_CACHEDEXPR_IF_CACHED step */
+ scratch.opcode = EEOP_CACHEDEXPR_IF_CACHED;
+ ExprEvalPushStep(state, &scratch);
+
+ /* add subexpression steps */
+ ExecInitExprRec((Expr *) ((CachedExpr *) node)->subexpr, state,
+ resv, resnull);
+
+ /* add EEOP_CACHEDEXPR_SUBEXPR_END step */
+ scratch.opcode = EEOP_CACHEDEXPR_SUBEXPR_END;
+ ExprEvalPushStep(state, &scratch);
+
+ /* adjust jump target */
+ scratch.d.cachedexpr.state->jumpdone = state->steps_len;
+
+ break;
+ }
+
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
@@ -2057,6 +2089,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
break;
}
+ /* note that DomainConstraintExpr nodes are handled within this block */
case T_CoerceToDomain:
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
@@ -2648,6 +2681,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
bool *domainnull = NULL;
Datum *save_innermost_domainval;
bool *save_innermost_domainnull;
+ List *constraints;
ListCell *l;
scratch->d.domaincheck.resulttype = ctest->resulttype;
@@ -2685,15 +2719,16 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
constraint_ref,
CurrentMemoryContext,
false);
+ constraints = GetDomainConstraintExprList(constraint_ref);
/*
* Compile code to check each domain constraint. NOTNULL constraints can
* just be applied on the resv/resnull value, but for CHECK constraints we
* need more pushups.
*/
- foreach(l, constraint_ref->constraints)
+ foreach(l, constraints)
{
- DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+ DomainConstraintExpr *con = (DomainConstraintExpr *) lfirst(l);
scratch->d.domaincheck.constraintname = con->name;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 0c3f668..39c9bc5 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -68,6 +68,7 @@
#include "pgstat.h"
#include "utils/builtins.h"
#include "utils/date.h"
+#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -368,6 +369,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_WINDOW_FUNC,
&&CASE_EEOP_SUBPLAN,
&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
+ &&CASE_EEOP_CACHEDEXPR_IF_CACHED,
+ &&CASE_EEOP_CACHEDEXPR_SUBEXPR_END,
&&CASE_EEOP_LAST
};
@@ -1543,6 +1546,55 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_CACHEDEXPR_IF_CACHED)
+ {
+ if (op->d.cachedexpr.state->isExecuted)
+ {
+ /* use saved result and skip subexpression evaluation */
+ *op->resnull = op->d.cachedexpr.state->resnull;
+ if (!(*op->resnull))
+ *op->resvalue = op->d.cachedexpr.state->resvalue;
+
+ EEO_JUMP(op->d.cachedexpr.state->jumpdone);
+ }
+
+ /* we are ready for subexpression evaluation */
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CACHEDEXPR_SUBEXPR_END)
+ {
+ int16 restyplen;
+ bool restypbyval;
+ MemoryContext oldContext;
+
+ /* save result */
+ op->d.cachedexpr.state->resnull = *op->resnull;
+ if (!(*op->resnull))
+ {
+ get_typlenbyval(op->d.cachedexpr.state->restypid, &restyplen,
+ &restypbyval);
+
+ /*
+ * Switch per-query memory context. It is necessary to save the
+ * subexpression result between all tuples if its value datum is
+ * a pointer.
+ */
+ oldContext = MemoryContextSwitchTo(
+ econtext->ecxt_per_query_memory);
+
+ op->d.cachedexpr.state->resvalue = datumCopy(*op->resvalue,
+ restypbyval,
+ restyplen);
+
+ /* switch memory context back */
+ MemoryContextSwitchTo(oldContext);
+ }
+ op->d.cachedexpr.state->isExecuted = true;
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_LAST)
{
/* unreachable */
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index b344d4b..4f09bc1 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -207,6 +207,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
pstmt->utilityStmt = NULL;
pstmt->stmt_location = -1;
pstmt->stmt_len = -1;
+ pstmt->hasCachedExpr = false;
/* Return serialized copy of our dummy PlannedStmt. */
return nodeToString(pstmt);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 527f7d8..f477d8d 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -502,6 +502,7 @@ init_execution_state(List *queryTree_list,
stmt->utilityStmt = queryTree->utilityStmt;
stmt->stmt_location = queryTree->stmt_location;
stmt->stmt_len = queryTree->stmt_len;
+ stmt->hasCachedExpr = false;
}
else
stmt = pg_plan_query(queryTree,
@@ -912,6 +913,7 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
palloc(offsetof(ParamListInfoData, params) +
nargs * sizeof(ParamExternData));
/* we have static list of params, so no hooks needed */
+ paramLI->common.type = PARAM_LIST_INFO_DATA;
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->paramCompile = NULL;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 977f317..5888ba1 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1202,7 +1202,8 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
*/
/* Replan if needed, and increment plan refcount for portal */
- cplan = GetCachedPlan(plansource, paramLI, false, _SPI_current->queryEnv);
+ cplan = GetCachedPlan(plansource, (ParamListInfoCommon) paramLI, false,
+ _SPI_current->queryEnv, false);
stmt_list = cplan->stmt_list;
if (!plan->saved)
@@ -1636,7 +1637,7 @@ SPI_plan_get_cached_plan(SPIPlanPtr plan)
/* Get the generic plan for the query */
cplan = GetCachedPlan(plansource, NULL, plan->saved,
- _SPI_current->queryEnv);
+ _SPI_current->queryEnv, false);
Assert(cplan == plansource->gplan);
/* Pop the error context stack */
@@ -2026,7 +2027,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
* Replan if needed, and increment plan refcount. If it's a saved
* plan, the refcount must be backed by the CurrentResourceOwner.
*/
- cplan = GetCachedPlan(plansource, paramLI, plan->saved, _SPI_current->queryEnv);
+ cplan = GetCachedPlan(plansource, (ParamListInfoCommon) paramLI,
+ plan->saved, _SPI_current->queryEnv, false);
stmt_list = cplan->stmt_list;
/*
@@ -2257,6 +2259,7 @@ _SPI_convert_params(int nargs, Oid *argtypes,
paramLI = (ParamListInfo) palloc(offsetof(ParamListInfoData, params) +
nargs * sizeof(ParamExternData));
/* we have static list of params, so no hooks needed */
+ paramLI->common.type = PARAM_LIST_INFO_DATA;
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->paramCompile = NULL;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 84d7171..c72f91d 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -101,6 +101,7 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(utilityStmt);
COPY_LOCATION_FIELD(stmt_location);
COPY_LOCATION_FIELD(stmt_len);
+ COPY_SCALAR_FIELD(hasCachedExpr);
return newnode;
}
@@ -1417,6 +1418,19 @@ _copyWindowFunc(const WindowFunc *from)
}
/*
+ * _copyCachedExpr
+ */
+static CachedExpr *
+_copyCachedExpr(const CachedExpr *from)
+{
+ CachedExpr *newnode = makeNode(CachedExpr);
+
+ COPY_NODE_FIELD(subexpr);
+
+ return newnode;
+}
+
+/*
* _copyArrayRef
*/
static ArrayRef *
@@ -4884,6 +4898,9 @@ copyObjectImpl(const void *from)
case T_WindowFunc:
retval = _copyWindowFunc(from);
break;
+ case T_CachedExpr:
+ retval = _copyCachedExpr(from);
+ break;
case T_ArrayRef:
retval = _copyArrayRef(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2e869a9..cb66eaa 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -264,6 +264,14 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b)
}
static bool
+_equalCachedExpr(const CachedExpr *a, const CachedExpr *b)
+{
+ COMPARE_NODE_FIELD(subexpr);
+
+ return true;
+}
+
+static bool
_equalArrayRef(const ArrayRef *a, const ArrayRef *b)
{
COMPARE_SCALAR_FIELD(refarraytype);
@@ -3035,6 +3043,9 @@ equal(const void *a, const void *b)
case T_WindowFunc:
retval = _equalWindowFunc(a, b);
break;
+ case T_CachedExpr:
+ retval = _equalCachedExpr(a, b);
+ break;
case T_ArrayRef:
retval = _equalArrayRef(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index c2a93b2..5e4d2f7 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -66,6 +66,10 @@ exprType(const Node *expr)
case T_WindowFunc:
type = ((const WindowFunc *) expr)->wintype;
break;
+ case T_CachedExpr:
+ type =
+ exprType((const Node *) ((const CachedExpr *) expr)->subexpr);
+ break;
case T_ArrayRef:
{
const ArrayRef *arrayref = (const ArrayRef *) expr;
@@ -286,6 +290,9 @@ exprTypmod(const Node *expr)
return ((const Const *) expr)->consttypmod;
case T_Param:
return ((const Param *) expr)->paramtypmod;
+ case T_CachedExpr:
+ return
+ exprTypmod((const Node *) ((const CachedExpr *) expr)->subexpr);
case T_ArrayRef:
/* typmod is the same for array or element */
return ((const ArrayRef *) expr)->reftypmod;
@@ -573,6 +580,11 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
return true;
}
+ if (expr && IsA(expr, CachedExpr))
+ return exprIsLengthCoercion(
+ (const Node *) ((const CachedExpr *) expr)->subexpr,
+ coercedTypmod);
+
return false;
}
@@ -655,6 +667,11 @@ strip_implicit_coercions(Node *node)
if (c->coercionformat == COERCE_IMPLICIT_CAST)
return strip_implicit_coercions((Node *) c->arg);
}
+ else if (IsA(node, CachedExpr))
+ {
+ return strip_implicit_coercions(
+ (Node *) ((CachedExpr *) node)->subexpr);
+ }
return node;
}
@@ -699,6 +716,8 @@ expression_returns_set_walker(Node *node, void *context)
return false;
if (IsA(node, WindowFunc))
return false;
+ if (IsA(node, CachedExpr))
+ return false;
return expression_tree_walker(node, expression_returns_set_walker,
context);
@@ -744,6 +763,10 @@ exprCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->wincollid;
break;
+ case T_CachedExpr:
+ coll = exprCollation(
+ (const Node *) ((const CachedExpr *) expr)->subexpr);
+ break;
case T_ArrayRef:
coll = ((const ArrayRef *) expr)->refcollid;
break;
@@ -933,6 +956,10 @@ exprInputCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->inputcollid;
break;
+ case T_CachedExpr:
+ coll = exprInputCollation(
+ (const Node *) ((const CachedExpr *) expr)->subexpr);
+ break;
case T_FuncExpr:
coll = ((const FuncExpr *) expr)->inputcollid;
break;
@@ -988,6 +1015,10 @@ exprSetCollation(Node *expr, Oid collation)
case T_WindowFunc:
((WindowFunc *) expr)->wincollid = collation;
break;
+ case T_CachedExpr:
+ exprSetCollation((Node *) ((CachedExpr *) expr)->subexpr,
+ collation);
+ break;
case T_ArrayRef:
((ArrayRef *) expr)->refcollid = collation;
break;
@@ -1129,6 +1160,10 @@ exprSetInputCollation(Node *expr, Oid inputcollation)
case T_WindowFunc:
((WindowFunc *) expr)->inputcollid = inputcollation;
break;
+ case T_CachedExpr:
+ exprSetInputCollation((Node *) ((CachedExpr *) expr)->subexpr,
+ inputcollation);
+ break;
case T_FuncExpr:
((FuncExpr *) expr)->inputcollid = inputcollation;
break;
@@ -1217,6 +1252,10 @@ exprLocation(const Node *expr)
/* function name should always be the first thing */
loc = ((const WindowFunc *) expr)->location;
break;
+ case T_CachedExpr:
+ loc = exprLocation(
+ (const Node *) ((const CachedExpr *) expr)->subexpr);
+ break;
case T_ArrayRef:
/* just use array argument's location */
loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
@@ -1590,6 +1629,9 @@ fix_opfuncids_walker(Node *node, void *context)
{
if (node == NULL)
return false;
+ if (IsA(node, CachedExpr))
+ return fix_opfuncids_walker((Node *) ((CachedExpr *) node)->subexpr,
+ context);
if (IsA(node, OpExpr))
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
@@ -1669,6 +1711,9 @@ check_functions_in_node(Node *node, check_function_callback checker,
return true;
}
break;
+ case T_CachedExpr:
+ return check_functions_in_node(
+ (Node *) ((CachedExpr *) node)->subexpr, checker, context);
case T_FuncExpr:
{
FuncExpr *expr = (FuncExpr *) node;
@@ -1910,6 +1955,18 @@ expression_tree_walker(Node *node,
return true;
}
break;
+ case T_CachedExpr:
+ {
+ /*
+ * cachedexpr is processed by walker, so its subexpr is
+ * processed too and we need to process sub-nodes of subexpr.
+ */
+ if (expression_tree_walker(
+ (Node *) ((CachedExpr *) node)->subexpr,
+ walker, context))
+ return true;
+ }
+ break;
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
@@ -2528,6 +2585,25 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_CachedExpr:
+ {
+ CachedExpr *expr = (CachedExpr *) node;
+ CachedExpr *newnode;
+
+ FLATCOPY(newnode, expr, CachedExpr);
+
+ /*
+ * expr is already mutated, so its subexpr is already mutated
+ * too and we need to mutate sub-nodes of subexpr.
+ */
+ newnode->subexpr = (CacheableExpr *) expression_tree_mutator(
+ (Node *) expr->subexpr,
+ mutator,
+ context);
+
+ return (Node *) newnode;
+ }
+ break;
case T_ArrayRef:
{
ArrayRef *arrayref = (ArrayRef *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e468d7c..80084d5 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -286,6 +286,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(utilityStmt);
WRITE_LOCATION_FIELD(stmt_location);
WRITE_LOCATION_FIELD(stmt_len);
+ WRITE_BOOL_FIELD(hasCachedExpr);
}
/*
@@ -1180,6 +1181,14 @@ _outWindowFunc(StringInfo str, const WindowFunc *node)
}
static void
+_outCachedExpr(StringInfo str, const CachedExpr *node)
+{
+ WRITE_NODE_TYPE("CACHEDEXPR");
+
+ WRITE_NODE_FIELD(subexpr);
+}
+
+static void
_outArrayRef(StringInfo str, const ArrayRef *node)
{
WRITE_NODE_TYPE("ARRAYREF");
@@ -3793,6 +3802,9 @@ outNode(StringInfo str, const void *obj)
case T_WindowFunc:
_outWindowFunc(str, obj);
break;
+ case T_CachedExpr:
+ _outCachedExpr(str, obj);
+ break;
case T_ArrayRef:
_outArrayRef(str, obj);
break;
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index 94acdf4..37451e8 100644
--- a/src/backend/nodes/params.c
+++ b/src/backend/nodes/params.c
@@ -46,6 +46,7 @@ copyParamList(ParamListInfo from)
from->numParams * sizeof(ParamExternData);
retval = (ParamListInfo) palloc(size);
+ retval->common.type = PARAM_LIST_INFO_DATA;
retval->paramFetch = NULL;
retval->paramFetchArg = NULL;
retval->paramCompile = NULL;
@@ -222,6 +223,7 @@ RestoreParamList(char **start_address)
nparams * sizeof(ParamExternData);
paramLI = (ParamListInfo) palloc(size);
+ paramLI->common.type = PARAM_LIST_INFO_DATA;
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->paramCompile = NULL;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 1133c70..7fb9aef 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -640,6 +640,19 @@ _readWindowFunc(void)
}
/*
+ * _readCachedExpr
+ */
+static CachedExpr *
+_readCachedExpr(void)
+{
+ READ_LOCALS(CachedExpr);
+
+ READ_NODE_FIELD(subexpr);
+
+ READ_DONE();
+}
+
+/*
* _readArrayRef
*/
static ArrayRef *
@@ -1484,6 +1497,7 @@ _readPlannedStmt(void)
READ_NODE_FIELD(utilityStmt);
READ_LOCATION_FIELD(stmt_location);
READ_LOCATION_FIELD(stmt_len);
+ READ_BOOL_FIELD(hasCachedExpr);
READ_DONE();
}
@@ -2482,6 +2496,8 @@ parseNodeString(void)
return_value = _readGroupingFunc();
else if (MATCH("WINDOWFUNC", 10))
return_value = _readWindowFunc();
+ else if (MATCH("CACHEDEXPR", 10))
+ return_value = _readCachedExpr();
else if (MATCH("ARRAYREF", 8))
return_value = _readArrayRef();
else if (MATCH("FUNCEXPR", 8))
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0e8463e..109591e 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -380,7 +380,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
set_subquery_pathlist(root, rel, rti, rte);
break;
case RTE_FUNCTION:
- set_function_size_estimates(root, rel);
+ {
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo, root);
+ set_function_size_estimates(root, rel);
+ }
break;
case RTE_TABLEFUNC:
set_tablefunc_size_estimates(root, rel);
@@ -519,6 +523,9 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
*/
check_index_predicates(root, rel);
+ rel->baserestrictinfo = replace_qual_cached_expressions(
+ rel->baserestrictinfo, root);
+
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
}
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index b4cbc34..ba7e03e 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -827,6 +827,18 @@ clause_selectivity(PlannerInfo *root,
jointype,
sjinfo);
}
+ else if (IsA(clause, CachedExpr))
+ {
+ /*
+ * Not sure this case is needed, but it can't hurt.
+ * Calculate selectivity of subexpression.
+ */
+ s1 = clause_selectivity(root,
+ (Node *) ((CachedExpr *) clause)->subexpr,
+ varRelid,
+ jointype,
+ sjinfo);
+ }
else
{
/*
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index c3daacd..405a903 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -143,6 +143,8 @@ static MergeScanSelCache *cached_scansel(PlannerInfo *root,
PathKey *pathkey);
static void cost_rescan(PlannerInfo *root, Path *path,
Cost *rescan_startup_cost, Cost *rescan_total_cost);
+static QualCost cost_eval_cacheable_expr(CacheableExpr *node,
+ PlannerInfo *root);
static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
ParamPathInfo *param_info,
@@ -3729,6 +3731,129 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
*cost = context.total;
}
+/*
+ * cost_eval_cacheable_expr
+ * Evaluate the cost for expressions that can be cached.
+ *
+ * This function was created to not duplicate code for some expression and
+ * cached some expression.
+ */
+static QualCost
+cost_eval_cacheable_expr(CacheableExpr *node, PlannerInfo *root)
+{
+ QualCost node_cost;
+
+ node_cost.startup = node_cost.per_tuple = 0;
+
+ /*
+ * For each operator or function node in the given tree, we charge the
+ * estimated execution cost given by pg_proc.procost (remember to multiply
+ * this by cpu_operator_cost).
+ *
+ * Vars and Consts are charged zero, and so are boolean operators (AND,
+ * OR, NOT). Simplistic, but a lot better than no model at all.
+ */
+ if (IsA(node, FuncExpr))
+ {
+ node_cost.per_tuple = get_func_cost(((FuncExpr *) node)->funcid) *
+ cpu_operator_cost;
+ }
+ else if (IsA(node, OpExpr) ||
+ IsA(node, DistinctExpr) ||
+ IsA(node, NullIfExpr))
+ {
+ OpExpr *opexpr = (OpExpr *) node;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ node_cost.per_tuple = get_func_cost(opexpr->opfuncid) *
+ cpu_operator_cost;
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ /*
+ * Estimate that the operator will be applied to about half of the
+ * array elements before the answer is determined.
+ */
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
+ Node *arraynode = (Node *) lsecond(saop->args);
+
+ set_sa_opfuncid(saop);
+ node_cost.per_tuple = get_func_cost(saop->opfuncid) *
+ cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
+ }
+ else if (IsA(node, CoerceViaIO))
+ {
+ CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+ Oid iofunc;
+ Oid typioparam;
+ bool typisvarlena;
+
+ /* check the result type's input function */
+ getTypeInputInfo(iocoerce->resulttype,
+ &iofunc, &typioparam);
+ node_cost.per_tuple = get_func_cost(iofunc) * cpu_operator_cost;
+ /* check the input type's output function */
+ getTypeOutputInfo(exprType((Node *) iocoerce->arg),
+ &iofunc, &typisvarlena);
+ node_cost.per_tuple += get_func_cost(iofunc) * cpu_operator_cost;
+ }
+ else if (IsA(node, ArrayCoerceExpr))
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+ QualCost perelemcost;
+
+ cost_qual_eval_node(&perelemcost, (Node *) acoerce->elemexpr, root);
+ node_cost.startup = perelemcost.startup;
+ if (perelemcost.per_tuple > 0)
+ node_cost.per_tuple += perelemcost.per_tuple *
+ estimate_array_length((Node *) acoerce->arg);
+ }
+ else if (IsA(node, RowCompareExpr))
+ {
+ /* Conservatively assume we will check all the columns */
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *lc;
+
+ foreach(lc, rcexpr->opnos)
+ {
+ Oid opid = lfirst_oid(lc);
+
+ node_cost.per_tuple += get_func_cost(get_opcode(opid)) *
+ cpu_operator_cost;
+ }
+ }
+ else if (IsA(node, MinMaxExpr) ||
+ IsA(node, SQLValueFunction) ||
+ IsA(node, XmlExpr) ||
+ IsA(node, CoerceToDomain))
+ {
+ /* Treat all these as having cost 1 */
+ node_cost.per_tuple = cpu_operator_cost;
+ }
+ else if (!(IsA(node, ArrayRef) ||
+ IsA(node, BoolExpr) ||
+ IsA(node, FieldSelect) ||
+ IsA(node, RelabelType) ||
+ IsA(node, ConvertRowtypeExpr) ||
+ IsA(node, CaseExpr) ||
+ IsA(node, CaseTestExpr) ||
+ IsA(node, ArrayExpr) ||
+ IsA(node, RowExpr) ||
+ IsA(node, CoalesceExpr) ||
+ IsA(node, NullTest) ||
+ IsA(node, BooleanTest) ||
+ IsA(node, CoerceToDomainValue)))
+ {
+ elog(ERROR,
+ "unrecognized type of cacheable node: %d",
+ (int) nodeTag(node));
+ }
+
+ return node_cost;
+}
+
static bool
cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
{
@@ -3802,32 +3927,35 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
* moreover, since our rowcount estimates for functions tend to be pretty
* phony, the results would also be pretty phony.
*/
- if (IsA(node, FuncExpr))
- {
- context->total.per_tuple +=
- get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
- }
- else if (IsA(node, OpExpr) ||
- IsA(node, DistinctExpr) ||
- IsA(node, NullIfExpr))
+ if (IsA(node, FuncExpr) ||
+ IsA(node, OpExpr) ||
+ IsA(node, DistinctExpr) ||
+ IsA(node, NullIfExpr) ||
+ IsA(node, ScalarArrayOpExpr) ||
+ IsA(node, CoerceViaIO) ||
+ IsA(node, ArrayCoerceExpr) ||
+ IsA(node, RowCompareExpr) ||
+ IsA(node, MinMaxExpr) ||
+ IsA(node, SQLValueFunction) ||
+ IsA(node, XmlExpr) ||
+ IsA(node, CoerceToDomain))
{
- /* rely on struct equivalence to treat these all alike */
- set_opfuncid((OpExpr *) node);
- context->total.per_tuple +=
- get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost;
+ QualCost node_cost = cost_eval_cacheable_expr(
+ (CacheableExpr *) node, context->root);
+
+ context->total.startup += node_cost.startup;
+ context->total.per_tuple += node_cost.per_tuple;
}
- else if (IsA(node, ScalarArrayOpExpr))
+ else if (IsA(node, CachedExpr))
{
/*
- * Estimate that the operator will be applied to about half of the
- * array elements before the answer is determined.
+ * Calculate subexpression cost as usual and add it to startup cost
+ * (because subexpression will be executed only once for all tuples).
*/
- ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
- Node *arraynode = (Node *) lsecond(saop->args);
+ QualCost node_cost = cost_eval_cacheable_expr(
+ ((CachedExpr *) node)->subexpr, context->root);
- set_sa_opfuncid(saop);
- context->total.per_tuple += get_func_cost(saop->opfuncid) *
- cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
+ context->total.startup += node_cost.startup + node_cost.per_tuple;
}
else if (IsA(node, Aggref) ||
IsA(node, WindowFunc))
@@ -3843,55 +3971,9 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
*/
return false; /* don't recurse into children */
}
- else if (IsA(node, CoerceViaIO))
- {
- CoerceViaIO *iocoerce = (CoerceViaIO *) node;
- Oid iofunc;
- Oid typioparam;
- bool typisvarlena;
-
- /* check the result type's input function */
- getTypeInputInfo(iocoerce->resulttype,
- &iofunc, &typioparam);
- context->total.per_tuple += get_func_cost(iofunc) * cpu_operator_cost;
- /* check the input type's output function */
- getTypeOutputInfo(exprType((Node *) iocoerce->arg),
- &iofunc, &typisvarlena);
- context->total.per_tuple += get_func_cost(iofunc) * cpu_operator_cost;
- }
- else if (IsA(node, ArrayCoerceExpr))
- {
- ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
- QualCost perelemcost;
-
- cost_qual_eval_node(&perelemcost, (Node *) acoerce->elemexpr,
- context->root);
- context->total.startup += perelemcost.startup;
- if (perelemcost.per_tuple > 0)
- context->total.per_tuple += perelemcost.per_tuple *
- estimate_array_length((Node *) acoerce->arg);
- }
- else if (IsA(node, RowCompareExpr))
+ else if (IsA(node, NextValueExpr))
{
- /* Conservatively assume we will check all the columns */
- RowCompareExpr *rcexpr = (RowCompareExpr *) node;
- ListCell *lc;
-
- foreach(lc, rcexpr->opnos)
- {
- Oid opid = lfirst_oid(lc);
-
- context->total.per_tuple += get_func_cost(get_opcode(opid)) *
- cpu_operator_cost;
- }
- }
- else if (IsA(node, MinMaxExpr) ||
- IsA(node, SQLValueFunction) ||
- IsA(node, XmlExpr) ||
- IsA(node, CoerceToDomain) ||
- IsA(node, NextValueExpr))
- {
- /* Treat all these as having cost 1 */
+ /* Treat this as having cost 1 */
context->total.per_tuple += cpu_operator_cost;
}
else if (IsA(node, CurrentOfExpr))
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 889e8af..f2670b2 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -38,6 +38,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
@@ -368,6 +369,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
subroot->plan_params = NIL;
subroot->outer_params = NULL;
subroot->init_plans = NIL;
+ subroot->hasCachedExpr = false;
subroot->parse = parse = copyObject(root->parse);
IncrementVarSublevelsUp((Node *) parse, 1, 1);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 382791f..88b3a84 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -56,6 +56,7 @@
#include "utils/selfuncs.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
/* GUC parameters */
@@ -109,6 +110,18 @@ typedef struct
int *tleref_to_colnum_map;
} grouping_sets_data;
+typedef struct replace_cached_expressions_context
+{
+ PlannerInfo *root;
+
+ /*
+ * Pointers are nulls if the parent nodes don't use CaseTestExpr /
+ * CoerceToDomainValue nodes respectively.
+ */
+ bool *innermost_nonconst_or_noncached_casetestexpr;
+ bool *innermost_nonconst_or_noncached_coercetodomainvalue;
+} replace_cached_expressions_context;
+
/* Local functions */
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
@@ -185,6 +198,8 @@ static PathTarget *make_sort_input_target(PlannerInfo *root,
bool *have_postponed_srfs);
static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel,
List *targets, List *targets_contain_srfs);
+static Node *replace_cached_expressions_mutator(Node *node,
+ replace_cached_expressions_context *context);
/*****************************************************************************
@@ -201,7 +216,7 @@ static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel,
*
*****************************************************************************/
PlannedStmt *
-planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
+planner(Query *parse, int cursorOptions, ParamListInfoCommon boundParams)
{
PlannedStmt *result;
@@ -213,7 +228,8 @@ planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
}
PlannedStmt *
-standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
+standard_planner(Query *parse, int cursorOptions,
+ ParamListInfoCommon boundParams)
{
PlannedStmt *result;
PlannerGlobal *glob;
@@ -480,6 +496,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->utilityStmt = parse->utilityStmt;
result->stmt_location = parse->stmt_location;
result->stmt_len = parse->stmt_len;
+ result->hasCachedExpr = root->hasCachedExpr;
return result;
}
@@ -554,6 +571,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
else
root->wt_param_id = -1;
root->non_recursive_path = NULL;
+ root->hasCachedExpr = false;
/*
* If there is a WITH list, process each WITH query and build an initplan
@@ -1220,6 +1238,7 @@ inheritance_planner(PlannerInfo *root)
*/
subroot = makeNode(PlannerInfo);
memcpy(subroot, parent_root, sizeof(PlannerInfo));
+ subroot->hasCachedExpr = false;
/*
* Generate modified query with this rel as target. We first apply
@@ -6083,6 +6102,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
root->query_level = 1;
root->planner_cxt = CurrentMemoryContext;
root->wt_param_id = -1;
+ root->hasCachedExpr = false;
/* Build a minimal RTE for the rel */
rte = makeNode(RangeTblEntry);
@@ -6201,3 +6221,1205 @@ get_partitioned_child_rels_for_join(PlannerInfo *root, Relids join_relids)
return result;
}
+
+/*
+ * replace_pathtarget_cached_expressions
+ * Replace cached expresisons in a PathTarget tlist.
+ *
+ * As a notational convenience, returns the same PathTarget pointer passed in.
+ */
+PathTarget *
+replace_pathtarget_cached_expressions(PathTarget *target, PlannerInfo *root)
+{
+ replace_cached_expressions_context context;
+
+ context.root = root;
+ context.innermost_nonconst_or_noncached_casetestexpr = NULL;
+ context.innermost_nonconst_or_noncached_coercetodomainvalue = NULL;
+
+ target->exprs = (List *) replace_cached_expressions_mutator(
+ (Node *) target->exprs, &context);
+
+ return target;
+}
+
+/*
+ * replace_qual_cached_expressions
+ * Replace cacehd expressions in a WHERE clause. The input can be either an
+ * implicitly-ANDed list of boolean expressions, or a list of RestrictInfo
+ * nodes.
+ */
+List *
+replace_qual_cached_expressions(List *quals, PlannerInfo *root)
+{
+ replace_cached_expressions_context context;
+
+ context.root = root;
+ context.innermost_nonconst_or_noncached_casetestexpr = NULL;
+ context.innermost_nonconst_or_noncached_coercetodomainvalue = NULL;
+
+ return (List *) replace_cached_expressions_mutator((Node *) quals,
+ &context);
+}
+
+/*
+ * Return true if node is null or const or CachedExpr or extern const parameter.
+ */
+static bool
+is_cached_or_const(Expr *node, ParamListInfoCommon boundParams)
+{
+ if (node == NULL || IsA(node, Const) || IsA(node, CachedExpr))
+ return true;
+
+ if (IsA(node, Param))
+ {
+ Param *param = (Param *) node;
+ int paramid = param->paramid;
+ ParamListInfoDataType boundParamsType;
+
+ if (param->paramkind != PARAM_EXTERN ||
+ paramid <= 0 ||
+ boundParams == NULL)
+ return false;
+
+ boundParamsType = boundParams->type;
+
+ switch (boundParamsType)
+ {
+ case PARAM_LIST_INFO_DATA:
+ {
+ ParamListInfo params = (ParamListInfo) boundParams;
+
+ return (paramid <= params->numParams &&
+ params->params[paramid - 1].pflags &
+ PARAM_FLAG_CONST);
+ }
+ case PARAM_LIST_INFO_PRECALCULATION_DATA:
+ {
+ ParamListInfoPrecalculation params =
+ (ParamListInfoPrecalculation) boundParams;
+
+ return (paramid <= params->numParams &&
+ params->isConstParam[paramid - 1]);
+ }
+ default:
+ elog(ERROR, "unrecognized ParamListInfoData type: %d",
+ (int) boundParamsType);
+ break;
+ }
+ }
+
+ return false;
+}
+
+static Node *
+replace_cached_expressions_mutator(Node *node,
+ replace_cached_expressions_context *context)
+{
+ ParamListInfoCommon boundParams = context->root->glob->boundParams;
+
+ if (node == NULL)
+ return NULL;
+
+ /* mutate certain types of nodes */
+ if (IsA(node, CachedExpr))
+ {
+ /* nothing to mutate */
+ return node;
+ }
+ else if (IsA(node, RestrictInfo))
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) node;
+
+ /*
+ * For an OR clause, recurse into the marked-up tree so that we replace
+ * cached expressions for contained RestrictInfos too.
+ */
+ if (rinfo->orclause)
+ rinfo->orclause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->orclause, context);
+ else
+ rinfo->clause = (Expr *) replace_cached_expressions_mutator(
+ (Node *) rinfo->clause, context);
+
+ /* do NOT recurse into children */
+ return node;
+ }
+ else if (IsA(node, ArrayRef))
+ {
+ /*
+ * ArrayRef is cached if all its subexpressions (refupperindexpr etc.)
+ * are consts or cached expressions too. (it returns array or array
+ * single element so we don't need to check if it returns set; and
+ * knowing its inputs its behaviour is quite defined so we don't need to
+ * check volatility)
+ */
+ ArrayRef *aref = (ArrayRef *) node;
+ ListCell *indexpr;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ aref = (ArrayRef *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ /* check expressions of upper array indexes */
+ foreach(indexpr, aref->refupperindexpr)
+ {
+ if (!is_cached_or_const(lfirst(indexpr), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ /* check expressions of lower array indexes */
+ foreach(indexpr, aref->reflowerindexpr)
+ {
+ if (!is_cached_or_const(lfirst(indexpr), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ /* check expression of array value */
+ if (!is_cached_or_const(aref->refexpr, boundParams))
+ has_nonconst_or_noncached_input = true;
+
+ /* check expression of source value */
+ if (!is_cached_or_const(aref->refassgnexpr, boundParams))
+ has_nonconst_or_noncached_input = true;
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return ArrayRef, which will not be cached */
+ return (Node *) aref;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) aref;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, FuncExpr))
+ {
+ /*
+ * Function is cached if:
+ * 1) it doesn't return set,
+ * 2) it's not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ FuncExpr *funcexpr;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool func_returns_set;
+
+ /* firstly recurse into children */
+ funcexpr = (FuncExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ func_returns_set = funcexpr->funcretset ||
+ expression_returns_set((Node *) funcexpr->args);
+
+ foreach(arg, funcexpr->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (func_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) funcexpr))
+ {
+ /* return FuncExpr, which will not be cached */
+ return (Node *) funcexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) funcexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, OpExpr))
+ {
+ /*
+ * Operator is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ OpExpr *opexpr = (OpExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid(opexpr);
+
+ /* firstly recurse into children */
+ opexpr = (OpExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ op_returns_set = opexpr->opretset ||
+ expression_returns_set((Node *) opexpr->args);
+
+ foreach(arg, opexpr->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) opexpr))
+ {
+ /* return OpExpr, which will not be cached */
+ return (Node *) opexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) opexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, DistinctExpr))
+ {
+ /*
+ * Operator of DistinctExpr is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ DistinctExpr *distinctexpr = (DistinctExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid((OpExpr *) distinctexpr);
+
+ /* firstly recurse into children */
+ distinctexpr = (DistinctExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ op_returns_set = distinctexpr->opretset ||
+ expression_returns_set((Node *) distinctexpr->args);
+
+ foreach(arg, distinctexpr->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) distinctexpr))
+ {
+ /* return DistinctExpr, which will not be cached */
+ return (Node *) distinctexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) distinctexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, NullIfExpr))
+ {
+ /*
+ * Operator of NullIfExpr is cached if:
+ * 1) its function doesn't return set,
+ * 1) its function is not volatile itself,
+ * 3) its arguments are constants or cached expressions too.
+ */
+ NullIfExpr *nullifexpr = (NullIfExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+ bool op_returns_set;
+
+ /* rely on struct equivalence to treat these all alike */
+ set_opfuncid((OpExpr *) nullifexpr);
+
+ /* firstly recurse into children */
+ nullifexpr = (NullIfExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+ op_returns_set = nullifexpr->opretset ||
+ expression_returns_set((Node *) nullifexpr->args);
+
+ foreach(arg, nullifexpr->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (op_returns_set ||
+ has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) nullifexpr))
+ {
+ /* return NullIfExpr, which will not be cached */
+ return (Node *) nullifexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) nullifexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ /*
+ * Operator of ScalarArrayOpExpr is cached if:
+ * 1) its function is not volatile itself,
+ * 2) its arguments are constants or cached expressions too.
+ * (it returns boolean so we don't need to check if it returns set)
+ */
+ ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ set_sa_opfuncid(saopexpr);
+
+ /* firstly recurse into children */
+ saopexpr = (ScalarArrayOpExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, saopexpr->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) saopexpr))
+ {
+ /* return ScalarArrayOpExpr, which will not be cached */
+ return (Node *) saopexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) saopexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, BoolExpr))
+ {
+ /*
+ * BoolExpr is cached if its arguments are constants or cached
+ * expressions too. (it returns boolean so we don't need to check if it
+ * returns set; and its too simple for evaluation so we don't need to
+ * check volatility)
+ */
+ BoolExpr *boolexpr = (BoolExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ boolexpr = (BoolExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, boolexpr->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return BoolExpr, which will not be cached */
+ return (Node *) boolexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) boolexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, FieldSelect))
+ {
+ /*
+ * FieldSelect is cached if its argument is const or cached expression
+ * too. (it returns one field from a tuple value so we don't need to
+ * check if it returns set; and knowing its argument its behaviour is
+ * quite defined so we don't need to check volatility)
+ */
+ FieldSelect *fselect = (FieldSelect *) node;
+
+ /* firstly recurse into children */
+ fselect = (FieldSelect *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ if (!is_cached_or_const(fselect->arg, boundParams))
+ {
+ /* return FieldSelect, which will not be cached */
+ return (Node *) fselect;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) fselect;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, RelabelType))
+ {
+ /*
+ * RelabelType is cached if its argument is const or cached expression
+ * too. (it returns its argument so we don't need to check if it returns
+ * set; and it is a no-op at runtime so we don't need to check
+ * volatility)
+ */
+ RelabelType *relabel = (RelabelType *) node;
+
+ /* firstly recurse into children */
+ relabel = (RelabelType *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ if (!is_cached_or_const(relabel->arg, boundParams))
+ {
+ /* return RelabelType, which will not be cached */
+ return (Node *) relabel;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) relabel;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CoerceViaIO))
+ {
+ /*
+ * CoerceViaIO is cached if:
+ * 1) its argument is const or cached expression too,
+ * 2) the source type's typoutput function and the destination type's
+ * typinput function are not volatile themselves.
+ * (it returns its argument with a type coercion so we don't need
+ * to check if it returns set)
+ */
+ CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+
+ /* firstly recurse into children */
+ iocoerce = (CoerceViaIO *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ if (!is_cached_or_const(iocoerce->arg, boundParams) ||
+ contain_volatile_functions((Node *) iocoerce))
+ {
+ /* return CoerceViaIO, which will not be cached */
+ return (Node *) iocoerce;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) iocoerce;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ArrayCoerceExpr))
+ {
+ /*
+ * ArrayCoerceExpr is cached if:
+ * 1) its element-type coercion expression can be cached,
+ * 2) its argument is const or cached expression too.
+ * (it returns its argument with a type coercion so we don't need to
+ * check if it returns set)
+ */
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+ Expr *elemexpr;
+ bool has_nonconst_or_noncached_input = false;
+ bool nonconst_or_noncached_arg;
+ replace_cached_expressions_context new_context;
+
+ /* firstly recurse into children */
+ acoerce = (ArrayCoerceExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ /* check arg and fill new context */
+ nonconst_or_noncached_arg = !is_cached_or_const(acoerce->arg,
+ boundParams);
+ has_nonconst_or_noncached_input = nonconst_or_noncached_arg;
+
+ new_context.root = context->root;
+ new_context.innermost_nonconst_or_noncached_casetestexpr =
+ &nonconst_or_noncached_arg;
+ new_context.innermost_nonconst_or_noncached_coercetodomainvalue =
+ context->innermost_nonconst_or_noncached_coercetodomainvalue;
+
+ /*
+ * Recurse into element-type coercion expression with new context (it
+ * will be used by CaseTestExpr if it's used here).
+ */
+ elemexpr = (Expr *) replace_cached_expressions_mutator(
+ (Node *) acoerce->elemexpr,
+ (void *) &new_context);
+
+ /* check elemexpr */
+ if (!is_cached_or_const(elemexpr, boundParams))
+ has_nonconst_or_noncached_input = true;
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return ArrayCoerceExpr, which will not be cached */
+ return (Node *) acoerce;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) acoerce;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ConvertRowtypeExpr))
+ {
+ /*
+ * ConvertRowtypeExpr is cached if its argument is const or cached
+ * expression too. (it returns its argument (row) maybe without values
+ * of some columns so we don't need to check if it returns set; and
+ * knowing its argument its behaviour is quite defined so we don't need
+ * to check volatility)
+ */
+ ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node;
+
+ /* firstly recurse into children */
+ convexpr = (ConvertRowtypeExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ if (!is_cached_or_const(convexpr->arg, boundParams))
+ {
+ /* return ConvertRowtypeExpr, which will not be cached */
+ return (Node *) convexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) convexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CaseExpr))
+ {
+ /*
+ * CaseExpr is cached if all its arguments (= implicit equality
+ * comparison argument and WHEN clauses) and the default result
+ * expression are consts or cached expressions too. (it returns one of
+ * its arguments or the default result so we don't need to check if it
+ * returns set; and knowing its arguments its behaviour is quite defined
+ * so we don't need to check volatility)
+ */
+ CaseExpr *caseexpr = (CaseExpr *) node;
+ CaseExpr *new_caseexpr;
+ bool has_nonconst_or_noncached_input = false;
+ bool nonconst_or_noncached_testvalue;
+ replace_cached_expressions_context new_context;
+ ListCell *cell;
+
+ /*
+ * Recurse into node manually because we will need different context for
+ * its subnodes.
+ */
+ new_caseexpr = (CaseExpr *) palloc(sizeof(CaseExpr));
+ memcpy((new_caseexpr), (caseexpr), sizeof(CaseExpr));
+
+ /* recurse into arg */
+ new_caseexpr->arg = (Expr *) replace_cached_expressions_mutator(
+ (Node *) new_caseexpr->arg,
+ context);
+
+ /* check arg and fill new context */
+ nonconst_or_noncached_testvalue = !is_cached_or_const(new_caseexpr->arg,
+ boundParams);
+ has_nonconst_or_noncached_input = nonconst_or_noncached_testvalue;
+
+ new_context.root = context->root;
+ new_context.innermost_nonconst_or_noncached_casetestexpr =
+ &nonconst_or_noncached_testvalue;
+ new_context.innermost_nonconst_or_noncached_coercetodomainvalue =
+ context->innermost_nonconst_or_noncached_coercetodomainvalue;
+
+ /*
+ * Recurse into args with new context (it will be used by CaseTestExpr
+ * if it's used in current WHEN clauses subtrees).
+ */
+ new_caseexpr->args = (List *) replace_cached_expressions_mutator(
+ (Node *) new_caseexpr->args,
+ (void *) &new_context);
+
+ /* check args */
+ foreach(cell, new_caseexpr->args)
+ {
+ CaseWhen *casewhen = lfirst(cell);
+
+ if (!is_cached_or_const(casewhen->expr, boundParams) ||
+ !is_cached_or_const(casewhen->result, boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ /* recurse into defresult */
+ new_caseexpr->defresult = (Expr *) replace_cached_expressions_mutator(
+ (Node *) new_caseexpr->defresult,
+ context);
+
+ /* check defresult */
+ if (!is_cached_or_const(new_caseexpr->defresult, boundParams))
+ has_nonconst_or_noncached_input = true;
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return CaseExpr, which will not be cached */
+ return (Node *) new_caseexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) new_caseexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CaseTestExpr))
+ {
+ /*
+ * CaseTestExpr is cached if we got in context that it is in CaseExpr/
+ * ArrayCoerceExpr and arg of innermost CaseExpr/ArrayCoerceExpr is
+ * const or cached expression too. (it is a placeholder node for the
+ * test value of its CaseExpr/source element of its ArrayCoerceExpr so
+ * we don't need to check if it returns set and we don't need to check
+ * volatility)
+ */
+ CaseTestExpr *casetest = (CaseTestExpr *) node;
+
+ if (!context->innermost_nonconst_or_noncached_casetestexpr ||
+ *(context->innermost_nonconst_or_noncached_casetestexpr))
+ {
+ /* return CaseTestExpr, which will not be cached */
+ return (Node *) casetest;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) casetest;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, ArrayExpr))
+ {
+ /*
+ * ArrayExpr is cached if its elements are consts or cached expressions
+ * too. (it returns array so we don't need to check if it returns set;
+ * and knowing its elements its behaviour is quite defined so we don't
+ * need to check volatility)
+ */
+ ArrayExpr *arrayexpr = (ArrayExpr *) node;
+ ListCell *element;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ arrayexpr = (ArrayExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(element, arrayexpr->elements)
+ {
+ if (!is_cached_or_const(lfirst(element), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return ArrayExpr, which will not be cached */
+ return (Node *) arrayexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) arrayexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, RowExpr))
+ {
+ /*
+ * RowExpr is cached if its arguments are consts or cached expressions
+ * too. (it returns tuple so we don't need to check if it returns set;
+ * and knowing its arguments its behaviour is quite defined so we don't
+ * need to check volatility)
+ */
+ RowExpr *rowexpr = (RowExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ rowexpr = (RowExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, rowexpr->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return RowExpr, which will not be cached */
+ return (Node *) rowexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) rowexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, RowCompareExpr))
+ {
+ /*
+ * RowCompareExpr is cached if:
+ * 1) its pairwise comparison operators are not volatile themselves,
+ * 2) its arguments are consts or cached expressions too.
+ * (it returns boolean so we don't need to check if it returns set)
+ */
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ rcexpr = (RowCompareExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, rcexpr->largs)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ foreach(arg, rcexpr->rargs)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input ||
+ contain_volatile_functions((Node *) rcexpr))
+ {
+ /* return RowCompareExpr, which will not be cached */
+ return (Node *) rcexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) rcexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CoalesceExpr))
+ {
+ /*
+ * CoalesceExpr is cached if its arguments are consts or cached
+ * expressions too. (it returns one of its arguments so we don't need to
+ * check if it returns set; and knowing its arguments its behaviour is
+ * quite defined so we don't need to check volatility)
+ */
+ CoalesceExpr *coalesce = (CoalesceExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ coalesce = (CoalesceExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, coalesce->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return CoalesceExpr, which will not be cached */
+ return (Node *) coalesce;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) coalesce;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, MinMaxExpr))
+ {
+ /*
+ * MinMaxExpr is cached if its arguments are consts or cached
+ * expressions too. (it returns one of its arguments so we don't need to
+ * check if it returns set; and it uses btree comparison functions so we
+ * don't need to check volatility)
+ */
+ MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ minmaxexpr = (MinMaxExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, minmaxexpr->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return MinMaxExpr, which will not be cached */
+ return (Node *) minmaxexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) minmaxexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, SQLValueFunction))
+ {
+ /*
+ * SQLValueFunction is always cached because all of these functions
+ * don't return a set and are stable.
+ */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) node;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ else if (IsA(node, XmlExpr))
+ {
+ /*
+ * XmlExpr is cached if all its arguments are consts or cached
+ * expressions too. (it returns values of different types so we don't
+ * need to check if it returns set; and knowing its arguments its
+ * behaviour is quite defined so we don't need to check volatility)
+ */
+ XmlExpr *xexpr = (XmlExpr *) node;
+ ListCell *arg;
+ bool has_nonconst_or_noncached_input = false;
+
+ /* firstly recurse into children */
+ xexpr = (XmlExpr *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ foreach(arg, xexpr->named_args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ foreach(arg, xexpr->args)
+ {
+ if (!is_cached_or_const(lfirst(arg), boundParams))
+ has_nonconst_or_noncached_input = true;
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return XmlExpr, which will not be cached */
+ return (Node *) xexpr;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) xexpr;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, NullTest))
+ {
+ /*
+ * NullTest is cached if its argument is const or cached expression too.
+ * (it returns boolean so we don't need to check if it returns set; and
+ * knowing its argument its behaviour is quite defined so we don't need
+ * to check volatility)
+ */
+ NullTest *nulltest = (NullTest *) node;
+
+ /* firstly recurse into children */
+ nulltest = (NullTest *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ if (!is_cached_or_const(nulltest->arg, boundParams))
+ {
+ /* return NullTest, which will not be cached */
+ return (Node *) nulltest;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) nulltest;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, BooleanTest))
+ {
+ /*
+ * BooleanTest is cached if its argument is const or cached expression
+ * too. (it returns boolean so we don't need to check if it returns set;
+ * and knowing its argument its behaviour is quite defined so we don't
+ * need to check volatility)
+ */
+ BooleanTest *btest = (BooleanTest *) node;
+
+ /* firstly recurse into children */
+ btest = (BooleanTest *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ if (!is_cached_or_const(btest->arg, boundParams))
+ {
+ /* return BooleanTest, which will not be cached */
+ return (Node *) btest;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) btest;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CoerceToDomain))
+ {
+ /*
+ * CoerceToDomain is cached if:
+ * 1) its constraints can be cached,
+ * 2) its argument is const or cached expression too.
+ * (it returns its argument coercing a value to a domain type so we
+ * don't need to check if it returns set)
+ */
+ CoerceToDomain *ctest = (CoerceToDomain *) node;
+ bool has_nonconst_or_noncached_input = false;
+ bool nonconst_or_noncached_value;
+ replace_cached_expressions_context new_context;
+ DomainConstraintRef *constraint_ref;
+ List *constraints;
+ ListCell *cell;
+
+ /* firstly recurse into children */
+ ctest = (CoerceToDomain *) expression_tree_mutator(
+ node,
+ replace_cached_expressions_mutator,
+ (void *) context);
+
+ /* check arg and fill new context */
+ nonconst_or_noncached_value = !is_cached_or_const(ctest->arg,
+ boundParams);
+ has_nonconst_or_noncached_input = nonconst_or_noncached_value;
+
+ new_context.root = context->root;
+ new_context.innermost_nonconst_or_noncached_casetestexpr =
+ context->innermost_nonconst_or_noncached_casetestexpr;
+ new_context.innermost_nonconst_or_noncached_coercetodomainvalue =
+ &nonconst_or_noncached_value;
+
+ /* get constraints and recurse into them with new context */
+ constraint_ref = (DomainConstraintRef *)
+ palloc(sizeof(DomainConstraintRef));
+ InitDomainConstraintRef(ctest->resulttype,
+ constraint_ref,
+ context->root->planner_cxt,
+ false);
+ constraints = GetDomainConstraintExprList(constraint_ref);
+ foreach(cell, constraints)
+ {
+ DomainConstraintExpr *con = (DomainConstraintExpr *) lfirst(cell);
+ Expr *check_expr = con->check_expr;
+
+ switch (con->constrainttype)
+ {
+ case DOM_CONSTRAINT_NOTNULL:
+ /* OK */
+ break;
+ case DOM_CONSTRAINT_CHECK:
+ check_expr = (Expr *) replace_cached_expressions_mutator(
+ (Node *) check_expr,
+ &new_context);
+ if (!is_cached_or_const(check_expr, boundParams))
+ has_nonconst_or_noncached_input = true;
+ break;
+ default:
+ elog(ERROR, "unrecognized constraint type: %d",
+ (int) con->constrainttype);
+ break;
+ }
+ }
+
+ if (has_nonconst_or_noncached_input)
+ {
+ /* return RowCompareExpr, which will not be cached */
+ return (Node *) ctest;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) ctest;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+ else if (IsA(node, CoerceToDomainValue))
+ {
+ /*
+ * CoerceToDomainValue is cached if we got in context that it is in
+ * CoerceToDomain expression and arg of innermost CoerceToDomain
+ * expression is const or cached expression too. (it is a placeholder
+ * node for the value of its CoerceToDomain expression so we don't need
+ * to check if it returns set and we don't need to check volatility)
+ */
+ CoerceToDomainValue *domval = (CoerceToDomainValue *) node;
+
+ if (!context->innermost_nonconst_or_noncached_coercetodomainvalue ||
+ *(context->innermost_nonconst_or_noncached_coercetodomainvalue))
+ {
+ /* return CoerceToDomainValue, which will not be cached */
+ return (Node *) domval;
+ }
+ else
+ {
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) domval;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
+ }
+ }
+
+ /* otherwise recurse into children */
+ return expression_tree_mutator(node, replace_cached_expressions_mutator,
+ (void *) context);
+}
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 1d7e499..099d7fb 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -918,6 +918,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->hasRecursion = false;
subroot->wt_param_id = -1;
subroot->non_recursive_path = NULL;
+ subroot->hasCachedExpr = false;
/* No CTEs to worry about */
Assert(subquery->cteList == NIL);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 9ca384d..5cacff9 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -62,7 +62,7 @@ typedef struct
typedef struct
{
- ParamListInfo boundParams;
+ ParamListInfoCommon boundParams;
PlannerInfo *root;
List *active_fns;
Node *case_val;
@@ -980,6 +980,11 @@ contain_volatile_functions_walker(Node *node, void *context)
/* NextValueExpr is volatile */
return true;
}
+ else if (IsA(node, CachedExpr))
+ {
+ /* CachedExpr is not volatile */
+ return false;
+ }
/*
* See notes in contain_mutable_functions_walker about why we treat
@@ -2513,7 +2518,13 @@ eval_const_expressions_mutator(Node *node,
case T_Param:
{
Param *param = (Param *) node;
- ParamListInfo paramLI = context->boundParams;
+ ParamListInfo paramLI;
+
+ if (context->boundParams != NULL &&
+ context->boundParams->type == PARAM_LIST_INFO_DATA)
+ paramLI = (ParamListInfo) context->boundParams;
+ else
+ paramLI = NULL;
/* Look to see if we've been given a value for this Param */
if (param->paramkind == PARAM_EXTERN &&
@@ -2625,6 +2636,32 @@ eval_const_expressions_mutator(Node *node,
return (Node *) newexpr;
}
+ case T_CachedExpr:
+ {
+ CachedExpr *cachedexpr = (CachedExpr *) node;
+ CacheableExpr *new_subexpr = (CacheableExpr *)
+ eval_const_expressions_mutator((Node *) cachedexpr->subexpr,
+ context);
+ CachedExpr *new_cachedexpr;
+
+ if (IsA(new_subexpr, Const))
+ {
+ /* successfully simplified it */
+ return (Node *) new_subexpr;
+ }
+ else
+ {
+ /*
+ * The expression cannot be simplified any further, so build
+ * and return a replacement CachedExpr node using the
+ * possibly-simplified arguments of subexpression.
+ */
+ new_cachedexpr = makeNode(CachedExpr);
+ new_cachedexpr->subexpr = new_subexpr;
+
+ return (Node *) new_cachedexpr;
+ }
+ }
case T_FuncExpr:
{
FuncExpr *expr = (FuncExpr *) node;
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 4c4f4cd..9c51285 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -1111,6 +1111,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
root->parse = qry;
root->planner_cxt = CurrentMemoryContext;
root->hasJoinRTEs = true;
+ root->hasCachedExpr = false;
groupClauses = (List *) flatten_join_alias_vars(root,
(Node *) groupClauses);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 1b24ddd..d7477d4 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -787,7 +787,8 @@ pg_rewrite_query(Query *query)
* This is a thin wrapper around planner() and takes the same parameters.
*/
PlannedStmt *
-pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
+pg_plan_query(Query *querytree, int cursorOptions,
+ ParamListInfoCommon boundParams)
{
PlannedStmt *plan;
@@ -848,7 +849,8 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
* The result is a list of PlannedStmt nodes.
*/
List *
-pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
+pg_plan_queries(List *querytrees, int cursorOptions,
+ ParamListInfoCommon boundParams)
{
List *stmt_list = NIL;
ListCell *query_list;
@@ -867,6 +869,7 @@ pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
stmt->utilityStmt = query->utilityStmt;
stmt->stmt_location = query->stmt_location;
stmt->stmt_len = query->stmt_len;
+ stmt->hasCachedExpr = false;
}
else
{
@@ -1644,6 +1647,7 @@ exec_bind_message(StringInfo input_message)
params = (ParamListInfo) palloc(offsetof(ParamListInfoData, params) +
numParams * sizeof(ParamExternData));
/* we have static list of params, so no hooks needed */
+ params->common.type = PARAM_LIST_INFO_DATA;
params->paramFetch = NULL;
params->paramFetchArg = NULL;
params->paramCompile = NULL;
@@ -1794,7 +1798,8 @@ exec_bind_message(StringInfo input_message)
* will be generated in MessageContext. The plan refcount will be
* assigned to the Portal, so it will be released at portal destruction.
*/
- cplan = GetCachedPlan(psrc, params, false, NULL);
+ cplan = GetCachedPlan(psrc, (ParamListInfoCommon) params, false, NULL,
+ false);
/*
* Now we can define the portal.
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4da1f8f..db31839 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1068,6 +1068,7 @@ ProcessUtilitySlow(ParseState *pstate,
wrapper->utilityStmt = stmt;
wrapper->stmt_location = pstmt->stmt_location;
wrapper->stmt_len = pstmt->stmt_len;
+ wrapper->hasCachedExpr = false;
ProcessUtility(wrapper,
queryString,
@@ -1148,6 +1149,7 @@ ProcessUtilitySlow(ParseState *pstate,
wrapper->utilityStmt = stmt;
wrapper->stmt_location = pstmt->stmt_location;
wrapper->stmt_len = pstmt->stmt_len;
+ wrapper->hasCachedExpr = false;
ProcessUtility(wrapper,
queryString,
PROCESS_UTILITY_SUBCOMMAND,
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
index 86f916f..ff7586a 100644
--- a/src/backend/utils/adt/domains.c
+++ b/src/backend/utils/adt/domains.c
@@ -138,7 +138,8 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
foreach(l, my_extra->constraint_ref.constraints)
{
- DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+ DomainConstraintState *con_state = (DomainConstraintState *) lfirst(l);
+ DomainConstraintExpr *con = con_state->expr;
switch (con->constrainttype)
{
@@ -178,7 +179,7 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
my_extra->constraint_ref.tcache->typlen);
econtext->domainValue_isNull = isnull;
- if (!ExecCheck(con->check_exprstate, econtext))
+ if (!ExecCheck(con_state->check_exprstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("value for domain %s violates check constraint \"%s\"",
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8514c21..2e72e74 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7661,6 +7661,11 @@ get_rule_expr(Node *node, deparse_context *context,
get_windowfunc_expr((WindowFunc *) node, context);
break;
+ case T_CachedExpr:
+ get_rule_expr((Node *) ((CachedExpr *) node)->subexpr, context,
+ showimplicit);
+ break;
+
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 853c1f6..25e4c7e 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -90,11 +90,12 @@ static CachedPlanSource *first_saved_plan = NULL;
static void ReleaseGenericPlan(CachedPlanSource *plansource);
static List *RevalidateCachedQuery(CachedPlanSource *plansource,
QueryEnvironment *queryEnv);
-static bool CheckCachedPlan(CachedPlanSource *plansource);
+static bool CheckCachedPlan(CachedPlanSource *plansource,
+ ParamListInfoPrecalculation boundParams);
static CachedPlan *BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
- ParamListInfo boundParams, QueryEnvironment *queryEnv);
+ ParamListInfoCommon boundParams, QueryEnvironment *queryEnv);
static bool choose_custom_plan(CachedPlanSource *plansource,
- ParamListInfo boundParams);
+ ParamListInfoCommon boundParams);
static double cached_plan_cost(CachedPlan *plan, bool include_planner);
static Query *QueryListGetPrimaryStmt(List *stmts);
static void AcquireExecutorLocks(List *stmt_list, bool acquire);
@@ -785,6 +786,65 @@ RevalidateCachedQuery(CachedPlanSource *plansource,
}
/*
+ * CheckNotConst: check if the bound params are not constants.
+ */
+static bool
+CheckNotConst(ParamListInfoPrecalculation boundParams, int start_index)
+{
+ int index;
+
+ if (boundParams == NULL)
+ return true;
+
+ if (start_index < 0)
+ start_index = 0;
+
+ for (index = start_index; index < boundParams->numParams; ++index)
+ {
+ if (boundParams->isConstParam[index])
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * CheckBoundParams: check if bound params are compatible in the generic plan.
+ */
+static bool
+CheckBoundParams(ParamListInfoPrecalculation firstBoundParams,
+ ParamListInfoPrecalculation secondBoundParams)
+{
+ int numCommonParams,
+ index;
+
+ if (firstBoundParams == NULL || secondBoundParams == NULL)
+ numCommonParams = 0;
+ else if (firstBoundParams->numParams <= secondBoundParams->numParams)
+ numCommonParams = firstBoundParams->numParams;
+ else
+ numCommonParams = secondBoundParams->numParams;
+
+ /*
+ * Check that the common parameters are equal (both are constants or both
+ * are not).
+ */
+ for (index = 0; index < numCommonParams; ++index)
+ {
+ if (firstBoundParams->isConstParam[index] !=
+ secondBoundParams->isConstParam[index])
+ return false;
+ }
+
+ /*
+ * Check that the other parameters are not constants, so they have not
+ * previously been precalculated and will not be precalculated.
+ */
+ return (CheckNotConst(firstBoundParams, numCommonParams) &&
+ CheckNotConst(secondBoundParams, numCommonParams));
+}
+
+/*
* CheckCachedPlan: see if the CachedPlanSource's generic plan is valid.
*
* Caller must have already called RevalidateCachedQuery to verify that the
@@ -794,7 +854,8 @@ RevalidateCachedQuery(CachedPlanSource *plansource,
* (We must do this for the "true" result to be race-condition-free.)
*/
static bool
-CheckCachedPlan(CachedPlanSource *plansource)
+CheckCachedPlan(CachedPlanSource *plansource,
+ ParamListInfoPrecalculation boundParams)
{
CachedPlan *plan = plansource->gplan;
@@ -845,8 +906,10 @@ CheckCachedPlan(CachedPlanSource *plansource)
*/
if (plan->is_valid)
{
- /* Successfully revalidated and locked the query. */
- return true;
+ /*
+ * Successfully revalidated and locked the query. Check boundParams.
+ */
+ return CheckBoundParams(plan->boundParams, boundParams);
}
/* Oops, the race case happened. Release useless locks. */
@@ -879,7 +942,7 @@ CheckCachedPlan(CachedPlanSource *plansource)
*/
static CachedPlan *
BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
- ParamListInfo boundParams, QueryEnvironment *queryEnv)
+ ParamListInfoCommon boundParams, QueryEnvironment *queryEnv)
{
CachedPlan *plan;
List *plist;
@@ -1002,6 +1065,35 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
plan->is_saved = false;
plan->is_valid = true;
+ /*
+ * Specify the precalculation parameters. If the plan is not one-shot, make
+ * a deep copy.
+ */
+ if (boundParams != NULL &&
+ boundParams->type == PARAM_LIST_INFO_PRECALCULATION_DATA)
+ {
+ ParamListInfoPrecalculation params =
+ (ParamListInfoPrecalculation) boundParams;
+
+ if (!plansource->is_oneshot)
+ {
+ plan->boundParams = (ParamListInfoPrecalculation) palloc(
+ offsetof(ParamListInfoPrecalculationData, isConstParam) +
+ params->numParams * sizeof(bool));
+
+ memcpy(plan->boundParams, params,
+ sizeof(ParamListInfoPrecalculationData));
+ }
+ else
+ {
+ plan->boundParams = params;
+ }
+ }
+ else
+ {
+ plan->boundParams = NULL;
+ }
+
/* assign generation number to new plan */
plan->generation = ++(plansource->generation);
@@ -1016,7 +1108,8 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
* This defines the policy followed by GetCachedPlan.
*/
static bool
-choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
+choose_custom_plan(CachedPlanSource *plansource,
+ ParamListInfoCommon boundParams)
{
double avg_custom_cost;
@@ -1025,7 +1118,8 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
return true;
/* Otherwise, never any point in a custom plan if there's no parameters */
- if (boundParams == NULL)
+ if (boundParams == NULL ||
+ boundParams->type != PARAM_LIST_INFO_DATA)
return false;
/* ... nor for transaction control statements */
if (IsTransactionStmtPlan(plansource))
@@ -1114,6 +1208,50 @@ cached_plan_cost(CachedPlan *plan, bool include_planner)
}
/*
+ * GetPrecalculationData: get ParamListInfoPrecalculationData from the source.
+ */
+static ParamListInfoPrecalculation
+GetPrecalculationData(ParamListInfoCommon boundParams)
+{
+ ParamListInfoPrecalculation result;
+ ParamListInfoDataType boundParamsType;
+
+ if (boundParams == NULL)
+ return NULL;
+
+ boundParamsType = boundParams->type;
+
+ switch (boundParamsType)
+ {
+ case PARAM_LIST_INFO_DATA:
+ {
+ ParamListInfo params = (ParamListInfo) boundParams;
+ int index;
+
+ result = (ParamListInfoPrecalculation) palloc(
+ offsetof(ParamListInfoPrecalculationData, isConstParam) +
+ params->numParams * sizeof(bool));
+
+ result->common.type = PARAM_LIST_INFO_PRECALCULATION_DATA;
+ result->numParams = params->numParams;
+ for (index = 0; index < params->numParams; ++index)
+ result->isConstParam[index] =
+ params->params[index].pflags & PARAM_FLAG_CONST;
+ }
+ break;
+ case PARAM_LIST_INFO_PRECALCULATION_DATA:
+ result = (ParamListInfoPrecalculation) boundParams;
+ break;
+ default:
+ elog(ERROR, "unrecognized ParamListInfoData type: %d",
+ (int) boundParamsType);
+ break;
+ }
+
+ return result;
+}
+
+/*
* GetCachedPlan: get a cached plan from a CachedPlanSource.
*
* This function hides the logic that decides whether to use a generic
@@ -1130,14 +1268,22 @@ cached_plan_cost(CachedPlan *plan, bool include_planner)
*
* Note: if any replanning activity is required, the caller's memory context
* is used for that work.
+ *
+ * Note: set genericPlanPrecalculateConstBoundParams to true only if you are
+ * sure that the bound parameters will remain (non)constant quite often for the
+ * next calls to this function. Otherwise the generic plan will be rebuilt each
+ * time when there's a bound parameter that was constant for the previous
+ * generic plan and is not constant now or vice versa.
*/
CachedPlan *
-GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
- bool useResOwner, QueryEnvironment *queryEnv)
+GetCachedPlan(CachedPlanSource *plansource, ParamListInfoCommon boundParams,
+ bool useResOwner, QueryEnvironment *queryEnv,
+ bool genericPlanPrecalculateConstBoundParams)
{
CachedPlan *plan = NULL;
List *qlist;
bool customplan;
+ ParamListInfoPrecalculation genericPlanBoundParams;
/* Assert caller is doing things in a sane order */
Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
@@ -1154,7 +1300,13 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
if (!customplan)
{
- if (CheckCachedPlan(plansource))
+ /* set the parameters for the generic plan */
+ if (genericPlanPrecalculateConstBoundParams)
+ genericPlanBoundParams = GetPrecalculationData(boundParams);
+ else
+ genericPlanBoundParams = NULL;
+
+ if (CheckCachedPlan(plansource, genericPlanBoundParams))
{
/* We want a generic plan, and we already have a valid one */
plan = plansource->gplan;
@@ -1163,7 +1315,9 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
else
{
/* Build a new generic plan */
- plan = BuildCachedPlan(plansource, qlist, NULL, queryEnv);
+ plan = BuildCachedPlan(plansource, qlist,
+ (ParamListInfoCommon) genericPlanBoundParams,
+ queryEnv);
/* Just make real sure plansource->gplan is clear */
ReleaseGenericPlan(plansource);
/* Link the new generic plan into the plansource */
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index f6450c4..b40bed9 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -946,7 +946,8 @@ load_domaintype_info(TypeCacheEntry *typentry)
bool isNull;
char *constring;
Expr *check_expr;
- DomainConstraintState *r;
+ DomainConstraintState *r_state;
+ DomainConstraintExpr *r;
/* Ignore non-CHECK constraints (presently, shouldn't be any) */
if (c->contype != CONSTRAINT_CHECK)
@@ -985,11 +986,14 @@ load_domaintype_info(TypeCacheEntry *typentry)
/* ExecInitExpr will assume we've planned the expression */
check_expr = expression_planner(check_expr);
- r = makeNode(DomainConstraintState);
+ r_state = makeNode(DomainConstraintState);
+ r_state->expr = makeNode(DomainConstraintExpr);
+ r = r_state->expr;
+
r->constrainttype = DOM_CONSTRAINT_CHECK;
r->name = pstrdup(NameStr(c->conname));
r->check_expr = check_expr;
- r->check_exprstate = NULL;
+ r_state->check_exprstate = NULL;
MemoryContextSwitchTo(oldcxt);
@@ -1006,7 +1010,7 @@ load_domaintype_info(TypeCacheEntry *typentry)
ccons = (DomainConstraintState **)
repalloc(ccons, cconslen * sizeof(DomainConstraintState *));
}
- ccons[nccons++] = r;
+ ccons[nccons++] = r_state;
}
systable_endscan(scan);
@@ -1043,7 +1047,8 @@ load_domaintype_info(TypeCacheEntry *typentry)
*/
if (notNull)
{
- DomainConstraintState *r;
+ DomainConstraintState *r_state;
+ DomainConstraintExpr *r;
/* Create the DomainConstraintCache object and context if needed */
if (dcc == NULL)
@@ -1063,15 +1068,17 @@ load_domaintype_info(TypeCacheEntry *typentry)
/* Create node trees in DomainConstraintCache's context */
oldcxt = MemoryContextSwitchTo(dcc->dccContext);
- r = makeNode(DomainConstraintState);
+ r_state = makeNode(DomainConstraintState);
+ r_state->expr = makeNode(DomainConstraintExpr);
+ r = r_state->expr;
r->constrainttype = DOM_CONSTRAINT_NOTNULL;
r->name = pstrdup("NOT NULL");
r->check_expr = NULL;
- r->check_exprstate = NULL;
+ r_state->check_exprstate = NULL;
/* lcons to apply the nullness check FIRST */
- dcc->constraints = lcons(r, dcc->constraints);
+ dcc->constraints = lcons(r_state, dcc->constraints);
MemoryContextSwitchTo(oldcxt);
}
@@ -1100,7 +1107,7 @@ dcs_cmp(const void *a, const void *b)
const DomainConstraintState *const *ca = (const DomainConstraintState *const *) a;
const DomainConstraintState *const *cb = (const DomainConstraintState *const *) b;
- return strcmp((*ca)->name, (*cb)->name);
+ return strcmp((*ca)->expr->name, (*cb)->expr->name);
}
/*
@@ -1150,16 +1157,21 @@ prep_domain_constraints(List *constraints, MemoryContext execctx)
foreach(lc, constraints)
{
- DomainConstraintState *r = (DomainConstraintState *) lfirst(lc);
- DomainConstraintState *newr;
+ DomainConstraintState *r_state = (DomainConstraintState *) lfirst(lc);
+ DomainConstraintExpr *r = r_state->expr;
+ DomainConstraintState *newr_state;
+ DomainConstraintExpr *newr;
+
+ newr_state = makeNode(DomainConstraintState);
+ newr_state->expr = makeNode(DomainConstraintExpr);
+ newr = newr_state->expr;
- newr = makeNode(DomainConstraintState);
newr->constrainttype = r->constrainttype;
newr->name = r->name;
newr->check_expr = r->check_expr;
- newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
+ newr_state->check_exprstate = ExecInitExpr(r->check_expr, NULL);
- result = lappend(result, newr);
+ result = lappend(result, newr_state);
}
MemoryContextSwitchTo(oldcxt);
@@ -1278,6 +1290,22 @@ DomainHasConstraints(Oid type_id)
return (typentry->domainData != NULL);
}
+/*
+ * Return list of DomainConstraintExpr of DomainConstraintState elements of the
+ * given list.
+ */
+List *
+GetDomainConstraintExprList(DomainConstraintRef *ref)
+{
+ List *result = NIL;
+ ListCell *lc;
+
+ foreach(lc, ref->constraints)
+ result = lappend(result, ((DomainConstraintState *) lfirst(lc))->expr);
+
+ return result;
+}
+
/*
* array_element_has_equality and friends are helper routines to check
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 080252f..aedefdb 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -219,6 +219,16 @@ typedef enum ExprEvalOp
EEOP_SUBPLAN,
EEOP_ALTERNATIVE_SUBPLAN,
+ /*
+ * Evaluate CachedExpr. EEOP_CACHEDEXPR_IF_CACHED is used before
+ * subexpression evaluation (if subexpression was evaluated use cached value
+ * and jump to next state or get prepared to subexpression evaluation
+ * otherwise). EEOP_CACHEDEXPR_SUBEXPR_END is used after subexpression
+ * evaluation for caching its result.
+ */
+ EEOP_CACHEDEXPR_IF_CACHED,
+ EEOP_CACHEDEXPR_SUBEXPR_END,
+
/* non-existent operation, used e.g. to check array lengths */
EEOP_LAST
} ExprEvalOp;
@@ -574,6 +584,13 @@ typedef struct ExprEvalStep
/* out-of-line state, created by nodeSubplan.c */
AlternativeSubPlanState *asstate;
} alternative_subplan;
+
+ /* for EEOP_CACHEDEXPR_* */
+ struct
+ {
+ /* steps for evaluation the same CachedExpr have the same state */
+ struct CachedExprState *state;
+ } cachedexpr;
} d;
} ExprEvalStep;
@@ -614,6 +631,20 @@ typedef struct ArrayRefState
} ArrayRefState;
+/*
+ * Non-inline data for EEOP_CACHEDEXPR_* operations (steps for evaluation the
+ * same CachedExpr have the same state).
+ */
+typedef struct CachedExprState
+{
+ bool isExecuted;
+ bool resnull;
+ Datum resvalue;
+ Oid restypid; /* for copying resvalue of subexpression */
+ int jumpdone; /* jump here if result determined */
+} CachedExprState;
+
+
/* functions in execExpr.c */
extern void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index c9a5279..d21d5cb 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -801,25 +801,14 @@ typedef struct AlternativeSubPlanState
int active; /* list index of the one we're using */
} AlternativeSubPlanState;
-/*
- * DomainConstraintState - one item to check during CoerceToDomain
- *
- * Note: we consider this to be part of an ExprState tree, so we give it
- * a name following the xxxState convention. But there's no directly
- * associated plan-tree node.
+/* ----------------
+ * DomainConstraintState node
+ * ----------------
*/
-typedef enum DomainConstraintType
-{
- DOM_CONSTRAINT_NOTNULL,
- DOM_CONSTRAINT_CHECK
-} DomainConstraintType;
-
typedef struct DomainConstraintState
{
NodeTag type;
- DomainConstraintType constrainttype; /* constraint type */
- char *name; /* name of constraint (for error msgs) */
- Expr *check_expr; /* for CHECK, a boolean expression */
+ DomainConstraintExpr *expr; /* expression plan node */
ExprState *check_exprstate; /* check_expr's eval state, or NULL */
} DomainConstraintState;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c5b5115..31039be 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -149,6 +149,8 @@ typedef enum NodeTag
T_Aggref,
T_GroupingFunc,
T_WindowFunc,
+ T_CacheableExpr,
+ T_CachedExpr,
T_ArrayRef,
T_FuncExpr,
T_NamedArgExpr,
@@ -180,6 +182,7 @@ typedef enum NodeTag
T_NullTest,
T_BooleanTest,
T_CoerceToDomain,
+ T_DomainConstraintExpr,
T_CoerceToDomainValue,
T_SetToDefault,
T_CurrentOfExpr,
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index b198db5..ce45e3e 100644
--- a/src/include/nodes/params.h
+++ b/src/include/nodes/params.h
@@ -96,6 +96,8 @@ typedef struct ParamExternData
} ParamExternData;
typedef struct ParamListInfoData *ParamListInfo;
+typedef struct ParamListInfoCommonData *ParamListInfoCommon;
+typedef struct ParamListInfoPrecalculationData *ParamListInfoPrecalculation;
typedef ParamExternData *(*ParamFetchHook) (ParamListInfo params,
int paramid, bool speculative,
@@ -107,8 +109,20 @@ typedef void (*ParamCompileHook) (ParamListInfo params, struct Param *param,
typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);
+typedef enum ParamListInfoDataType
+{
+ PARAM_LIST_INFO_DATA, /* for ParamListInfoData */
+ PARAM_LIST_INFO_PRECALCULATION_DATA /* for ParamListInfoPrecalculationData */
+} ParamListInfoDataType;
+
+typedef struct ParamListInfoCommonData
+{
+ ParamListInfoDataType type;
+} ParamListInfoCommonData;
+
typedef struct ParamListInfoData
{
+ ParamListInfoCommonData common;
ParamFetchHook paramFetch; /* parameter fetch hook */
void *paramFetchArg;
ParamCompileHook paramCompile; /* parameter compile hook */
@@ -150,6 +164,14 @@ typedef struct ParamExecData
} ParamExecData;
+typedef struct ParamListInfoPrecalculationData
+{
+ ParamListInfoCommonData common;
+ int numParams; /* number of elements in isConstParam array */
+ bool isConstParam[FLEXIBLE_ARRAY_MEMBER];
+} ParamListInfoPrecalculationData;
+
+
/* Functions found in src/backend/nodes/params.c */
extern ParamListInfo copyParamList(ParamListInfo from);
extern Size EstimateParamListSpace(ParamListInfo paramLI);
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index d763da6..7a25c15 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -96,6 +96,8 @@ typedef struct PlannedStmt
/* statement location in source string (copied from Query) */
int stmt_location; /* start location, or -1 if unknown */
int stmt_len; /* length in bytes; 0 means "rest of string" */
+
+ bool hasCachedExpr; /* true if any expressions are cached */
} PlannedStmt;
/* macro for fetching the Plan associated with a SubPlan node */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 074ae0a..5fa1dff 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -364,6 +364,38 @@ typedef struct WindowFunc
int location; /* token location, or -1 if unknown */
} WindowFunc;
+/*
+ * CacheableExpr - generic suberclass for expressions that can be cacheable.
+ *
+ * All expression node types that can be cacheable should derive from
+ * CacheableExpr (that is, have CacheableExpr as their first field). Since
+ * CacheableExpr only contains NodeTag, this is a formality, but it is an easy
+ * form of documentation.
+ *
+ * Expression is cached (= is are calculated once for all output rows, but as
+ * many times as expression is mentioned in query), if:
+ * - it doesn't return a set
+ * - it is not volatile itself
+ * - its arguments are constants or recursively precalculated expressions.
+ *
+ * In planner if expression can be cached it becomes a part of CachedExpr node.
+ */
+typedef struct CacheableExpr
+{
+ NodeTag type;
+} CacheableExpr;
+
+/*
+ * CachedExpr - expression node for cached expressions (= they are calculated
+ * once for all output rows, but as many times as function is mentioned in
+ * query).
+ */
+typedef struct CachedExpr
+{
+ Expr xpr;
+ CacheableExpr *subexpr; /* expression to be cached */
+} CachedExpr;
+
/* ----------------
* ArrayRef: describes an array subscripting operation
*
@@ -395,7 +427,7 @@ typedef struct WindowFunc
*/
typedef struct ArrayRef
{
- Expr xpr;
+ CacheableExpr xpr;
Oid refarraytype; /* type of the array proper */
Oid refelemtype; /* type of the array elements */
int32 reftypmod; /* typmod of the array (and elements too) */
@@ -445,7 +477,7 @@ typedef enum CoercionForm
*/
typedef struct FuncExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid funcid; /* PG_PROC OID of the function */
Oid funcresulttype; /* PG_TYPE OID of result value */
bool funcretset; /* true if function returns set */
@@ -492,7 +524,7 @@ typedef struct NamedArgExpr
*/
typedef struct OpExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid opno; /* PG_OPERATOR OID of the operator */
Oid opfuncid; /* PG_PROC OID of underlying function */
Oid opresulttype; /* PG_TYPE OID of result value */
@@ -535,7 +567,7 @@ typedef OpExpr NullIfExpr;
*/
typedef struct ScalarArrayOpExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid opno; /* PG_OPERATOR OID of the operator */
Oid opfuncid; /* PG_PROC OID of underlying function */
bool useOr; /* true for ANY, false for ALL */
@@ -558,7 +590,7 @@ typedef enum BoolExprType
typedef struct BoolExpr
{
- Expr xpr;
+ CacheableExpr xpr;
BoolExprType boolop;
List *args; /* arguments to this expression */
int location; /* token location, or -1 if unknown */
@@ -738,7 +770,7 @@ typedef struct AlternativeSubPlan
typedef struct FieldSelect
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
AttrNumber fieldnum; /* attribute number of field to extract */
Oid resulttype; /* type of the field (result type of this
@@ -790,7 +822,7 @@ typedef struct FieldStore
typedef struct RelabelType
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion expression */
int32 resulttypmod; /* output typmod (usually -1) */
@@ -810,7 +842,7 @@ typedef struct RelabelType
typedef struct CoerceViaIO
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion */
/* output typmod is not stored, but is presumed -1 */
@@ -834,7 +866,7 @@ typedef struct CoerceViaIO
typedef struct ArrayCoerceExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression (yields an array) */
Expr *elemexpr; /* expression representing per-element work */
Oid resulttype; /* output type of coercion (an array type) */
@@ -859,7 +891,7 @@ typedef struct ArrayCoerceExpr
typedef struct ConvertRowtypeExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type (always a composite type) */
/* Like RowExpr, we deliberately omit a typmod and collation here */
@@ -906,7 +938,7 @@ typedef struct CollateExpr
*/
typedef struct CaseExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid casetype; /* type of expression result */
Oid casecollid; /* OID of collation, or InvalidOid if none */
Expr *arg; /* implicit equality comparison argument */
@@ -936,7 +968,7 @@ typedef struct CaseWhen
*/
typedef struct CaseTestExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid typeId; /* type for substituted value */
int32 typeMod; /* typemod for substituted value */
Oid collation; /* collation for the substituted value */
@@ -952,7 +984,7 @@ typedef struct CaseTestExpr
*/
typedef struct ArrayExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid array_typeid; /* type of expression result */
Oid array_collid; /* OID of collation, or InvalidOid if none */
Oid element_typeid; /* common type of array elements */
@@ -986,7 +1018,7 @@ typedef struct ArrayExpr
*/
typedef struct RowExpr
{
- Expr xpr;
+ CacheableExpr xpr;
List *args; /* the fields */
Oid row_typeid; /* RECORDOID or a composite type's ID */
@@ -1034,7 +1066,7 @@ typedef enum RowCompareType
typedef struct RowCompareExpr
{
- Expr xpr;
+ CacheableExpr xpr;
RowCompareType rctype; /* LT LE GE or GT, never EQ or NE */
List *opnos; /* OID list of pairwise comparison ops */
List *opfamilies; /* OID list of containing operator families */
@@ -1048,7 +1080,7 @@ typedef struct RowCompareExpr
*/
typedef struct CoalesceExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid coalescetype; /* type of expression result */
Oid coalescecollid; /* OID of collation, or InvalidOid if none */
List *args; /* the arguments */
@@ -1066,7 +1098,7 @@ typedef enum MinMaxOp
typedef struct MinMaxExpr
{
- Expr xpr;
+ CacheableExpr xpr;
Oid minmaxtype; /* common type of arguments and result */
Oid minmaxcollid; /* OID of collation of result */
Oid inputcollid; /* OID of collation that function should use */
@@ -1107,7 +1139,7 @@ typedef enum SQLValueFunctionOp
typedef struct SQLValueFunction
{
- Expr xpr;
+ CacheableExpr xpr;
SQLValueFunctionOp op; /* which function this is */
Oid type; /* result type/typmod */
int32 typmod;
@@ -1145,7 +1177,7 @@ typedef enum
typedef struct XmlExpr
{
- Expr xpr;
+ CacheableExpr xpr;
XmlExprOp op; /* xml function ID */
char *name; /* name in xml(NAME foo ...) syntaxes */
List *named_args; /* non-XML expressions for xml_attributes */
@@ -1183,7 +1215,7 @@ typedef enum NullTestType
typedef struct NullTest
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
NullTestType nulltesttype; /* IS NULL, IS NOT NULL */
bool argisrow; /* T to perform field-by-field null checks */
@@ -1206,7 +1238,7 @@ typedef enum BoolTestType
typedef struct BooleanTest
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
BoolTestType booltesttype; /* test type */
int location; /* token location, or -1 if unknown */
@@ -1223,7 +1255,7 @@ typedef struct BooleanTest
*/
typedef struct CoerceToDomain
{
- Expr xpr;
+ CacheableExpr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* domain type ID (result type) */
int32 resulttypmod; /* output typmod (currently always -1) */
@@ -1233,6 +1265,24 @@ typedef struct CoerceToDomain
} CoerceToDomain;
/*
+ * DomainConstraintExpr - one item to check during CoerceToDomain
+ */
+
+typedef enum DomainConstraintType
+{
+ DOM_CONSTRAINT_NOTNULL,
+ DOM_CONSTRAINT_CHECK
+} DomainConstraintType;
+
+typedef struct DomainConstraintExpr
+{
+ Expr xpr;
+ DomainConstraintType constrainttype; /* constraint type */
+ char *name; /* name of constraint (for error msgs) */
+ Expr *check_expr; /* for CHECK, a boolean expression */
+} DomainConstraintExpr;
+
+/*
* Placeholder node for the value to be processed by a domain's check
* constraint. This is effectively like a Param, but can be implemented more
* simply since we need only one replacement value at a time.
@@ -1243,7 +1293,7 @@ typedef struct CoerceToDomain
*/
typedef struct CoerceToDomainValue
{
- Expr xpr;
+ CacheableExpr xpr;
Oid typeId; /* type for substituted value */
int32 typeMod; /* typemod for substituted value */
Oid collation; /* collation for the substituted value */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 3b9d303..4c3b85f 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -93,7 +93,7 @@ typedef struct PlannerGlobal
{
NodeTag type;
- ParamListInfo boundParams; /* Param values provided to planner() */
+ ParamListInfoCommon boundParams; /* Param values provided to planner() */
List *subplans; /* Plans for SubPlan nodes */
@@ -317,6 +317,8 @@ typedef struct PlannerInfo
/* optional private data for join_search_hook, e.g., GEQO */
void *join_search_private;
+
+ bool hasCachedExpr; /* true if any expressions are cached */
} PlannerInfo;
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index 2801bfd..c77ce78 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -21,7 +21,7 @@
/* Hook for plugins to get control in planner() */
typedef PlannedStmt *(*planner_hook_type) (Query *parse,
int cursorOptions,
- ParamListInfo boundParams);
+ ParamListInfoCommon boundParams);
extern PGDLLIMPORT planner_hook_type planner_hook;
/* Hook for plugins to get control when grouping_planner() plans upper rels */
@@ -33,9 +33,9 @@ extern PGDLLIMPORT create_upper_paths_hook_type create_upper_paths_hook;
extern PlannedStmt *planner(Query *parse, int cursorOptions,
- ParamListInfo boundParams);
+ ParamListInfoCommon boundParams);
extern PlannedStmt *standard_planner(Query *parse, int cursorOptions,
- ParamListInfo boundParams);
+ ParamListInfoCommon boundParams);
extern PlannerInfo *subquery_planner(PlannerGlobal *glob, Query *parse,
PlannerInfo *parent_root,
@@ -61,4 +61,9 @@ extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti);
extern List *get_partitioned_child_rels_for_join(PlannerInfo *root,
Relids join_relids);
+extern PathTarget *replace_pathtarget_cached_expressions(PathTarget *target,
+ PlannerInfo *root);
+
+extern List *replace_qual_cached_expressions(List *quals, PlannerInfo *root);
+
#endif /* PLANNER_H */
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index 0d3ec92..1b4fd8f 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -65,8 +65,12 @@ extern void split_pathtarget_at_srfs(PlannerInfo *root,
PathTarget *target, PathTarget *input_target,
List **targets, List **targets_contain_srfs);
-/* Convenience macro to get a PathTarget with valid cost/width fields */
+/*
+ * Convenience macro to get a PathTarget with valid cost/width fields and
+ * cached expressions.
+ */
#define create_pathtarget(root, tlist) \
- set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist))
+ set_pathtarget_cost_width(root, replace_pathtarget_cached_expressions( \
+ make_pathtarget_from_tlist(tlist), root))
#endif /* TLIST_H */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 62c7f6c..24ab0c8 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -58,9 +58,9 @@ extern List *pg_analyze_and_rewrite_params(RawStmt *parsetree,
void *parserSetupArg,
QueryEnvironment *queryEnv);
extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
- ParamListInfo boundParams);
+ ParamListInfoCommon boundParams);
extern List *pg_plan_queries(List *querytrees, int cursorOptions,
- ParamListInfo boundParams);
+ ParamListInfoCommon boundParams);
extern bool check_max_stack_depth(int *newval, void **extra, GucSource source);
extern void assign_max_stack_depth(int newval, void *extra);
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index 87fab19..2dadb88 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -138,6 +138,13 @@ typedef struct CachedPlan
bool dependsOnRole; /* is plan specific to that role? */
TransactionId saved_xmin; /* if valid, replan when TransactionXmin
* changes from this value */
+
+ /*
+ * Used to check whether the generic plan is valid for the new
+ * boundParams; NULL for the custom plans.
+ */
+ ParamListInfoPrecalculation boundParams;
+
int generation; /* parent's generation number for this plan */
int refcount; /* count of live references to this struct */
MemoryContext context; /* context containing this CachedPlan */
@@ -177,9 +184,10 @@ extern List *CachedPlanGetTargetList(CachedPlanSource *plansource,
QueryEnvironment *queryEnv);
extern CachedPlan *GetCachedPlan(CachedPlanSource *plansource,
- ParamListInfo boundParams,
+ ParamListInfoCommon boundParams,
bool useResOwner,
- QueryEnvironment *queryEnv);
+ QueryEnvironment *queryEnv,
+ bool genericPlanPrecalculateConstBoundParams);
extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
#endif /* PLANCACHE_H */
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index c203dab..6760c8c 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -167,6 +167,8 @@ extern void UpdateDomainConstraintRef(DomainConstraintRef *ref);
extern bool DomainHasConstraints(Oid type_id);
+extern List * GetDomainConstraintExprList(DomainConstraintRef *ref);
+
extern TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod);
extern TupleDesc lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod,
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index dd575e7..97fc003 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3453,6 +3453,7 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
/* initialize our ParamListInfo with appropriate hook functions */
estate->paramLI = (ParamListInfo)
palloc(offsetof(ParamListInfoData, params));
+ estate->paramLI->common.type = PARAM_LIST_INFO_DATA;
estate->paramLI->paramFetch = plpgsql_param_fetch;
estate->paramLI->paramFetchArg = (void *) estate;
estate->paramLI->paramCompile = plpgsql_param_compile;
@@ -6521,6 +6522,7 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
Query *query;
CachedPlan *cplan;
MemoryContext oldcontext;
+ ListCell *cell;
/*
* Initialize to "not simple".
@@ -6602,6 +6604,22 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
/* Can't fail, because we checked for a single CachedPlanSource above */
Assert(cplan != NULL);
+ /* Check that there're no cached expressions in the plan.
+ *
+ * If CachedExpr will not be initialized by ExecInitCachedExpr possibly it
+ * will use cached value when it shouldn't (for example, snapshot has
+ * changed).
+ */
+ foreach(cell, cplan->stmt_list)
+ {
+ if (((PlannedStmt *) cell->data.ptr_value)->hasCachedExpr)
+ {
+ /* Oops, release refcount and fail */
+ ReleaseCachedPlan(cplan, true);
+ return;
+ }
+ }
+
/* Share the remaining work with replan code path */
exec_save_simple_expr(expr, cplan);
diff --git a/src/test/regress/expected/precalculate_stable_functions.out b/src/test/regress/expected/precalculate_stable_functions.out
new file mode 100644
index 0000000..6bb0c90
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions.out
@@ -0,0 +1,6645 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create types and tables for testing
+CREATE TYPE my_integer AS (value integer);
+CREATE TYPE composite_type AS (first integer, second integer[], third boolean);
+CREATE TABLE x (x integer);
+INSERT INTO x SELECT generate_series(1, 4) x;
+CREATE TABLE wxyz (w integer, x integer[], y boolean, z integer);
+CREATE TABLE wxyz_child () INHERITS (wxyz);
+CREATE TABLE wxyz_child2 (a integer, b integer) INHERITS (wxyz);
+CREATE TABLE no_columns ();
+CREATE TABLE no_columns_child () INHERITS (no_columns);
+CREATE TABLE no_columns_child2 (a integer, b integer) INHERITS (no_columns);
+-- Create volatile functions for testing
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_array_integer (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_boolean (
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v boolean';
+ RETURN TRUE;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz (
+)
+RETURNS wxyz VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child (
+)
+RETURNS wxyz_child VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child2 (
+)
+RETURNS wxyz_child2 VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_oid (
+)
+RETURNS oid VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_my_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_instruction_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_xml (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_xml_content (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt2 (
+ integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_vlt (
+ integer
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer volatile';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_vlt (
+ my_integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer volatile';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create stable functions for testing
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_array_integer (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz (
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child (
+)
+RETURNS wxyz_child STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child2 (
+)
+RETURNS wxyz_child2 STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_oid (
+)
+RETURNS oid STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_my_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_instruction_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_xml (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_xml_content (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_integer (
+ integer[]
+)
+RETURNS integer[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_oid (
+ oid[]
+)
+RETURNS oid[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_oid';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_wxyz (
+ wxyz
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 wxyz';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_composite_type (
+ composite_type
+)
+RETURNS composite_type STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 composite_type';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer (
+ my_integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_no_columns (
+ no_columns
+)
+RETURNS no_columns STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 no_columns';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_name (
+ name
+)
+RETURNS name STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 name';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_xml (
+ xml
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 xml';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_text (
+ text
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 text';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_stl (
+ integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer stable';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_stl (
+ my_integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer stable';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(x) from x);
+END
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+-- Create immutable functions for testing
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_my_integer (
+ my_integer
+)
+RETURNS my_integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_imm (
+ boolean,
+ boolean
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create operators for testing
+CREATE OPERATOR === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_booleans_imm,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+-- Create domains for testing
+CREATE DOMAIN my_integer_no_check AS integer;
+CREATE DOMAIN my_integer_not_null AS integer;
+CREATE DOMAIN my_integer_vlt_check AS integer CHECK (VALUE === 1);
+CREATE DOMAIN my_integer_stl_check AS integer CHECK (VALUE ==== 1);
+CREATE DOMAIN my_integer_imm_check AS integer CHECK (VALUE ===== 1);
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_no_check (
+ my_integer_no_check
+)
+RETURNS my_integer_no_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_no_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_not_null (
+ my_integer_not_null
+)
+RETURNS my_integer_not_null STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_not_null';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_vlt_check (
+ my_integer_vlt_check
+)
+RETURNS my_integer_vlt_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_vlt_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_stl_check (
+ my_integer_stl_check
+)
+RETURNS my_integer_stl_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_stl_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_imm_check (
+ my_integer_imm_check
+)
+RETURNS my_integer_imm_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_imm_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_my_integer_vlt_check (
+ my_integer_vlt_check[]
+)
+RETURNS my_integer_vlt_check[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_my_integer_vlt_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_my_integer_stl_check (
+ my_integer_stl_check[]
+)
+RETURNS my_integer_stl_check[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_my_integer_stl_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Functions testing
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl() FROM x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- JOIN/ON clause testing
+-- should not be precalculated
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x;
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+NOTICE: s
+NOTICE: s
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+ x_vlt2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(1) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+NOTICE: s2 boolean
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+NOTICE: s2 boolean
+NOTICE: equal booleans immutable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl() ==== 1 FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- IS (NOT) DISTINCT FROM expression testing
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- IS (NOT) DISTINCT FROM expressions with null arguments testing
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM 1) FROM x;
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM 1) FROM x;
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL)) FROM x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM x_stl2(NULL)) FROM x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+NOTICE: equal my_integer immutable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- NULLIF expressions with null arguments testing
+SELECT x_stl2(NULLIF(1, NULL)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(NULLIF(NULL::integer, NULL)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+-- should not be precalculated
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+SELECT 1 === ANY ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 === ALL ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY ('{2, 3}') FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL ('{2, 3}') FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== ANY ('{2, 3}') FROM x;
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== ALL ('{2, 3}') FROM x;
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+SELECT 1 ==== ANY ('{2, NULL}') FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY ('{2, 3}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY ('{2, NULL}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::integer ==== ANY (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT 1 ==== ALL ('{2, NULL}') FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL ('{2, 3}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL ('{2, NULL}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::integer ==== ALL (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x;
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x;
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Boolean expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- ARRAY[] expressions testing
+-- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[x_vlt(), 2]) FROM x;
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+SELECT x_stl2_array_integer(ARRAY[x_stl(), 2]) FROM x;
+NOTICE: s
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+-- Multidimensional ARRAY[] expressions testing
+-- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[[x_vlt(), 2], [3, 4]]) FROM x;
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+(4 rows)
+
+SELECT x_stl2_array_integer(ARRAY[[x_stl(), 2], [3, 4]]) FROM x;
+NOTICE: s
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+(4 rows)
+
+-- Array subscripting operations testing
+SELECT x_stl2(('{1, 2}'::integer[])[1]) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_array_integer(('{1, 2}'::integer[])[:]) FROM x;
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+-- Mixed functions and array subscripting operations testing
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x;
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x;
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+NOTICE: s array_integer
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- FieldSelect expressions testing
+SELECT x_stl2(('(1, {2}, TRUE, 3)'::wxyz).w) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(('(1)'::my_integer).value) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+NOTICE: s wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+NOTICE: s my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- ROW() expressions testing
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE, 3)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_composite_type((1, '{2}', TRUE)) FROM x;
+NOTICE: s2 composite_type
+ x_stl2_composite_type
+-----------------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_composite_type(ROW(1, '{2}', TRUE)) FROM x;
+NOTICE: s2 composite_type
+ x_stl2_composite_type
+-----------------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_composite_type((1, '{2}', TRUE)::composite_type) FROM x;
+NOTICE: s2 composite_type
+ x_stl2_composite_type
+-----------------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+-- Mixed functions and ROW() expressions testing
+-- should not be precalculated
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x;
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- RelabelType expressions testing
+-- should not be precalculated
+SELECT x_stl2(x_vlt_oid()::integer) FROM x;
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+NOTICE: s oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- CoerceViaIO expressions testing
+SELECT x_stl2_my_integer('(1)'::text::my_integer) FROM x;
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x;
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+NOTICE: s text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and CoerceViaIO expressions testing
+-- should not be precalculated
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x;
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+NOTICE: s text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- ArrayCoerce expressions testing
+-- Binary-coercible types:
+-- should not be precalculated
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_vlt;
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x; -- should not be precalculated
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+ my_integer
+------------
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+(4 rows)
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x;
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ my_integer
+------------
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+(4 rows)
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+-- should not be precalculated
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer stable
+NOTICE: cast my_integer as integer stable
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+-- should not be precalculated
+SELECT x_vlt_array_integer()::my_integer[] FROM x;
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_vlt_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+NOTICE: s array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_stl_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+-- ConvertRowtypeExpr testing
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3)'::wxyz_child::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_no_columns('()'::no_columns_child::no_columns) FROM x;
+NOTICE: s2 no_columns
+ x_stl2_no_columns
+-------------------
+ ()
+ ()
+ ()
+ ()
+(4 rows)
+
+SELECT x_stl2_no_columns('(1, 2)'::no_columns_child2::no_columns) FROM x;
+NOTICE: s2 no_columns
+ x_stl2_no_columns
+-------------------
+ ()
+ ()
+ ()
+ ()
+(4 rows)
+
+-- Mixed functions and ConvertRowtypeExpr testing
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: s wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: s wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- CASE expressions testing
+-- should not be precalculated
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x;
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x;
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+NOTICE: s2 boolean
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- RowCompareExpr testing
+SELECT x_stl2_boolean((1, 2) < (1, 3)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and RowCompareExpr testing
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- COALESCE expressions testing
+-- should not be precalculated
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x;
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, 1, 3)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, 1, 3)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and GREATEST and LEAST functions testing
+-- should not be precalculated
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x;
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x;
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- SQLValueFunction testing
+CREATE ROLE regress_testrol2 SUPERUSER;
+CREATE ROLE regress_testrol1 SUPERUSER LOGIN IN ROLE regress_testrol2;
+\c -
+SET SESSION AUTHORIZATION regress_testrol1;
+SET ROLE regress_testrol2;
+SELECT x_stl2_boolean(date(now()) = current_date) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::timetz = current_time) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::timetz(2) = current_time(2)) FROM x; -- precision
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now() = current_timestamp) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- precision
+SELECT x_stl2_boolean(
+ length(current_timestamp::text) >= length(current_timestamp(0)::text)
+)
+FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::time = localtime) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::time(2) = localtime(2)) FROM x; -- precision
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::timestamp = localtimestamp) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- precision
+SELECT x_stl2_boolean(now()::timestamp(2) = localtimestamp(2)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_name(current_role) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+(4 rows)
+
+SELECT x_stl2_name(current_user) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+(4 rows)
+
+SELECT x_stl2_name(user) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+(4 rows)
+
+SELECT x_stl2_name(session_user) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol1
+ regress_testrol1
+ regress_testrol1
+ regress_testrol1
+(4 rows)
+
+SELECT x_stl2_name(current_catalog) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+-------------
+ regression
+ regression
+ regression
+ regression
+(4 rows)
+
+SELECT x_stl2_name(current_schema) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+-------------
+ public
+ public
+ public
+ public
+(4 rows)
+
+\c
+DROP ROLE regress_testrol1, regress_testrol2;
+-- Xml expressions testing
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', '<bar>foo</bar>')) FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), 'cont', 'ent')
+)
+FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------
+ <foo bar="bar">content</foo>
+ <foo bar="bar">content</foo>
+ <foo bar="bar">content</foo>
+ <foo bar="bar">content</foo>
+(4 rows)
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, 123 AS bar)) FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------
+ <foo>abc</foo><bar>123</bar>
+ <foo>abc</foo><bar>123</bar>
+ <foo>abc</foo><bar>123</bar>
+ <foo>abc</foo><bar>123</bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>'
+))
+FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT 'abc<foo>bar</foo><bar>foo</bar>')) FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPI(name php, 'echo "hello world";')) FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+SELECT x_stl2_xml(XMLROOT(
+ '<?xml version="1.0"?><content>abc</content>',
+ version '1.0',
+ standalone yes
+))
+FROM x;
+NOTICE: s2 xml
+ x_stl2_xml
+--------------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><content>abc</content>
+ <?xml version="1.0" standalone="yes"?><content>abc</content>
+ <?xml version="1.0" standalone="yes"?><content>abc</content>
+ <?xml version="1.0" standalone="yes"?><content>abc</content>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>' AS text
+))
+FROM x;
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------------------------------
+ <?xml version="1.0"?><book><title>Manual</title></book>
+ <?xml version="1.0"?><book><title>Manual</title></book>
+ <?xml version="1.0"?><book><title>Manual</title></book>
+ <?xml version="1.0"?><book><title>Manual</title></book>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(
+ CONTENT 'abc<foo>bar</foo><bar>foo</bar>' AS text
+))
+FROM x;
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_boolean('abc<foo>bar</foo><bar>foo</bar>' IS DOCUMENT) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+-------------------------------------
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------------------
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+ x_stl2_text
+----------------
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+-------------------------------------
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+(4 rows)
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------------------
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+NOTICE: s text xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+NOTICE: s xml content
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+NOTICE: s text xml instruction content
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+NOTICE: s xml
+NOTICE: s2 text
+ x_stl2_text
+----------------
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+NOTICE: s xml content
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+NOTICE: s xml content
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- NullTest expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x;
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x;
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- BooleanTest expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- CoerceToDomain expressions testing
+SELECT x_stl2_my_integer_no_check(1::my_integer_no_check) FROM x;
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(1::my_integer_not_null) FROM x;
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_vlt_check(1::my_integer_vlt_check) FROM x;
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+ x_stl2_my_integer_vlt_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(1::my_integer_stl_check) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(1::my_integer_imm_check) FROM x;
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and CoerceToDomain expressions testing
+-- should not be precalculated
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x;
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x;
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed ArrayCoerce and CoerceToDomain expressions testing
+-- should not be precalculated
+SELECT x_stl2_array_my_integer_vlt_check(
+ '{1, 1}'::integer[]::my_integer_vlt_check[]
+)
+FROM x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 array_my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 array_my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 array_my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 array_my_integer_vlt_check
+ x_stl2_array_my_integer_vlt_check
+-----------------------------------
+ {1,1}
+ {1,1}
+ {1,1}
+ {1,1}
+(4 rows)
+
+SELECT x_stl2_array_my_integer_stl_check(
+ '{1, 1}'::integer[]::my_integer_stl_check[]
+)
+FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 array_my_integer_stl_check
+ x_stl2_array_my_integer_stl_check
+-----------------------------------
+ {1,1}
+ {1,1}
+ {1,1}
+ {1,1}
+(4 rows)
+
+-- Tracking functions testing
+SET track_functions TO 'all';
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl() FROM x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- JOIN/ON clause testing
+-- should not be precalculated
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x;
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+NOTICE: s
+NOTICE: s
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+ x_vlt2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(1) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+NOTICE: s2 boolean
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+NOTICE: s2 boolean
+NOTICE: equal booleans immutable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl() ==== 1 FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+-- should not be precalculated
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and boolean expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and ARRAY[] expressions testing
+-- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[x_vlt()]) FROM x;
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1}
+ {1}
+ {1}
+ {1}
+(4 rows)
+
+SELECT x_stl2_array_integer(ARRAY[x_stl()]) FROM x;
+NOTICE: s
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1}
+ {1}
+ {1}
+ {1}
+(4 rows)
+
+-- Mixed functions and array subscripting operations testing
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x;
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x;
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+NOTICE: s array_integer
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+NOTICE: s wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+NOTICE: s my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and ROW() expressions testing
+-- should not be precalculated
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x;
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- Mixed functions and RelabelType expressions testing
+SELECT x_stl2(x_vlt_oid()::integer) FROM x; -- should not be precalculated
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+NOTICE: s oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and CoerceViaIO expressions testing
+-- should not be precalculated
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x;
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+NOTICE: s text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x;
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+NOTICE: s text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Binary-coercible types:
+-- should not be precalculated
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- Not binary-coercible types:
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+-- should not be precalculated
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer stable
+NOTICE: cast my_integer as integer stable
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+-- should not be precalculated
+SELECT x_vlt_array_integer()::my_integer[] FROM x;
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_vlt_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+NOTICE: s array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_stl_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+-- Mixed functions and ConvertRowtypeExpr testing
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: s wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: s wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- Mixed functions and CASE expressions testing
+-- should not be precalculated
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x;
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x;
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+NOTICE: s2 boolean
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and RowCompareExpr testing
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and COALESCE expressions testing
+-- should not be precalculated
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x;
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- Mixed functions and GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+-------------------------------------
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------------------
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+NOTICE: v text xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+NOTICE: v text xml content
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+NOTICE: v text xml instruction content
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+NOTICE: v xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+NOTICE: v xml
+NOTICE: s2 text
+ x_stl2_text
+----------------
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+NOTICE: v xml content
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+NOTICE: v xml content
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+----------------------
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+ <abc/><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+-------------------------------------
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+ <foo bar="bar"><bar>foo</bar></foo>
+(4 rows)
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------------------
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+ <foo>abc</foo><bar><bar>foo</bar></bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+NOTICE: s text xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+ <book><title>Manual</title></book>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+NOTICE: s xml content
+NOTICE: s2 xml
+ x_stl2_xml
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+NOTICE: s text xml instruction content
+NOTICE: s2 xml
+ x_stl2_xml
+-----------------------------
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+ <?php echo "hello world";?>
+(4 rows)
+
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: s xml
+NOTICE: s2 xml
+ x_stl2_xml
+------------------------------------------------------
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+ <?xml version="1.0" standalone="yes"?><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+NOTICE: s xml
+NOTICE: s2 text
+ x_stl2_text
+----------------
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+ <bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+NOTICE: s xml content
+NOTICE: s2 text
+ x_stl2_text
+---------------------------------
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+ abc<foo>bar</foo><bar>foo</bar>
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+NOTICE: s xml content
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and NullTest expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x;
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x;
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and BooleanTest expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and CoerceToDomain expressions testing
+-- should not be precalculated
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x;
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x;
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SET track_functions TO DEFAULT;
+-- ROW() expressions with dropped columns testing
+ALTER TABLE wxyz DROP COLUMN z;
+-- Update some functions
+CREATE OR REPLACE FUNCTION public.x_stl2_wxyz (
+ wxyz
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 wxyz';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- ROW() expressions testing
+SELECT x_stl2_wxyz((1, '{2}', TRUE)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_wxyz((1, '{2}', TRUE)::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+-- Mixed functions and ROW() expressions testing
+-- should not be precalculated
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE)) FROM x;
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE)) FROM x;
+NOTICE: s
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+BEGIN;
+SELECT simple();
+ simple
+--------
+ 4
+(1 row)
+
+INSERT INTO x VALUES (5);
+SELECT simple();
+ simple
+--------
+ 5
+(1 row)
+
+ROLLBACK;
+-- Prepared statements testing
+PREPARE test_x_imm2 (integer) AS SELECT x_imm2(x_imm2($1)) FROM x;
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+-- Drop tables and domains for testing
+DROP TABLE x;
+DROP FUNCTION x_vlt_wxyz, x_vlt_wxyz_child, x_vlt_wxyz_child2;
+DROP FUNCTION x_stl_wxyz, x_stl_wxyz_child, x_stl_wxyz_child2, x_stl2_wxyz;
+DROP TABLE wxyz, wxyz_child, wxyz_child2;
+DROP FUNCTION x_stl2_no_columns;
+DROP TABLE no_columns, no_columns_child, no_columns_child2;
+DROP FUNCTION x_stl2_my_integer_no_check;
+DROP DOMAIN my_integer_no_check;
+DROP FUNCTION x_stl2_my_integer_not_null;
+DROP DOMAIN my_integer_not_null;
+DROP FUNCTION x_stl2_my_integer_vlt_check;
+DROP FUNCTION x_stl2_array_my_integer_vlt_check;
+DROP DOMAIN my_integer_vlt_check;
+DROP FUNCTION x_stl2_my_integer_stl_check;
+DROP FUNCTION x_stl2_array_my_integer_stl_check;
+DROP DOMAIN my_integer_stl_check;
+DROP FUNCTION x_stl2_my_integer_imm_check;
+DROP DOMAIN my_integer_imm_check;
diff --git a/src/test/regress/expected/precalculate_stable_functions_1.out b/src/test/regress/expected/precalculate_stable_functions_1.out
new file mode 100644
index 0000000..b640bd9
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions_1.out
@@ -0,0 +1,6291 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create types and tables for testing
+CREATE TYPE my_integer AS (value integer);
+CREATE TYPE composite_type AS (first integer, second integer[], third boolean);
+CREATE TABLE x (x integer);
+INSERT INTO x SELECT generate_series(1, 4) x;
+CREATE TABLE wxyz (w integer, x integer[], y boolean, z integer);
+CREATE TABLE wxyz_child () INHERITS (wxyz);
+CREATE TABLE wxyz_child2 (a integer, b integer) INHERITS (wxyz);
+CREATE TABLE no_columns ();
+CREATE TABLE no_columns_child () INHERITS (no_columns);
+CREATE TABLE no_columns_child2 (a integer, b integer) INHERITS (no_columns);
+-- Create volatile functions for testing
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_array_integer (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_boolean (
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v boolean';
+ RETURN TRUE;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz (
+)
+RETURNS wxyz VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child (
+)
+RETURNS wxyz_child VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child2 (
+)
+RETURNS wxyz_child2 VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_oid (
+)
+RETURNS oid VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_my_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_instruction_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_xml (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_xml_content (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt2 (
+ integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_vlt (
+ integer
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer volatile';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_vlt (
+ my_integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer volatile';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create stable functions for testing
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_array_integer (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz (
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child (
+)
+RETURNS wxyz_child STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child2 (
+)
+RETURNS wxyz_child2 STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_oid (
+)
+RETURNS oid STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_my_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_instruction_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_xml (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_xml_content (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_integer (
+ integer[]
+)
+RETURNS integer[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_oid (
+ oid[]
+)
+RETURNS oid[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_oid';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_wxyz (
+ wxyz
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 wxyz';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_composite_type (
+ composite_type
+)
+RETURNS composite_type STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 composite_type';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer (
+ my_integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_no_columns (
+ no_columns
+)
+RETURNS no_columns STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 no_columns';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_name (
+ name
+)
+RETURNS name STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 name';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_xml (
+ xml
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 xml';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_text (
+ text
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 text';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_stl (
+ integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer stable';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_stl (
+ my_integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer stable';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(x) from x);
+END
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+-- Create immutable functions for testing
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_my_integer (
+ my_integer
+)
+RETURNS my_integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_imm (
+ boolean,
+ boolean
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create operators for testing
+CREATE OPERATOR === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_booleans_imm,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+-- Create domains for testing
+CREATE DOMAIN my_integer_no_check AS integer;
+CREATE DOMAIN my_integer_not_null AS integer;
+CREATE DOMAIN my_integer_vlt_check AS integer CHECK (VALUE === 1);
+CREATE DOMAIN my_integer_stl_check AS integer CHECK (VALUE ==== 1);
+CREATE DOMAIN my_integer_imm_check AS integer CHECK (VALUE ===== 1);
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_no_check (
+ my_integer_no_check
+)
+RETURNS my_integer_no_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_no_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_not_null (
+ my_integer_not_null
+)
+RETURNS my_integer_not_null STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_not_null';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_vlt_check (
+ my_integer_vlt_check
+)
+RETURNS my_integer_vlt_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_vlt_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_stl_check (
+ my_integer_stl_check
+)
+RETURNS my_integer_stl_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_stl_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_imm_check (
+ my_integer_imm_check
+)
+RETURNS my_integer_imm_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_imm_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_my_integer_vlt_check (
+ my_integer_vlt_check[]
+)
+RETURNS my_integer_vlt_check[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_my_integer_vlt_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_array_my_integer_stl_check (
+ my_integer_stl_check[]
+)
+RETURNS my_integer_stl_check[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_my_integer_stl_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Functions testing
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl() FROM x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- JOIN/ON clause testing
+-- should not be precalculated
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x;
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+NOTICE: s
+NOTICE: s
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+ x_vlt2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(1) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+NOTICE: s2 boolean
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+NOTICE: s2 boolean
+NOTICE: equal booleans immutable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl() ==== 1 FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- IS (NOT) DISTINCT FROM expression testing
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x;
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- IS (NOT) DISTINCT FROM expressions with null arguments testing
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM 1) FROM x;
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM 1) FROM x;
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL)) FROM x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM x_stl2(NULL)) FROM x;
+NOTICE: s2
+NOTICE: s2
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+NOTICE: equal my_integer immutable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- NULLIF expressions with null arguments testing
+SELECT x_stl2(NULLIF(1, NULL)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(NULLIF(NULL::integer, NULL)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+-- should not be precalculated
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+SELECT 1 === ANY ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 === ALL ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY ('{2, 3}') FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL ('{2, 3}') FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== ANY ('{2, 3}') FROM x;
+NOTICE: equal integers immutable
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== ALL ('{2, 3}') FROM x;
+NOTICE: equal integers immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: equal my_integer immutable
+NOTICE: equal my_integer immutable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+SELECT 1 ==== ANY ('{2, NULL}') FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY ('{2, 3}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ANY ('{2, NULL}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::integer ==== ANY (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT 1 ==== ALL ('{2, NULL}') FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL ('{2, 3}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT NULL ==== ALL ('{2, NULL}'::integer[]) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::integer ==== ALL (NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x;
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x;
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Boolean expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- ARRAY[] expressions testing
+-- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[x_vlt(), 2]) FROM x;
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+SELECT x_stl2_array_integer(ARRAY[x_stl(), 2]) FROM x;
+NOTICE: s
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+-- Multidimensional ARRAY[] expressions testing
+-- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[[x_vlt(), 2], [3, 4]]) FROM x;
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+(4 rows)
+
+SELECT x_stl2_array_integer(ARRAY[[x_stl(), 2], [3, 4]]) FROM x;
+NOTICE: s
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+ {{1,2},{3,4}}
+(4 rows)
+
+-- Array subscripting operations testing
+SELECT x_stl2(('{1, 2}'::integer[])[1]) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_array_integer(('{1, 2}'::integer[])[:]) FROM x;
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+-- Mixed functions and array subscripting operations testing
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x;
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x;
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+NOTICE: s array_integer
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- FieldSelect expressions testing
+SELECT x_stl2(('(1, {2}, TRUE, 3)'::wxyz).w) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(('(1)'::my_integer).value) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+NOTICE: s wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+NOTICE: s my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- ROW() expressions testing
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE, 3)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_composite_type((1, '{2}', TRUE)) FROM x;
+NOTICE: s2 composite_type
+ x_stl2_composite_type
+-----------------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_composite_type(ROW(1, '{2}', TRUE)) FROM x;
+NOTICE: s2 composite_type
+ x_stl2_composite_type
+-----------------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_composite_type((1, '{2}', TRUE)::composite_type) FROM x;
+NOTICE: s2 composite_type
+ x_stl2_composite_type
+-----------------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+-- Mixed functions and ROW() expressions testing
+-- should not be precalculated
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x;
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- RelabelType expressions testing
+-- should not be precalculated
+SELECT x_stl2(x_vlt_oid()::integer) FROM x;
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+NOTICE: s oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- CoerceViaIO expressions testing
+SELECT x_stl2_my_integer('(1)'::text::my_integer) FROM x;
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x;
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+NOTICE: s text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and CoerceViaIO expressions testing
+-- should not be precalculated
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x;
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+NOTICE: s text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- ArrayCoerce expressions testing
+-- Binary-coercible types:
+-- should not be precalculated
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_vlt;
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x; -- should not be precalculated
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+NOTICE: cast integer as my_integer volatile
+ my_integer
+------------
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+(4 rows)
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x;
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ my_integer
+------------
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+ {(1),(2)}
+(4 rows)
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+-- should not be precalculated
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer stable
+NOTICE: cast my_integer as integer stable
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+-- should not be precalculated
+SELECT x_vlt_array_integer()::my_integer[] FROM x;
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_vlt_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+NOTICE: s array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_stl_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+-- ConvertRowtypeExpr testing
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3)'::wxyz_child::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_no_columns('()'::no_columns_child::no_columns) FROM x;
+NOTICE: s2 no_columns
+ x_stl2_no_columns
+-------------------
+ ()
+ ()
+ ()
+ ()
+(4 rows)
+
+SELECT x_stl2_no_columns('(1, 2)'::no_columns_child2::no_columns) FROM x;
+NOTICE: s2 no_columns
+ x_stl2_no_columns
+-------------------
+ ()
+ ()
+ ()
+ ()
+(4 rows)
+
+-- Mixed functions and ConvertRowtypeExpr testing
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: s wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: s wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- CASE expressions testing
+-- should not be precalculated
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x;
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x;
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+NOTICE: s2 boolean
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- RowCompareExpr testing
+SELECT x_stl2_boolean((1, 2) < (1, 3)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and RowCompareExpr testing
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- COALESCE expressions testing
+-- should not be precalculated
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x;
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, 1, 3)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, 1, 3)) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and GREATEST and LEAST functions testing
+-- should not be precalculated
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x;
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x;
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- SQLValueFunction testing
+CREATE ROLE regress_testrol2 SUPERUSER;
+CREATE ROLE regress_testrol1 SUPERUSER LOGIN IN ROLE regress_testrol2;
+\c -
+SET SESSION AUTHORIZATION regress_testrol1;
+SET ROLE regress_testrol2;
+SELECT x_stl2_boolean(date(now()) = current_date) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::timetz = current_time) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::timetz(2) = current_time(2)) FROM x; -- precision
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now() = current_timestamp) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- precision
+SELECT x_stl2_boolean(
+ length(current_timestamp::text) >= length(current_timestamp(0)::text)
+)
+FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::time = localtime) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::time(2) = localtime(2)) FROM x; -- precision
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(now()::timestamp = localtimestamp) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- precision
+SELECT x_stl2_boolean(now()::timestamp(2) = localtimestamp(2)) FROM x;
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_name(current_role) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+(4 rows)
+
+SELECT x_stl2_name(current_user) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+(4 rows)
+
+SELECT x_stl2_name(user) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+ regress_testrol2
+(4 rows)
+
+SELECT x_stl2_name(session_user) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+------------------
+ regress_testrol1
+ regress_testrol1
+ regress_testrol1
+ regress_testrol1
+(4 rows)
+
+SELECT x_stl2_name(current_catalog) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+-------------
+ regression
+ regression
+ regression
+ regression
+(4 rows)
+
+SELECT x_stl2_name(current_schema) FROM x;
+NOTICE: s2 name
+ x_stl2_name
+-------------
+ public
+ public
+ public
+ public
+(4 rows)
+
+\c
+DROP ROLE regress_testrol1, regress_testrol2;
+-- Xml expressions testing
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', '<bar>foo</bar>')) FROM x;
+ERROR: unsupported XML feature
+LINE 1: SELECT x_stl2_xml(XMLCONCAT('<abc/>', '<bar>foo</bar>')) FRO...
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), 'cont', 'ent')
+)
+FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, 123 AS bar)) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLPARSE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>'
+))
+FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLPARSE(CONTENT 'abc<foo>bar</foo><bar>foo</bar>')) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLPI(name php, 'echo "hello world";')) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLROOT(
+ '<?xml version="1.0"?><content>abc</content>',
+ version '1.0',
+ standalone yes
+))
+FROM x;
+ERROR: unsupported XML feature
+LINE 2: '<?xml version="1.0"?><content>abc</content>',
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_text(XMLSERIALIZE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>' AS text
+))
+FROM x;
+ERROR: unsupported XML feature
+LINE 2: DOCUMENT '<?xml version="1.0"?><book><title>Manual</title>...
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_text(XMLSERIALIZE(
+ CONTENT 'abc<foo>bar</foo><bar>foo</bar>' AS text
+))
+FROM x;
+ERROR: unsupported XML feature
+LINE 2: CONTENT 'abc<foo>bar</foo><bar>foo</bar>' AS text
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_boolean('abc<foo>bar</foo><bar>foo</bar>' IS DOCUMENT) FROM x;
+ERROR: unsupported XML feature
+LINE 1: SELECT x_stl2_boolean('abc<foo>bar</foo><bar>foo</bar>' IS D...
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+ERROR: unsupported XML feature
+LINE 1: SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+NOTICE: v text xml
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+NOTICE: v text xml content
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: v xml
+ERROR: unsupported XML feature
+LINE 1: SELECT '<bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT '<bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_vlt_xml() line 4 at RETURN
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+NOTICE: v xml
+ERROR: unsupported XML feature
+LINE 1: SELECT '<bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT '<bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_vlt_xml() line 4 at RETURN
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+NOTICE: v xml content
+ERROR: unsupported XML feature
+LINE 1: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_vlt_xml_content() line 4 at RETURN
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+NOTICE: v xml content
+ERROR: unsupported XML feature
+LINE 1: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_vlt_xml_content() line 4 at RETURN
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+ERROR: unsupported XML feature
+LINE 1: SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+NOTICE: s text xml
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+NOTICE: s xml content
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: s xml
+ERROR: unsupported XML feature
+LINE 1: SELECT '<bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT '<bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_stl_xml() line 4 at RETURN
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+NOTICE: s xml
+ERROR: unsupported XML feature
+LINE 1: SELECT '<bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT '<bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_stl_xml() line 4 at RETURN
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+NOTICE: s xml content
+ERROR: unsupported XML feature
+LINE 1: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_stl_xml_content() line 4 at RETURN
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+NOTICE: s xml content
+ERROR: unsupported XML feature
+LINE 1: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_stl_xml_content() line 4 at RETURN
+-- NullTest expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x;
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x;
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- BooleanTest expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- CoerceToDomain expressions testing
+SELECT x_stl2_my_integer_no_check(1::my_integer_no_check) FROM x;
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(1::my_integer_not_null) FROM x;
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_vlt_check(1::my_integer_vlt_check) FROM x;
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: s2 my_integer_vlt_check
+ x_stl2_my_integer_vlt_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(1::my_integer_stl_check) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(1::my_integer_imm_check) FROM x;
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and CoerceToDomain expressions testing
+-- should not be precalculated
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x;
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x;
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed ArrayCoerce and CoerceToDomain expressions testing
+-- should not be precalculated
+SELECT x_stl2_array_my_integer_vlt_check(
+ '{1, 1}'::integer[]::my_integer_vlt_check[]
+)
+FROM x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 array_my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 array_my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 array_my_integer_vlt_check
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 array_my_integer_vlt_check
+ x_stl2_array_my_integer_vlt_check
+-----------------------------------
+ {1,1}
+ {1,1}
+ {1,1}
+ {1,1}
+(4 rows)
+
+SELECT x_stl2_array_my_integer_stl_check(
+ '{1, 1}'::integer[]::my_integer_stl_check[]
+)
+FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 array_my_integer_stl_check
+ x_stl2_array_my_integer_stl_check
+-----------------------------------
+ {1,1}
+ {1,1}
+ {1,1}
+ {1,1}
+(4 rows)
+
+-- Tracking functions testing
+SET track_functions TO 'all';
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl() FROM x;
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x_vlt
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+SELECT x_stl() FROM x WHERE x_stl() < x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+ x_stl
+-------
+ 1
+ 1
+ 1
+(3 rows)
+
+-- JOIN/ON clause testing
+-- should not be precalculated
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x;
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: v
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+NOTICE: s
+NOTICE: s
+ x | y
+---+---
+ 2 | 1
+ 3 | 1
+ 4 | 1
+ 2 | 2
+ 3 | 2
+ 4 | 2
+(6 rows)
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+NOTICE: v2
+ x_vlt2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(1) FROM x;
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+NOTICE: v
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2(x_stl()) FROM x;
+NOTICE: s
+NOTICE: i2
+ x_imm2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+NOTICE: v
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+NOTICE: v
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: s2 strict
+ x_stl2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+NOTICE: s2 strict
+NOTICE: i2 strict
+ x_imm2_strict
+---------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_stl2_strict
+---------------
+
+
+
+
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+NOTICE: s2
+ x_imm2_strict
+---------------
+
+
+
+
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM x;
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+NOTICE: s2 boolean
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+NOTICE: s2 boolean
+NOTICE: equal booleans immutable
+ ?column?
+----------
+
+
+
+
+(4 rows)
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl() ==== 1 FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal booleans stable strict
+ equal_booleans_stl_strict
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ equal_my_integer_stl
+----------------------
+
+
+
+
+(4 rows)
+
+-- should not be precalculated
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+ nullif
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+NOTICE: equal integers volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+NOTICE: equal my_integer volatile
+NOTICE: equal my_integer volatile
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+NOTICE: equal integers stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: v
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x; -- should not be precalculated
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+NOTICE: v array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+NOTICE: v my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+NOTICE: s array_integer
+NOTICE: equal integers stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+NOTICE: s my_integer
+NOTICE: equal my_integer stable
+NOTICE: equal my_integer stable
+ ?column?
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and boolean expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and ARRAY[] expressions testing
+-- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[x_vlt()]) FROM x;
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+NOTICE: v
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1}
+ {1}
+ {1}
+ {1}
+(4 rows)
+
+SELECT x_stl2_array_integer(ARRAY[x_stl()]) FROM x;
+NOTICE: s
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1}
+ {1}
+ {1}
+ {1}
+(4 rows)
+
+-- Mixed functions and array subscripting operations testing
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x;
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+NOTICE: v array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+NOTICE: v array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x;
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+NOTICE: s array_integer
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+NOTICE: v wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+NOTICE: v my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+NOTICE: s wxyz
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+NOTICE: s my_integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and ROW() expressions testing
+-- should not be precalculated
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x;
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- Mixed functions and RelabelType expressions testing
+SELECT x_stl2(x_vlt_oid()::integer) FROM x; -- should not be precalculated
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+NOTICE: v oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+NOTICE: s oid
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and CoerceViaIO expressions testing
+-- should not be precalculated
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x;
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+NOTICE: v text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+NOTICE: s text integer
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x;
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+NOTICE: v text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+NOTICE: s text my_integer
+NOTICE: s2 my_integer
+ x_stl2_my_integer
+-------------------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Binary-coercible types:
+-- should not be precalculated
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x;
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+NOTICE: v array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+NOTICE: s array_integer
+NOTICE: s2 array_oid
+ x_stl2_array_oid
+------------------
+ {2,3}
+ {2,3}
+ {2,3}
+ {2,3}
+(4 rows)
+
+-- Not binary-coercible types:
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+-- should not be precalculated
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+NOTICE: cast my_integer as integer volatile
+NOTICE: cast my_integer as integer volatile
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+NOTICE: cast my_integer as integer stable
+NOTICE: cast my_integer as integer stable
+NOTICE: s2 array_integer
+ x_stl2_array_integer
+----------------------
+ {1,2}
+ {1,2}
+ {1,2}
+ {1,2}
+(4 rows)
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+-- should not be precalculated
+SELECT x_vlt_array_integer()::my_integer[] FROM x;
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+NOTICE: v array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_vlt_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+NOTICE: s array_integer
+NOTICE: cast integer as my_integer stable
+NOTICE: cast integer as my_integer stable
+ x_stl_array_integer
+---------------------
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+ {(2),(3)}
+(4 rows)
+
+-- Mixed functions and ConvertRowtypeExpr testing
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+NOTICE: v wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+NOTICE: s wxyz_child
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+NOTICE: s wxyz_child2
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+ (1,{2},t,3)
+(4 rows)
+
+-- Mixed functions and CASE expressions testing
+-- should not be precalculated
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x;
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+NOTICE: v boolean
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x;
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: v
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+NOTICE: s2 boolean
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+NOTICE: s
+NOTICE: s
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and RowCompareExpr testing
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and COALESCE expressions testing
+-- should not be precalculated
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x;
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+NOTICE: v2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+NOTICE: s2
+NOTICE: s2
+ x_stl2
+--------
+ 2
+ 2
+ 2
+ 2
+(4 rows)
+
+-- Mixed functions and GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+NOTICE: v
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 3
+ 3
+ 3
+ 3
+(4 rows)
+
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+NOTICE: s
+NOTICE: s2
+ x_stl2
+--------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+ERROR: unsupported XML feature
+LINE 1: SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+NOTICE: v text xml
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+NOTICE: v text xml content
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: v xml
+ERROR: unsupported XML feature
+LINE 1: SELECT '<bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT '<bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_vlt_xml() line 4 at RETURN
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+NOTICE: v xml
+ERROR: unsupported XML feature
+LINE 1: SELECT '<bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT '<bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_vlt_xml() line 4 at RETURN
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+NOTICE: v xml content
+ERROR: unsupported XML feature
+LINE 1: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_vlt_xml_content() line 4 at RETURN
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+NOTICE: v xml content
+ERROR: unsupported XML feature
+LINE 1: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_vlt_xml_content() line 4 at RETURN
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+ERROR: unsupported XML feature
+LINE 1: SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+NOTICE: s text xml
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+NOTICE: s xml content
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+NOTICE: s xml
+ERROR: unsupported XML feature
+LINE 1: SELECT '<bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT '<bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_stl_xml() line 4 at RETURN
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+NOTICE: s xml
+ERROR: unsupported XML feature
+LINE 1: SELECT '<bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT '<bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_stl_xml() line 4 at RETURN
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+NOTICE: s xml content
+ERROR: unsupported XML feature
+LINE 1: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_stl_xml_content() line 4 at RETURN
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+NOTICE: s xml content
+ERROR: unsupported XML feature
+LINE 1: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+QUERY: SELECT 'abc<foo>bar</foo><bar>foo</bar>'::xml
+CONTEXT: PL/pgSQL function x_stl_xml_content() line 4 at RETURN
+-- Mixed functions and NullTest expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x;
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+NOTICE: v
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x;
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x;
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+NOTICE: v wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+NOTICE: s
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+NOTICE: s wxyz
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and BooleanTest expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x;
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+NOTICE: v boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+NOTICE: s2 boolean
+NOTICE: s2 boolean
+ x_stl2_boolean
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and CoerceToDomain expressions testing
+-- should not be precalculated
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x;
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+NOTICE: v
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x;
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+NOTICE: v
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x;
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+NOTICE: v
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x;
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+NOTICE: v
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_no_check
+ x_stl2_my_integer_no_check
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+NOTICE: s
+NOTICE: s2 my_integer_not_null
+ x_stl2_my_integer_not_null
+----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+NOTICE: s
+NOTICE: equal integers stable
+NOTICE: s2 my_integer_stl_check
+ x_stl2_my_integer_stl_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+NOTICE: s
+NOTICE: equal integers immutable
+NOTICE: s2 my_integer_imm_check
+ x_stl2_my_integer_imm_check
+-----------------------------
+ 1
+ 1
+ 1
+ 1
+(4 rows)
+
+SET track_functions TO DEFAULT;
+-- ROW() expressions with dropped columns testing
+ALTER TABLE wxyz DROP COLUMN z;
+-- Update some functions
+CREATE OR REPLACE FUNCTION public.x_stl2_wxyz (
+ wxyz
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 wxyz';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- ROW() expressions testing
+SELECT x_stl2_wxyz((1, '{2}', TRUE)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE)) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_wxyz((1, '{2}', TRUE)::wxyz) FROM x;
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+-- Mixed functions and ROW() expressions testing
+-- should not be precalculated
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE)) FROM x;
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+NOTICE: v
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE)) FROM x;
+NOTICE: s
+NOTICE: s2 wxyz
+ x_stl2_wxyz
+-------------
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+ (1,{2},t)
+(4 rows)
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+BEGIN;
+SELECT simple();
+ simple
+--------
+ 4
+(1 row)
+
+INSERT INTO x VALUES (5);
+SELECT simple();
+ simple
+--------
+ 5
+(1 row)
+
+ROLLBACK;
+-- Prepared statements testing
+PREPARE test_x_imm2 (integer) AS SELECT x_imm2(x_imm2($1)) FROM x;
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+NOTICE: i2
+NOTICE: i2
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+ QUERY PLAN
+---------------
+ Seq Scan on x
+(1 row)
+
+-- Drop tables and domains for testing
+DROP TABLE x;
+DROP FUNCTION x_vlt_wxyz, x_vlt_wxyz_child, x_vlt_wxyz_child2;
+DROP FUNCTION x_stl_wxyz, x_stl_wxyz_child, x_stl_wxyz_child2, x_stl2_wxyz;
+DROP TABLE wxyz, wxyz_child, wxyz_child2;
+DROP FUNCTION x_stl2_no_columns;
+DROP TABLE no_columns, no_columns_child, no_columns_child2;
+DROP FUNCTION x_stl2_my_integer_no_check;
+DROP DOMAIN my_integer_no_check;
+DROP FUNCTION x_stl2_my_integer_not_null;
+DROP DOMAIN my_integer_not_null;
+DROP FUNCTION x_stl2_my_integer_vlt_check;
+DROP FUNCTION x_stl2_array_my_integer_vlt_check;
+DROP DOMAIN my_integer_vlt_check;
+DROP FUNCTION x_stl2_my_integer_stl_check;
+DROP FUNCTION x_stl2_array_my_integer_stl_check;
+DROP DOMAIN my_integer_stl_check;
+DROP FUNCTION x_stl2_my_integer_imm_check;
+DROP DOMAIN my_integer_imm_check;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index e224977..0e893df 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -116,7 +116,7 @@ test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid c
# ----------
# Another group of parallel tests
# ----------
-test: identity partition_join partition_prune reloptions hash_part
+test: identity partition_join partition_prune reloptions hash_part precalculate_stable_functions
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 9fc5f1a..54470ea 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -184,5 +184,6 @@ test: partition_join
test: partition_prune
test: reloptions
test: hash_part
+test: precalculate_stable_functions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/precalculate_stable_functions.sql b/src/test/regress/sql/precalculate_stable_functions.sql
new file mode 100644
index 0000000..9437f24
--- /dev/null
+++ b/src/test/regress/sql/precalculate_stable_functions.sql
@@ -0,0 +1,2117 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create types and tables for testing
+
+CREATE TYPE my_integer AS (value integer);
+CREATE TYPE composite_type AS (first integer, second integer[], third boolean);
+
+CREATE TABLE x (x integer);
+INSERT INTO x SELECT generate_series(1, 4) x;
+
+CREATE TABLE wxyz (w integer, x integer[], y boolean, z integer);
+CREATE TABLE wxyz_child () INHERITS (wxyz);
+CREATE TABLE wxyz_child2 (a integer, b integer) INHERITS (wxyz);
+
+CREATE TABLE no_columns ();
+CREATE TABLE no_columns_child () INHERITS (no_columns);
+CREATE TABLE no_columns_child2 (a integer, b integer) INHERITS (no_columns);
+
+-- Create volatile functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_array_integer (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_boolean (
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v boolean';
+ RETURN TRUE;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz (
+)
+RETURNS wxyz VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child (
+)
+RETURNS wxyz_child VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_wxyz_child2 (
+)
+RETURNS wxyz_child2 VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_oid (
+)
+RETURNS oid VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_my_integer (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_text_xml_instruction_content (
+)
+RETURNS text VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_xml (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_xml_content (
+)
+RETURNS xml VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt2 (
+ integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'v2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+ integer,
+ integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers volatile';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+ my_integer,
+ my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer volatile';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_vlt (
+ integer
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer volatile';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_vlt (
+ my_integer
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer volatile';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create stable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's my_integer';
+ RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_array_integer (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's array_integer';
+ RETURN '{2, 3}'::integer[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz (
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child (
+)
+RETURNS wxyz_child STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child';
+ RETURN '(1, {2}, TRUE, 3)'::wxyz_child;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_wxyz_child2 (
+)
+RETURNS wxyz_child2 STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's wxyz_child2';
+ RETURN '(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_oid (
+)
+RETURNS oid STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's oid';
+ RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text integer';
+ RETURN 1::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_my_integer (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text my_integer';
+ RETURN '(1)'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml';
+ RETURN '<?xml version="1.0"?><book><title>Manual</title></book>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_text_xml_instruction_content (
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's text xml instruction content';
+ RETURN 'echo "hello world";'::text;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_xml (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml';
+ RETURN '<bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_xml_content (
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's xml content';
+ RETURN 'abc<foo>bar</foo><bar>foo</bar>'::xml;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+ integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+ integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+ boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 boolean';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_array_integer (
+ integer[]
+)
+RETURNS integer[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_array_oid (
+ oid[]
+)
+RETURNS oid[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_oid';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_wxyz (
+ wxyz
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 wxyz';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_composite_type (
+ composite_type
+)
+RETURNS composite_type STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 composite_type';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer (
+ my_integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_no_columns (
+ no_columns
+)
+RETURNS no_columns STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 no_columns';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_name (
+ name
+)
+RETURNS name STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 name';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_xml (
+ xml
+)
+RETURNS xml STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 xml';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_text (
+ text
+)
+RETURNS text STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 text';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+ integer,
+ integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers stable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+ boolean,
+ boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans stable strict';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+ my_integer,
+ my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer stable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.cast_integer_as_my_integer_stl (
+ integer
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast integer as my_integer stable';
+ RETURN ROW($1)::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.cast_my_integer_as_integer_stl (
+ my_integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'cast my_integer as integer stable';
+ RETURN $1.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN (SELECT max(x) from x);
+END
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+ RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create immutable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+ integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+ integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 strict';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_imm2_my_integer (
+ my_integer
+)
+RETURNS my_integer IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'i2 my_integer';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+ integer,
+ integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal integers immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_booleans_imm (
+ boolean,
+ boolean
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal booleans immutable';
+ RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+ my_integer,
+ my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 'equal my_integer immutable';
+ RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create operators for testing
+
+CREATE OPERATOR === (
+ PROCEDURE = equal_integers_vlt,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_integers_stl,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_integers_imm,
+ LEFTARG = integer,
+ RIGHTARG = integer
+);
+
+CREATE OPERATOR ==== (
+ PROCEDURE = equal_booleans_stl_strict,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+
+CREATE OPERATOR ===== (
+ PROCEDURE = equal_booleans_imm,
+ LEFTARG = boolean,
+ RIGHTARG = boolean
+);
+
+-- Create domains for testing
+
+CREATE DOMAIN my_integer_no_check AS integer;
+CREATE DOMAIN my_integer_not_null AS integer;
+CREATE DOMAIN my_integer_vlt_check AS integer CHECK (VALUE === 1);
+CREATE DOMAIN my_integer_stl_check AS integer CHECK (VALUE ==== 1);
+CREATE DOMAIN my_integer_imm_check AS integer CHECK (VALUE ===== 1);
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_no_check (
+ my_integer_no_check
+)
+RETURNS my_integer_no_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_no_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_not_null (
+ my_integer_not_null
+)
+RETURNS my_integer_not_null STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_not_null';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_vlt_check (
+ my_integer_vlt_check
+)
+RETURNS my_integer_vlt_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_vlt_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_stl_check (
+ my_integer_stl_check
+)
+RETURNS my_integer_stl_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_stl_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_my_integer_imm_check (
+ my_integer_imm_check
+)
+RETURNS my_integer_imm_check STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 my_integer_imm_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_array_my_integer_vlt_check (
+ my_integer_vlt_check[]
+)
+RETURNS my_integer_vlt_check[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_my_integer_vlt_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_array_my_integer_stl_check (
+ my_integer_stl_check[]
+)
+RETURNS my_integer_stl_check[] STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 array_my_integer_stl_check';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Functions testing
+
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+SELECT x_stl() FROM x;
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM x WHERE x_stl() < x;
+
+-- JOIN/ON clause testing
+
+-- should not be precalculated
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x;
+
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+SELECT x_stl2(1) FROM x;
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+
+SELECT x_stl2(x_stl()) FROM x;
+SELECT x_imm2(x_stl()) FROM x;
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+
+-- Operators testing
+
+SELECT 1 === 2 FROM x; -- should not be precalculated
+SELECT 1 ==== 2 FROM x;
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+SELECT x_stl() ==== 1 FROM x;
+
+-- IS (NOT) DISTINCT FROM expression testing
+
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x;
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer FROM x;
+SELECT '(1)'::my_integer IS NOT DISTINCT FROM '(2)'::my_integer FROM x;
+
+-- IS (NOT) DISTINCT FROM expressions with null arguments testing
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM 1) FROM x;
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM 1) FROM x;
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL)) FROM x;
+SELECT x_stl2_boolean(x_stl2(NULL) IS NOT DISTINCT FROM x_stl2(NULL)) FROM x;
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+
+-- NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer) FROM x;
+
+-- NULLIF expressions with null arguments testing
+SELECT x_stl2(NULLIF(1, NULL)) FROM x;
+SELECT x_stl2(NULLIF(NULL::integer, NULL)) FROM x;
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+
+-- should not be precalculated
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x;
+
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+
+SELECT 1 === ANY ('{2, 3}') FROM x; -- should not be precalculated
+SELECT 1 === ALL ('{2, 3}') FROM x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+SELECT 1 ==== ANY ('{2, 3}') FROM x;
+SELECT 1 ==== ALL ('{2, 3}') FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+SELECT 1 ===== ANY ('{2, 3}') FROM x;
+SELECT 1 ===== ALL ('{2, 3}') FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_imm,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+SELECT 1 ==== ANY ('{2, NULL}') FROM x;
+SELECT x_stl2_boolean(1 ==== ANY (NULL)) FROM x;
+SELECT NULL ==== ANY ('{2, 3}'::integer[]) FROM x;
+SELECT NULL ==== ANY ('{2, NULL}'::integer[]) FROM x;
+SELECT x_stl2_boolean(NULL::integer ==== ANY (NULL)) FROM x;
+
+SELECT 1 ==== ALL ('{2, NULL}') FROM x;
+SELECT x_stl2_boolean(1 ==== ALL (NULL)) FROM x;
+SELECT NULL ==== ALL ('{2, 3}'::integer[]) FROM x;
+SELECT NULL ==== ALL ('{2, NULL}'::integer[]) FROM x;
+SELECT x_stl2_boolean(NULL::integer ==== ALL (NULL)) FROM x;
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM x;
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM x;
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM x;
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+
+-- should not be precalculated
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x;
+
+-- should not be precalculated
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x;
+
+-- should not be precalculated
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x;
+
+-- should not be precalculated
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x;
+
+-- should not be precalculated
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+-- Boolean expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x;
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+
+-- ARRAY[] expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[x_vlt(), 2]) FROM x;
+
+SELECT x_stl2_array_integer(ARRAY[x_stl(), 2]) FROM x;
+
+-- Multidimensional ARRAY[] expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[[x_vlt(), 2], [3, 4]]) FROM x;
+
+SELECT x_stl2_array_integer(ARRAY[[x_stl(), 2], [3, 4]]) FROM x;
+
+-- Array subscripting operations testing
+
+SELECT x_stl2(('{1, 2}'::integer[])[1]) FROM x;
+SELECT x_stl2_array_integer(('{1, 2}'::integer[])[:]) FROM x;
+
+-- Mixed functions and array subscripting operations testing
+
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x;
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+
+-- FieldSelect expressions testing
+
+SELECT x_stl2(('(1, {2}, TRUE, 3)'::wxyz).w) FROM x;
+SELECT x_stl2(('(1)'::my_integer).value) FROM x;
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+
+-- ROW() expressions testing
+
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)) FROM x;
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE, 3)) FROM x;
+SELECT x_stl2_wxyz((1, '{2}', TRUE, 3)::wxyz) FROM x;
+
+SELECT x_stl2_composite_type((1, '{2}', TRUE)) FROM x;
+SELECT x_stl2_composite_type(ROW(1, '{2}', TRUE)) FROM x;
+SELECT x_stl2_composite_type((1, '{2}', TRUE)::composite_type) FROM x;
+
+-- Mixed functions and ROW() expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x;
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+
+-- RelabelType expressions testing
+
+-- should not be precalculated
+SELECT x_stl2(x_vlt_oid()::integer) FROM x;
+
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+
+-- CoerceViaIO expressions testing
+
+SELECT x_stl2_my_integer('(1)'::text::my_integer) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x;
+
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+
+-- Mixed functions and CoerceViaIO expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x;
+
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+
+-- ArrayCoerce expressions testing
+
+-- Binary-coercible types:
+
+-- should not be precalculated
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x;
+
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_vlt;
+
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x; -- should not be precalculated
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+
+SELECT '{1, 2}'::integer[]::my_integer[] FROM x;
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Not binary-coercible types:
+-- create cast here because we will drop and reuse it several times
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+
+-- should not be precalculated
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+
+-- should not be precalculated
+SELECT x_vlt_array_integer()::my_integer[] FROM x;
+
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+
+-- ConvertRowtypeExpr testing
+
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3)'::wxyz_child::wxyz) FROM x;
+SELECT x_stl2_wxyz('(1, {2}, TRUE, 3, 4, 5)'::wxyz_child2::wxyz) FROM x;
+
+SELECT x_stl2_no_columns('()'::no_columns_child::no_columns) FROM x;
+SELECT x_stl2_no_columns('(1, 2)'::no_columns_child2::no_columns) FROM x;
+
+-- Mixed functions and ConvertRowtypeExpr testing
+
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+
+-- CASE expressions testing
+
+-- should not be precalculated
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x;
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+
+-- RowCompareExpr testing
+
+SELECT x_stl2_boolean((1, 2) < (1, 3)) FROM x;
+
+-- Mixed functions and RowCompareExpr testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x;
+
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+
+-- COALESCE expressions testing
+
+-- should not be precalculated
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x;
+
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+
+-- GREATEST and LEAST functions testing
+
+SELECT x_stl2(GREATEST(2, 1, 3)) FROM x;
+SELECT x_stl2(LEAST(2, 1, 3)) FROM x;
+
+-- Mixed functions and GREATEST and LEAST functions testing
+
+-- should not be precalculated
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x;
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+
+-- SQLValueFunction testing
+
+CREATE ROLE regress_testrol2 SUPERUSER;
+CREATE ROLE regress_testrol1 SUPERUSER LOGIN IN ROLE regress_testrol2;
+
+\c -
+SET SESSION AUTHORIZATION regress_testrol1;
+SET ROLE regress_testrol2;
+
+SELECT x_stl2_boolean(date(now()) = current_date) FROM x;
+
+SELECT x_stl2_boolean(now()::timetz = current_time) FROM x;
+SELECT x_stl2_boolean(now()::timetz(2) = current_time(2)) FROM x; -- precision
+
+SELECT x_stl2_boolean(now() = current_timestamp) FROM x;
+
+-- precision
+SELECT x_stl2_boolean(
+ length(current_timestamp::text) >= length(current_timestamp(0)::text)
+)
+FROM x;
+
+SELECT x_stl2_boolean(now()::time = localtime) FROM x;
+SELECT x_stl2_boolean(now()::time(2) = localtime(2)) FROM x; -- precision
+
+SELECT x_stl2_boolean(now()::timestamp = localtimestamp) FROM x;
+
+-- precision
+SELECT x_stl2_boolean(now()::timestamp(2) = localtimestamp(2)) FROM x;
+
+SELECT x_stl2_name(current_role) FROM x;
+SELECT x_stl2_name(current_user) FROM x;
+SELECT x_stl2_name(user) FROM x;
+SELECT x_stl2_name(session_user) FROM x;
+SELECT x_stl2_name(current_catalog) FROM x;
+SELECT x_stl2_name(current_schema) FROM x;
+
+\c
+DROP ROLE regress_testrol1, regress_testrol2;
+
+-- Xml expressions testing
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', '<bar>foo</bar>')) FROM x;
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), 'cont', 'ent')
+)
+FROM x;
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, 123 AS bar)) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>'
+))
+FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT 'abc<foo>bar</foo><bar>foo</bar>')) FROM x;
+
+SELECT x_stl2_xml(XMLPI(name php, 'echo "hello world";')) FROM x;
+
+SELECT x_stl2_xml(XMLROOT(
+ '<?xml version="1.0"?><content>abc</content>',
+ version '1.0',
+ standalone yes
+))
+FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(
+ DOCUMENT '<?xml version="1.0"?><book><title>Manual</title></book>' AS text
+))
+FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(
+ CONTENT 'abc<foo>bar</foo><bar>foo</bar>' AS text
+))
+FROM x;
+
+SELECT x_stl2_boolean('abc<foo>bar</foo><bar>foo</bar>' IS DOCUMENT) FROM x;
+
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+
+-- NullTest expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x;
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+
+-- BooleanTest expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x;
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+
+-- CoerceToDomain expressions testing
+
+SELECT x_stl2_my_integer_no_check(1::my_integer_no_check) FROM x;
+SELECT x_stl2_my_integer_not_null(1::my_integer_not_null) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_vlt_check(1::my_integer_vlt_check) FROM x;
+
+SELECT x_stl2_my_integer_stl_check(1::my_integer_stl_check) FROM x;
+SELECT x_stl2_my_integer_imm_check(1::my_integer_imm_check) FROM x;
+
+-- Mixed functions and CoerceToDomain expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x;
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+
+-- Mixed ArrayCoerce and CoerceToDomain expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_array_my_integer_vlt_check(
+ '{1, 1}'::integer[]::my_integer_vlt_check[]
+)
+FROM x;
+
+SELECT x_stl2_array_my_integer_stl_check(
+ '{1, 1}'::integer[]::my_integer_stl_check[]
+)
+FROM x;
+
+-- Tracking functions testing
+
+SET track_functions TO 'all';
+
+-- Simple functions testing
+SELECT x_vlt() FROM x; -- should not be precalculated
+SELECT x_stl() FROM x;
+
+-- WHERE clause testing
+SELECT x_vlt() FROM x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM x WHERE x_stl() < x;
+
+-- JOIN/ON clause testing
+
+-- should not be precalculated
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_vlt() < x;
+
+SELECT * FROM x JOIN generate_series(1, 2) y ON x_stl() < x;
+
+-- Functions with constant arguments testing
+SELECT x_vlt2(1) FROM x; -- should not be precalculated
+SELECT x_stl2(1) FROM x;
+
+-- Nested functions testing
+SELECT x_stl2(x_vlt()) FROM x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM x; -- should not be precalculated
+
+SELECT x_stl2(x_stl()) FROM x;
+SELECT x_imm2(x_stl()) FROM x;
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM x;
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM x;
+
+-- Operators testing
+SELECT 1 === 2 FROM x; -- should not be precalculated
+SELECT 1 ==== 2 FROM x;
+
+-- Strict operators testing
+SELECT x_stl2_boolean(NULL) ==== TRUE FROM x;
+SELECT x_stl2_boolean(NULL) ===== TRUE FROM x;
+
+-- Mixed functions and operators testing
+SELECT x_stl2_boolean(1 === 2) FROM x; -- should not be precalculated
+SELECT x_stl2_boolean(1 ==== 2) FROM x;
+
+SELECT x_vlt() ==== 1 FROM x; -- should not be precalculated
+SELECT x_stl() ==== 1 FROM x;
+
+-- Mixed functions and IS (NOT) DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT equal_booleans_stl_strict(
+ ('(1)'::my_integer IS DISTINCT FROM '(1)'::my_integer),
+ ('(1)'::my_integer IS NOT DISTINCT FROM '(1)'::my_integer)
+)
+FROM x;
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+
+-- should not be precalculated
+SELECT (x_vlt_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+
+SELECT (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer) FROM x;
+SELECT (x_stl_my_integer() IS NOT DISTINCT FROM '(1)'::my_integer) FROM x;
+
+-- Mixed functions and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT equal_my_integer_stl(
+ NULLIF('(1)'::my_integer, '(2)'::my_integer),
+ NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM x;
+
+-- should not be precalculated
+SELECT NULLIF(x_vlt_my_integer(), '(2)'::my_integer) FROM x;
+
+SELECT NULLIF(x_stl_my_integer(), '(2)'::my_integer) FROM x;
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ANY ('{2, 3}')) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(1 === ALL ('{2, 3}')) FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_vlt,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+
+SELECT x_stl2_boolean(1 ==== ANY ('{2, 3}')) FROM x;
+SELECT x_stl2_boolean(1 ==== ALL ('{2, 3}')) FROM x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+ PROCEDURE = equal_my_integer_stl,
+ LEFTARG = my_integer,
+ RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+ '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+)
+FROM x;
+
+SELECT x_vlt() ==== ANY ('{2, 3}') FROM x; -- should not be precalculated
+SELECT x_vlt() ==== ALL ('{2, 3}') FROM x; -- should not be precalculated
+
+SELECT 1 ==== ANY (x_vlt_array_integer()) FROM x; -- should not be precalculated
+SELECT 1 ==== ALL (x_vlt_array_integer()) FROM x; -- should not be precalculated
+
+-- should not be precalculated
+SELECT x_vlt_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+SELECT x_stl() ==== ANY ('{2, 3}') FROM x;
+SELECT x_stl() ==== ALL ('{2, 3}') FROM x;
+
+SELECT 1 ==== ANY (x_stl_array_integer()) FROM x;
+SELECT 1 ==== ALL (x_stl_array_integer()) FROM x;
+
+SELECT x_stl_my_integer() IN ('(2)'::my_integer, '(3)'::my_integer) FROM x;
+
+-- Mixed functions and boolean expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() AND x_stl2_boolean(TRUE)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() OR x_stl2_boolean(TRUE)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NOT x_vlt_boolean()) FROM x;
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) AND x_stl2_boolean(TRUE)) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) OR x_stl2_boolean(TRUE)) FROM x;
+SELECT x_stl2_boolean(NOT x_stl2_boolean(TRUE)) FROM x;
+
+-- Mixed functions and ARRAY[] expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_array_integer(ARRAY[x_vlt()]) FROM x;
+
+SELECT x_stl2_array_integer(ARRAY[x_stl()]) FROM x;
+
+-- Mixed functions and array subscripting operations testing
+
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[x_vlt()]) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2((x_vlt_array_integer())[1]) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_array_integer((x_vlt_array_integer())[:]) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2(('{1, 2}'::integer[])[x_vlt()]) FROM x;
+
+SELECT x_stl2((x_stl_array_integer())[x_stl()]) FROM x;
+SELECT x_stl2((x_stl_array_integer())[1]) FROM x;
+SELECT x_stl2_array_integer((x_stl_array_integer())[:]) FROM x;
+SELECT x_stl2(('{1, 2}'::integer[])[x_stl()]) FROM x;
+
+-- Mixed functions and FieldSelect expressions testing
+SELECT x_stl2((x_vlt_wxyz()).w) FROM x; -- should not be precalculated
+SELECT x_stl2((x_vlt_my_integer()).value) FROM x; -- should not be precalculated
+
+SELECT x_stl2((x_stl_wxyz()).w) FROM x;
+SELECT x_stl2((x_stl_my_integer()).value) FROM x;
+
+-- Mixed functions and ROW() expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE, 3)) FROM x;
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE, 3)) FROM x;
+
+-- Mixed functions and RelabelType expressions testing
+SELECT x_stl2(x_vlt_oid()::integer) FROM x; -- should not be precalculated
+SELECT x_stl2(x_stl_oid()::integer) FROM x;
+
+-- Mixed functions and CoerceViaIO expressions testing
+
+-- should not be precalculated
+SELECT x_stl2(x_vlt_text_integer()::integer) FROM x;
+
+SELECT x_stl2(x_stl_text_integer()::integer) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_my_integer(x_vlt_text_my_integer()::my_integer) FROM x;
+
+SELECT x_stl2_my_integer(x_stl_text_my_integer()::my_integer) FROM x;
+
+-- Mixed functions and ArrayCoerce expressions testing
+-- Binary-coercible types:
+
+-- should not be precalculated
+SELECT x_stl2_array_oid(x_vlt_array_integer()::oid[]) FROM x;
+
+SELECT x_stl2_array_oid(x_stl_array_integer()::oid[]) FROM x;
+
+-- Not binary-coercible types:
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_vlt;
+
+-- should not be precalculated
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+
+DROP CAST (my_integer AS integer);
+CREATE CAST (my_integer AS integer)
+ WITH FUNCTION cast_my_integer_as_integer_stl;
+
+SELECT x_stl2_array_integer('{(1), (2)}'::my_integer[]::integer[]) FROM x;
+
+DROP CAST (integer AS my_integer);
+CREATE CAST (integer AS my_integer)
+ WITH FUNCTION cast_integer_as_my_integer_stl;
+
+-- should not be precalculated
+SELECT x_vlt_array_integer()::my_integer[] FROM x;
+
+SELECT x_stl_array_integer()::my_integer[] FROM x;
+
+-- Mixed functions and ConvertRowtypeExpr testing
+
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child()::wxyz_child::wxyz) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_wxyz(x_vlt_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+
+SELECT x_stl2_wxyz(x_stl_wxyz_child()::wxyz_child::wxyz) FROM x;
+SELECT x_stl2_wxyz(x_stl_wxyz_child2()::wxyz_child2::wxyz) FROM x;
+
+-- Mixed functions and CASE expressions testing
+
+-- should not be precalculated
+SELECT x_stl2(CASE WHEN x_vlt_boolean() THEN x_vlt() ELSE x_vlt() END) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2(CASE x_vlt() WHEN x_vlt() THEN x_vlt() ELSE x_vlt() END) FROM x;
+
+SELECT x_stl2(CASE WHEN x_stl2_boolean(TRUE) THEN x_stl() ELSE x_stl() END)
+FROM x;
+
+SELECT x_stl2(CASE x_stl() WHEN x_stl() THEN x_stl() ELSE x_stl() END) FROM x;
+
+-- Mixed functions and RowCompareExpr testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt(), 2) < (1, 3)) FROM x;
+
+SELECT x_stl2_boolean((x_stl(), 2) < (1, 3)) FROM x;
+
+-- Mixed functions and COALESCE expressions testing
+
+-- should not be precalculated
+SELECT x_stl2(COALESCE(NULL, x_vlt2(NULL), 2)) FROM x;
+
+SELECT x_stl2(COALESCE(NULL, x_stl2(NULL), 2)) FROM x;
+
+-- Mixed functions and GREATEST and LEAST functions testing
+SELECT x_stl2(GREATEST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+SELECT x_stl2(LEAST(2, x_vlt(), 3)) FROM x; -- should not be precalculated
+
+SELECT x_stl2(GREATEST(2, x_stl(), 3)) FROM x;
+SELECT x_stl2(LEAST(2, x_stl(), 3)) FROM x;
+
+-- Mixed functions and Xml expressions testing
+-- should not be precalculated
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_vlt_xml())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_vlt_xml())
+)
+FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_vlt_xml() AS bar)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_vlt_text_xml())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_vlt_text_xml_content())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLPI(name php, x_vlt_text_xml_instruction_content())) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_xml(XMLROOT(x_vlt_xml(), version '1.0', standalone yes)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_vlt_xml() AS text)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_vlt_xml_content() AS text)) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_xml_content() IS DOCUMENT) FROM x;
+
+SELECT x_stl2_xml(XMLCONCAT('<abc/>', x_stl_xml())) FROM x;
+
+SELECT x_stl2_xml(
+ XMLELEMENT(name foo, xmlattributes('bar' as bar), x_stl_xml())
+)
+FROM x;
+
+SELECT x_stl2_xml(XMLFOREST('abc' AS foo, x_stl_xml() AS bar)) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(DOCUMENT x_stl_text_xml())) FROM x;
+
+SELECT x_stl2_xml(XMLPARSE(CONTENT x_stl_text_xml_content())) FROM x;
+
+SELECT x_stl2_xml(XMLPI(name php, x_stl_text_xml_instruction_content())) FROM x;
+
+SELECT x_stl2_xml(XMLROOT(x_stl_xml(), version '1.0', standalone yes)) FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(DOCUMENT x_stl_xml() AS text)) FROM x;
+
+SELECT x_stl2_text(XMLSERIALIZE(CONTENT x_stl_xml_content() AS text)) FROM x;
+
+SELECT x_stl2_boolean(x_stl_xml_content() IS DOCUMENT) FROM x;
+
+-- Mixed functions and NullTest expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NULL) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt() IS NOT NULL) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NULL) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_wxyz() IS NOT NULL) FROM x;
+
+SELECT x_stl2_boolean(x_stl() IS NULL) FROM x;
+SELECT x_stl2_boolean(x_stl() IS NOT NULL) FROM x;
+
+SELECT x_stl2_boolean(x_stl_wxyz() IS NULL) FROM x;
+SELECT x_stl2_boolean(x_stl_wxyz() IS NOT NULL) FROM x;
+
+-- Mixed functions and BooleanTest expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS TRUE) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT TRUE) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS FALSE) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT FALSE) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS UNKNOWN) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(x_vlt_boolean() IS NOT UNKNOWN) FROM x;
+
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS TRUE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT TRUE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS FALSE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(TRUE) IS NOT FALSE) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS UNKNOWN) FROM x;
+SELECT x_stl2_boolean(x_stl2_boolean(NULL) IS NOT UNKNOWN) FROM x;
+
+-- Mixed functions and CoerceToDomain expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_no_check(x_vlt()::my_integer_no_check) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_not_null(x_vlt()::my_integer_not_null) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_stl_check(x_vlt()::my_integer_stl_check) FROM x;
+
+-- should not be precalculated
+SELECT x_stl2_my_integer_imm_check(x_vlt()::my_integer_imm_check) FROM x;
+
+SELECT x_stl2_my_integer_no_check(x_stl()::my_integer_no_check) FROM x;
+SELECT x_stl2_my_integer_not_null(x_stl()::my_integer_not_null) FROM x;
+SELECT x_stl2_my_integer_stl_check(x_stl()::my_integer_stl_check) FROM x;
+SELECT x_stl2_my_integer_imm_check(x_stl()::my_integer_imm_check) FROM x;
+
+SET track_functions TO DEFAULT;
+
+-- ROW() expressions with dropped columns testing
+
+ALTER TABLE wxyz DROP COLUMN z;
+
+-- Update some functions
+CREATE OR REPLACE FUNCTION public.x_stl2_wxyz (
+ wxyz
+)
+RETURNS wxyz STABLE AS
+$body$
+BEGIN
+ RAISE NOTICE 's2 wxyz';
+ RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- ROW() expressions testing
+SELECT x_stl2_wxyz((1, '{2}', TRUE)) FROM x;
+SELECT x_stl2_wxyz(ROW(1, '{2}', TRUE)) FROM x;
+SELECT x_stl2_wxyz((1, '{2}', TRUE)::wxyz) FROM x;
+
+-- Mixed functions and ROW() expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_wxyz((x_vlt(), '{2}', TRUE)) FROM x;
+
+SELECT x_stl2_wxyz((x_stl(), '{2}', TRUE)) FROM x;
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+
+BEGIN;
+SELECT simple();
+INSERT INTO x VALUES (5);
+SELECT simple();
+ROLLBACK;
+
+-- Prepared statements testing
+
+PREPARE test_x_imm2 (integer) AS SELECT x_imm2(x_imm2($1)) FROM x;
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+EXPLAIN (COSTS OFF) EXECUTE test_x_imm2(2);
+
+-- Drop tables and domains for testing
+
+DROP TABLE x;
+
+DROP FUNCTION x_vlt_wxyz, x_vlt_wxyz_child, x_vlt_wxyz_child2;
+DROP FUNCTION x_stl_wxyz, x_stl_wxyz_child, x_stl_wxyz_child2, x_stl2_wxyz;
+DROP TABLE wxyz, wxyz_child, wxyz_child2;
+
+DROP FUNCTION x_stl2_no_columns;
+DROP TABLE no_columns, no_columns_child, no_columns_child2;
+
+DROP FUNCTION x_stl2_my_integer_no_check;
+DROP DOMAIN my_integer_no_check;
+
+DROP FUNCTION x_stl2_my_integer_not_null;
+DROP DOMAIN my_integer_not_null;
+
+DROP FUNCTION x_stl2_my_integer_vlt_check;
+DROP FUNCTION x_stl2_array_my_integer_vlt_check;
+DROP DOMAIN my_integer_vlt_check;
+
+DROP FUNCTION x_stl2_my_integer_stl_check;
+DROP FUNCTION x_stl2_array_my_integer_stl_check;
+DROP DOMAIN my_integer_stl_check;
+
+DROP FUNCTION x_stl2_my_integer_imm_check;
+DROP DOMAIN my_integer_imm_check;
--
2.7.4
On 31 December 2017 at 06:55, Marina Polyakova <m.polyakova@postgrespro.ru>
wrote:
Secondly, here there's a sixth version of the patch for the
precalculation of
stable or immutable functions, stable or immutable operators and other
nonvolatile expressions.
Thanks for your patch, looks quite interesting!
To not send big patch I have split it (that's why version starts with the
first again) and here I send infrastructure patch which includes:
Yeah, but it's still 18k lines :) After the first quick glance I have a few
small questions.
If I call a stable function from a query and subquery, looks like it's
cached:
```
=# select stable_with_int(1) from (select stable_with_int(1) from x) q;
NOTICE: 00000: stable with int
LOCATION: exec_stmt_raise, pl_exec.c:3353
stable_with_int
-----------------
1
1
1
1
(4 rows)
```
But the same from CTE works different, is it supposed to be like that?
```
=# with data as (select stable_with_int(1) from x) select
stable_with_int(1) from data;
NOTICE: 00000: stable with int
LOCATION: exec_stmt_raise, pl_exec.c:3353
NOTICE: 00000: stable with int
LOCATION: exec_stmt_raise, pl_exec.c:3353
stable_with_int
-----------------
1
1
1
1
(4 rows)
```
Also I see this pattern quite some time, maybe it makes sense to move it to
a function?
```
+ /* create and return CachedExpr */
+ CachedExpr *new_node = makeNode(CachedExpr);
+ new_node->subexpr = (CacheableExpr *) current_node;
+
+ context->root->hasCachedExpr = true;
+
+ return (Node *) new_node;
```
Thanks for your patch, looks quite interesting!
Glad to hear it :)
To not send big patch I have split it (that's why version starts
with the
first again) and here I send infrastructure patch which includes:
Yeah, but it's still 18k lines :)
Here 13k lines - 2 sets of expected results for regression tests..)
After the first quick glance I have
a few
small questions.If I call a stable function from a query and subquery, looks like it's
cached:```
=# select stable_with_int(1) from (select stable_with_int(1) from x)
q;
NOTICE: 00000: stable with int
LOCATION: exec_stmt_raise, pl_exec.c:3353
stable_with_int
-----------------
1
1
1
1
(4 rows)
```But the same from CTE works different, is it supposed to be like that?
```
=# with data as (select stable_with_int(1) from x) select
stable_with_int(1) from data;
NOTICE: 00000: stable with int
LOCATION: exec_stmt_raise, pl_exec.c:3353
NOTICE: 00000: stable with int
LOCATION: exec_stmt_raise, pl_exec.c:3353
stable_with_int
-----------------
1
1
1
1
(4 rows)
```
The function is always cached, but in the first example the plan is
simplified so you only get one call of the function in the entire plan.
(In the function subquery_planner, CTE are processed separately by
calling the function SS_process_ctes. Subqueries are simplified a little
later by calling the function pull_up_subqueries; in our case the
function pull_up_simple_subquery is used.)
Also I see this pattern quite some time, maybe it makes sense to move
it to a function?``` + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexpr = (CacheableExpr *) current_node; + + context->root->hasCachedExpr = true; + + return (Node *) new_node; ```
Thanks, I agree with you and I'll change it accordingly.
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hello, hackers!
Here there's a seventh version of the patch for the precalculation of
stable or immutable functions, stable or immutable operators and other
nonvolatile expressions. It is rebased on the top of master and has some
code cleanup [1]Fixed:. See attached patch.
About TPC-H tests using dbt3 (see the attached small archieve):
* they were made based on commit
255f14183ac7bc6a83a5bb00d67d5ac7e8b645f1;
* they take some time so I have used only scales 1 and 4;
* 3 attempts are used to eliminate some noise; so you can see the
minimum, median and maximum execution time for each query on the graphs.
About their results:
* as expected, cached expressions were not used in queries;
* it seems that there is no obvious degradation of performance.
Any suggestions are welcome!
[1]: Fixed:
Also I see this pattern quite some time, maybe it makes sense to move
it to a function?``` + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexpr = (CacheableExpr *) current_node; + + context->root->hasCachedExpr = true; + + return (Node *) new_node; ```Thanks, I agree with you and I'll change it accordingly.
--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
dbt3.tar.gzapplication/x-gzip; name=dbt3.tar.gzDownload
� ��\Z ��PT��6�JhD��Z$(IriP$��,H��3
��(Q$I$�%K���H��g7����{3�}S������Z�>����Z����N�����������������������3scv{[3�����{���x��ON~^������������������������x91����,��pvt2t ��:X��_��_?��?~������4�U���W|�GRO00.kb`\r�\��M���`����PuY_TQ��X�8j:�9�M�W����6���.m(�m&g��^�kVeZ�Mq�V�
D�����U
d����������n��c����������v���I%-uc�J���y���!��z2`��2�}�%:���a` �p���S������(g��e����j����<D����dTT��;�Q.�+o������2)%���������ql�IIA�|��B�t���{�����(�T5T�����;yw�=nn��i��n2��A�PX$���:###��kn"[���{�GGF�����n<�n���m���gF�h��L=�]������G4�#�Z�A-M���������GWi$89����=~<�!����� wX��e��
`��B�!HKsh��[@b��F�������9t+������`���_������ul���
*(.��K����t(��~+��+/�����e��p�I��������@_?��;())i�H�b�@�S��;�g����&j����ih��-������MN�g����u����I��'��?�K\)�W������G��_�4���=\�=ab�&��u���?���f������������_�4
��Yyy?
2�����h������A\ZZZB%��������E����dpA�pfAA�����V^�������_��9�~��S�.�2=;�6V���"��q�-����t�����>��r��t+�c/� I��q�b�f�Y�G,Z7C��
cm�_�x��C%�"?GP������e�gahmo�z��<v`899��
��S}X4FP5z���v�0%Mr��z66��DQ�����#���6���jQ�99��jyR]������C��:����3�:�[���*%�|����N~O��������������� ��S����n~�J1�8���M�.��YkGG�`4��O{ H������XrS��&�}���G;�����%�qh��0o��M���U�o}f}o�c�Nf2hg�� ��~�w^��{,��Qf]�Z���8������,��=���\�V9!�^�<<i��5�k]N]��[EP�Ufe(%Y�:L���S6�6���h�d����xN s]����`����o �������^8����s�-�Ez�{�J��`��K-v7.�[A�A^l�A�oci ��.��DD�Jm�81�����AN~����^�L�'Q�O�v�q(-��l����5��U�����/��@�E�� 5%e�ho/l����Z �-�b*����������I�^����:!<��F^��C��y�}JE`���7�����nc!A��;8���������"���Q���Z������'*�"�mb��q|)b��+=�+�#������Z�I])��� ��K.�7�;��t"""�]��.���+��T���Dw���L7�j������;{q���e:Wi��/ ��D���d�7}�[�3���_Z�oT��>00H��{o �����g�rE�99%����Z�������X�������!�$Z@A/;������l�.<��������4pm�!H������hk�� 5�7�;;;�Y-��Pvd^�d����'��,��O�����yC���@H���^T�bf����3-+/���1*�H�90�J.,�d��!4TW��=���g�����"��m�9�b�����"�d��W�R��HU��a��z[�,,,���?�i���x���[�<�%{�J#�.������J��`��dY����#X���^��(����+�*+3��eh��%��l$=E��t�.���v3Nz
��������F�/����+�����������?�b�t�0��:�J�<���_������������&'�F���?9�/e F���U������Q��.��9F{�f�L����:j�����8$���9�HR�yN-ek��JNM�������p��y�� ��������A��n��(�l�����=
���zA�8��'�S�����-3���X�o)���9H@B�F��z��R��rZ<�]�J7���{�<��`���U@�p���kp� ����)>22M_��8��#I��y'u�EA���3�nw��R�M�`����]����*����Hm���W� ���4C�����\L��1
G�A������
�X����>8��dg�WP����q�cG^v2��X���������)�;�*u,W��;�;��>%$fL�&�2%-����>�������e,:�W.i)�ES�& ���T��S�Ak��Va ���\@$� B�X#���� �U��Gl�,�����:�[��Y�H��8=>��������Her�s0�A������O$8�M�QAP���j[uo�RO���d=�B����Js����TiK��'��ia���ir����~�2�f���d�>QQA�=�"�(�A�+��+W�JNm��U�2 9��Cc�a������z��������
h����:���r
)o�����IC>�%�zX��W�86�m
�L��>��Zr��Y��@�|8�%J��2@�v�W�(!��,�]x�2Fw�7Y>�a�v��"PA�dwU������+�����!��/_��
�77�W9}II�3Bvj��9��et�����������4�jU�u���s�_B����� � }��~�Z#O�=C����j���KEN�E���pYY������%8����_C]}�EZ������7��!N���L���V����|
}���/&?z����m�WN� �y��>��Y���������M��~{�������;g���������F�I�P�AGc{�,|��.)��� ��hn��������������}|�F91X�A����<���m2�!hC-�wWb}�:b8~��=hQ=Ns�z��5������j�5K���T����_��755577���Hu��H���2��I��!����>$4$44 ����v�O{5>>^TPY)(*�� �+1��!6�P���4PWi.�2������w'i�"�T���t���V�[�"��t����=��'�<#�-��[���"�a���m#��n����Y8�'**J�����O�J@H���i�6����RRn�������~���1@�gI
C<4�2.(���_NN�
���`GN���������#''�[���>?}�PPZ�����`\�R����7~�x��i UR����"����k�����-,/��������S��ih$�z�G����A�����������OS
rs�`0�.�N�)nrkWW���&��Z����oZZ�� Q-f�I<�2<�uo� p��HK��/<z�������zU7HH&?&$(���o/(���R�
%(�^��)*���C�����@�`G.�m� ������C�v<�f��OT�z��F��JJ���;���o��y��M
�w��8�B��'��� �����vS��tk���(F���7s��c+��q�:W����;�C�:��a�i'
���f��z8���`��g��!-PE��j���o5����K�[>|xW��+���PZZIA�:�k�5�����%e99?�
YEu5��PT?DD8����yM51l�G��F���TU�6g��###[[[�H�3��RH���wk��365
���>�J�`s�3�����
����(�FB�G��������@Q�A<==A�YUU�9W}9�Ii�$g��d�����w��� P��V,**=`}������@`� y��������51��BAf�#��(O"�. ���p��w2����3����iCppp��&j=�3 ��DB ��A���(sj�t83�g��������0k9���6���7�����||������F��'&�_d��E��m{�&����F��6k� �����7JJJ3S��W�@���������d��������j���1Q�������� ,�p���n1�z�D������L�}ddd��������J#��!eY%�����'&\&�)e"J`��w�?�����@t3LX�g��{o?�7�����b������m���F(�I��>���u�Pv�}Q5���,�����I!a�Z�D�����������px����\��m�������h�p��){�����>�0�jB�p�,s�i�$���^�7X�����D
9%�����������n��e�;IuI��������!1dhP�f1 �Y\\���?]�8o,<
�_�a�@��k���i��NI
���J�\r�DSi�7@�CP��������{h^K����D�5��.������_��P�y�����@�k1�����0!jkk ��O���� ��V����56
�C���yy���gcB��j�u�R�~�#�c6�wL,�� �
�N�]���O��C�!���a���!��P�20uH��|���,�ff�z��C���P�JB���\VG����;�'W�����B7��C���������?�w�W�88&���^@Ya��^]Z[�w^�Y>��b���-���81Q��a�g���L1]��DQ����#o����������OOO_��C$��<� v��?6q�����[o�6g�zQ��� aUK����>��� ��E��u�����D&
S�|�J��H==����]m����G��R���-;#���:�N��IA7��J�VCy���@e-vp��-�ih����-)�!i��0hni��^�:�F�V�PE����������HD4���~�����&���c1���pb"�Obl�t/��t5�.����4Q�(=��l�������B�Ts��
�Pw����~r;��"�4�o��{Hu
���Q��a'��A{:0���z[Q|C�n)Q D��/�C�3�pP�jj@���>i�h�[�������}D��]Z22���_V����/yy�321�ys��P�r�(���j)�MW�b�`��t�/=���-DSSS�h�,`?�K����P�I,'C={u�ih����a4z x�'G5I����p���>� ?^��?6`Y�#�E������VTh�����Q^ N��v�x7=�t�_�� �w:Q��+I<����Xe=�d�E��1l���"@�8�JB��>k�2������'G{�z����b[g ���=�yi�EKK�
�~
�����fbr�rJk��]3W����W��x�H�uX��^J�������K9�>~tY��p���� ���BNK_�v,�z1��������`�����gK.8 YYZ�GD����RS�����n^<��6���]e�IqP
O�Ej��U(����������/a?Zf�4�}F+`��vf�j��$Ri���d��v.�l�P�����$�"
jjf���7k�w(�-Kf������%���H$rnN�s7��M����NX�QT@AY�����Z��������������z=�E_�p��=y�����_�����]V ��v�A����v���l'����C��6���QI����C[������v�(���R���
<������'O�4����~D� 78�Oq��h��Q4����K�����$`SDO�$��� HKs�@///���$�<z��/� �w*
�E��M�Dp�Bt�b�������w����K�.Y9�����?f�?��P98�_:3NFRSW��T{���D@�2��k����c�m�U����#����8~�hb�����7����u����+"��K`NB `kmmm7����{l���]T�������Z��bl\{=hM���S�[a}=j���y�(g$ ���������K X
�>c`R#����PSS{��eu������@0��������)6�&�=s���&�K�/���z�bbc����}�u��M���B���$jT?q��|���n+�
AZ���m�A��k��kh�`b�_��;�ON���Sd�4��@����g��]cF��@�4�.&��]�g��
e</>w�b�'�)oZu��Ao��E�|�QC1��u>���O�Y����BO�w7qStF�.��(%�6H���U����lP���&z��!���m�/���@T��,+/�����d��i1>!�V��+lI���-��������) �lk�6�+@� h��� �K�
� �7�$�����8W��#�_����6Z�A8�6�4C#I�����JJ�nLQQj**n�o�g���N�� ��"o[dRJD�4$A�$|���911g�^$���r�P){!�������������?�w���'��������G���� �N&���RuV���7@�H J�.�6|:::q�?�\\����A�]�"tm��)��8V��d�� �^d��Y����\�nV����S�V�� zW��UB!����'>H����3P)��.^[��@�T'��qe�0"bb??������������{�h=(I2R�r@l�P�]k_�z�_��G�fI���!.ntxx�J���F�/����w���|:�*a3��IP��zD]b@ %���-�#�@��A�>��f�<h��������/_d�}CR��d�"*�����,�
ug���$�g9�.<���Yx�yF�}�d���7~��s�U5��z���*G�+�����!01{HvV�y��
���Ep���:�-�s|�`����c�}�!H6��������m����]!����4���G�r����� ���6��X�u�4�����t&�����.=��==�g�)�����C��Z����9z�����3"�3 �]GO�Yu9��)���������
����J�[PE���h��V���f}�IH���n����8�&(������ |�����B N�D�� Vg�(@�f{���w�_$T~��o������
��������[+**���0ii�z��Q(%�6����cP������/%��utt��X\������� ���B_�9@/H�gL��m�{���%u����s�Ktm����������z?/�wT������$)��id�� ���T����a�F=9�����X�=qn���-P�E�/�Y�v��eg'����d�����|v2���7��&�0q'��t����h�r`��U��� ��������9�;�^�"�7��mJ!w�Q�F�{��������i��%�5;����BB�s���>����}�*����l�h��\�g���x6� +B?��=bm��P����>��E\e_(#j���yx6+��Bq�WR(�cl7��s��������H�{�����1��\zJ���kH��R]7�,w�rg�����<b[.-%�S�������gB*��62X\��6��om��ycu��s0FVw�#�(7����y�r�\k~��;\9�*��y���"@E����k������z��\��Uh���;]� �y��,�����>������H�.��*�����??�,$,����A;���L���U��{1h;�����h6��(L�����v,�_��gv#S;O�� ������{�r���',�O�4gL�F����tb ��-���)H^���=�
�CCEem'Ve�X�-U��}Mv-���H������x���F������[Wp��������w'vd^[���TC��zvi~~�T�1�������"��P�ee�|OkB���G�����f�exxxii z�.9���+�������>>>RRR��p��7o������ET���W=�7�S�ud�T�)��6h������O��3������-q�Zo�����p�����+�3����X
Vn�6A��X9/!Z��@9��wvvz�K�|}}}q�BV�8�����v����^,��;A���F~?���m�����2�8����e��.�%++���m��$O�q.D>���+���r=��1��|y��������������L��4�%�W�[l����)�{�DD9X]��n��|U�2q���g�������\N��������������]���=!7f(���ExY��9� �'�|�y�WK���7~����G>�&����=����*�OZ~W���)C��3��Wx��5��<\_"��N�����q��
B���������&�� l@K?L�3����+{f#������:8��"�1����2��$V+��D��C�4hvv���oU�����g6-�aR1��mPf'�P�2.��;<NK2r���My+�����@#s����TZ^���@�7������NDM��pym�]` q�����}�3zj�t*����2L�+��kU� �d��5�����A�������r��������7��|�*&6C'J�~�h��<]A���cs^>��p������z���������=P�����������+#-�bD�~z\9fP��EK�����[��j������<2-���h-y6~7��-3#�CF���?�Tf�NTKuu�@~M8s���meO����
�1��)��N=�v*��vvw �][�������y��zi�*��1�������fz�=13-�X���G�\�Y���
<��;{{�5��R��=�Q��OZU�vvv*%��G ���{]��7v���<5�����+o:25�1���>o(}&%��/�}q�cbpj���()�VM �v����PL���� ##�����#��(����"���m_9_����n���>�������� "������w��^�.-���w3��U�;�$��������5u��k��:��*ak?�=���$E�ED����;�c��>��JEC�������U����H;��*w�;g{�K{����$�amm�:3glmm��FG\l�����4f���&>r�����q���|�bP*@zR����q�@���`�)�4���5������|����������������8��&���\xi���-�/1���P����W����{�?Y� �����?5������D�a_�C|��-$DAK[o�-���O%%%_��Y�3�������).y
�{pz�H������v�u ��1�y�TVj`��~�Sj54���������H�~�x��f���������B lll�&NN���������&&����
z�x����m/(�b���
,,�4�r� ����|jj�'\f���!_=z�5 v�o�U����g����n���8�wQ?��{C�c $�!�����`��x�=�n�RK,E�h������hU��p�Yx���Hl,y38���tc@:�uF����xU������F��������������kY����l���/��Q�I�t���_XX��y9�Itt[D�wr��Ao�\�0���z�&�V&�)(( �������Vw_c�tX���}m!9Y�����`�����X�����,L��1dd�3��j���;>�������������[���F)hcF���U�^�F(�������|A�K���b�cGAN~���T���k�n��|�t�N�x��s��W������r�5�8g���W���T�������������o�{��������o���������o���/��h��6��D�O���TOh����U����H5q.�C������r}io������
k1�8����x��N�#�^�b^�j�n�=��5C�A]��#V���-����FXX��5��O��``\9"���q�H��O�^��)++KK��<uv����s����qpp ����\���&�����>{����2��T;���PTT�n`200�8I�t?�p��CZQQ��DNN�p1F'/'���@��`��By���P%�}�&h?i����fJ�I0���r����(�Vr������{
����!�!��������sp}���.�1����A�"����O;F�ptt%�%��U����~?��SSS����_��h�
�L�+�������[�~�����Q���������b�?��og���X� b��Gv�7��IG�.!%;Q�I�bN���W��**��������/��&���������p���Y0uQwO��J�����~z��
� ����w6���?�����[�_�W��A���J����T��l�r����#������g���:2L~��T����U�rr*�7���7����{w������7���rt.;��Q=����e\�&��c��?B��n� ��6� �g�8k7�������.����T�b�+:H�+]u�b��)�(�HN:�z:V�����M��f�\;�1dc�z�t��v��9�E��n�?�@�-4���I�{�?�d�]��
�I������p����"���1 �{{�i@8x�?��TUSk+%�q�{��vp(����qS~8$>y�`����5r��7o��������!v�r������K����(h\�E"������s�V�_7��O���5R'B8��/�*�^��H�������D?��H��Uo�����S��`�
��0x&&&�:]��mW�Hh���I�` ��� ���<n���O�P����������1��W9��F*��HD�+C�T�C��~�����m�#Uc���m�yXVS#�����Q���Uv�-IRRr�����|�t��3�oXME%��:823,��r����$L������aKh%�����<00p���������6�<7�je13DM�{~�i-v�Hiz@���x,%E ����gU���~`�kE;��G�h�����\��H�z��� o�L� &���|���0����R!D
�R���G�b����B�����.�-��^�+6��R���������>,x��Y9����l����>RjU��v{����8MS��(��Q���#�vL������� ��"��/!�_G������:��=�J��,�F�!z��I�u-$x�����|�v�bS4A�vr�A>V���N��qv���������f�� -��Y����c�
<��X`���D�:����*�<���N��y���D��6��"�����D������ �AY�5LBB�"��z=���iK�#!%���h�GOE ��j��j�����������h�g�2QU%T��Y^m,��bvfM�N���
J~[��oY4�H?{����t�a�SS�F��|��[5;������'�$h�>�������t�d������������B���K��kn"�%��ju��s=�����]|e/�w�`�ud����#x�S���t;ag1����gk����|+�5WPP�����acTsR��|�����O����33~~~,�r����4. �.I����]��Hz���������cG�O���������������*�����w>n�p_�����C��}���������E����V���� ��vk���t��2��]�����P�z� ����H�/���:;��R��,����KCMM����� 9Z?��1f<64�N���)�u�"�h��n���~���RW��y�6�3�`�^aa�������4�A���\��#V�\�;�@��Z��
I�T�����e�aM�`y�9~w�V���p�49e��B�D �M"��CFO������G�M�j�@������UuiYb"�6,L#�:�W�������0�v��x����O�����d[e�@�y��Z�K���\q�t5gN-PTv+���{�����5K��y,�Jg�=!#!qn8�� �j�����V�,[Q������uIs]�GO|�������"�E��&ja{[0X�l�
8�vil����G��oP��&}���J��N�_>PUT�����dMr�]*�Y� B� A������?�I|�F>�[j����`\��>T3���w�v�(�;\����*��`ZYeK}Y���.��U�����.��z���1�i����F��������U.��+?(��q�6�NG;42���u$�r�df U��Z��;wd����,��#=���^��.=�V�����.�W�d3���\PZ�q�s�����T�C���F����wq������Q�G����A�B�����R���LL�,��H��^����i|���+�P/��h���
4[{��������������B1��~��eK�]�(���Qb��V!v�E\���J����<<������@��
��Z��s�����cNNN����]����d��bE��GGF��0��'�����\*~I/��{SSTtt]?��(]9�P;d�^���{Q�mfT2
�������7o���T��V���0 ���������=��1k�����H�������*[���z�T9���� �d7�l q3���M3�1�2vWvO�����hee�dr~^���}�����v<�q�@ZV�����p������)��mK��p�a�����������c�U�/��$��UU��xmmmw!�UU��Um��l�}M������������
3�������]��R����[V��Xbfj����9/�-�1����a:0(����
7�JM�?�2##C�f_���gX�LJ�WUe��k��j��*K�������D���+*)%�!�f�"�d<�e�n����/��ehc�����]�('�t�}o���G"���O{��3���+�~�0��<v����L����a�#}U���\�|0*��X����s�D��&S�|�;G!�vxxT���.!!��7�c6�
��n�D���4z����������?��� ����������^������u{-{-��W�@n���r�����a&�QPPh����q�� :::@����{o�+���G>����-]��UO�q>���h������Um��)����-{k��������;���X��ugee�/�@��t7����d��b��M�c|�������@`���=&i�z���o7q�������R=<����"��\�?�N��#�|�Bv����:����{����Tz bL�������V���vB�X�wq;b0G�JUi�v��Q8K������� ���J����FCm�?����t��:)���>h�|���a�,]}��}DD���������@�����
2��M��9��/�h�Y-�����Y��*T��t�����Tdd� ���%5###P�H�������w�]t��7"��Y��J���qnyK������$I���b�������'y����a��y�������VVV&'>|8�[�pZ�]��������xy 5�����k[[���k�H02��yZ����O�����lo";,;qi�M�g"D����� �������gp�t�i �XA�����d�q��mLNNNII '��{YQQq����nR;jH>QQqF�APP+\I��n)]d�� P���r��Z�tww�3/%���8��@�[������
�
P]\�hz[f��200T�����o�~�
r�������Ico�����w*���R�"��f_I}�� �d���(��+ �����������w�)ZYYAw�P
m7~�(�ZDi�}b�u��c�( ���z���1
�Q '?� ��01*�����W?�>�.���|1�1.�2���C�l�{K�x�E��N��z�
j>)����1��!X�'��
����b�xe������c��F�h���b�m�BB������B �-�� Y��%��|^ ��CSS��*%���f�����t65}�����g�����{�W!������> ���'I�����t ��>�;_ +��2��"F���O5)�A�@����+��������X�,(( ����&]�9�����7���dM�v'�s�>������� Pp��5��mlL���X)Q�uh(>..��}�U�_?FGF����tR( ��o1����K�H$����)�S��H:�]���g��x��i��q���
���1D�����v.������a�����e��r���������b��<===���2�p����D�*���~}�n~���
�����J�yY99���B�������#�X7frKV����Pf�d�C�G�Q�����&y��������;\��qic&�3�C�[�?��%�i��dL�^Y���KtlF�<�6���c�"@��khhhd$+�]zz���
2r�����v����|��cd&B)�����I����biD=n������5������ �|m��pcN���HJj�7k�l9��`<��b����l��Z�A� Z�q�H��������v[�b`�A6�����:=)�?����?=�j�1,� �����B_��~222*�H�R |R1
�����z���Q�D��p_�.6s]a ���$��y1e�A ���&����fkL�% �Wt�z4�x k8����:`���-� �`&i��1 =.�$�����'g^�t���'G{�.
G[�Nxm`�
V�+HN6���;0����3�
mN���$�s���
��{?������{e�|R��Am'��A� ��6��gO����y%Q<833���wu�j��U�`�������q�e��� b<�P[��],p*3 |:H(
����;Q���<PL���o <I�P'{�.�#���00���7-�@;w$ �[�����@��#��J,����g��.f��D[�����/@���&-��!rss�]V��]�3Z���{�hf�t����JJ�>��?su�iq������?��+��l&
��"� 3�D�um-�����L��a��6�t,,d���(�o�jii�wjmn�����ol���ON��7~�w%?�A��DE�����@N�#L�4����6����@��ea!&"�|�
�{ss��n��b<����Wp����[������\�{��KpqY�3�E)(����w��t��M����{��@&�����<���%��&ff---f&&�n~��A�TS�Mx2C) `)�Li�M6I�(a�W�o�������\��D��F Tf���h|l�����~����C�Srj���?�g���E$ym���Z��e���q���ws��8T�YY������K�i��l
j��#u
��wUa�?�C�����Kz��[Y�-�l�7
`���;)**
�������PJ
�# @k�����\6 No��nTx��p��l������� z���3<<�}w �������\u+[�?�#o��������ha!!cK������=��?���<�23�}Id������� ��C#�d~F��]��x����3\���m��W�����Br2L+/7WANN �-������t5-#c#���~��O�����|�|�����"���$"�@�9�:��u�jM�{A�!E�H��Z�����4���`@
q8Y���2!�A������=l]v2qwOPX8����EWgg�Fo�t�{�����3���aM?�P�������K�~� ��s�� �a����5���.a�����)g���
i��Y����p7�j����[�L��.�M5�E�$@�������mP��v��9�6 �O�74L>,0���x�z�y�7'NNN����[��S����T�k�cc9X�[��{�������������"���pt`��J�H�caYn
&�a�7_=�o��kwe��,W.�|A� �L��(�����,��s�8;{�*�P�}{��;���vum�w���������/{���lt��{�Qa��cO��nnn6�f'
�����x��-�\p���C���u�����l�u�g��d�y(x�C�I��|��k�[���!������c��uv2�ue���2������hq����:P�{*��Ggq��
�cac�.q�����N�y��-~�����o��� �q��������� �����ak��� v����&,LLt�!''�S�u��{����?��t�o�m�4�������p�6���rrr��}���[���:'.�]���~� �7�����\S��[�]v��
>�4��Ac�vkQ��@��5��\��o��99��=F���>{u���C�������@�;���J�7��__^ X�$���� 7T�y�C�9Q�`�Uy=��nmm���^?
���"b&��
8�������U\����44?�`J@�2Hm��;�~�[�G��:'��2�&��))G��������K��egg�^v��)����gm��x�9ncHH��/�<�+u,��2���i3~�����j] ��,���# �666�������&�WQ�ed��y�z�-bD��>1��.���>�������M�����Gs@�V�����n�4x���X��Q�����D0{�Vo������Y��������R��@��g�3��W�^�)���� �9=��������,F3�����w|||1l�������}0jm�rK3P�;"��O��7�.��&'Q��:)ir��3;+����&��
(�9�A�$:��P�@l�F������Sd����+��5l�u��%X+��n��������,��~�wl�
��$��������s�b@��R�������$c�8,�I���50�L��.2�[�;��H��y�g:����������IBxD�
JJaA�/Q������T�8A@H�p�u�f��
���J>GGGr��gFP��DbilD�=!�Z�h��9�a���]��p���E��m�}Z �|�r �������X��9"��+�#`��-�����P}pI�W���U�:+��2 _�;�>n=����-�"Ds&�{G� ��%�@����H�@���N��������E�$x���4��S6�N�NDf@ o��;�3�� ���#�P��N4�5��=�I�%���0z�f�������fb�����t�W�%Z5�l9��RP���gb3�����j��P������th��
�?�e�%�]��6D�l0*��%�.���w"��F�����������N(���u��6$�A��t��d��H�����j������m������^���N�|q�U�l����PT����g6 S��/�
ZFK(()m����(\�/�������I� ���������o`���L��cS����N�0�Ib���z���%i�0^(��_\����������]|\�����
D��
:g��u. 5��*�o /���>g�������UY_?��ql,�L����v*ALh�^ 00p�+���h�.�Y�Fe�)
��j6�pA�r���e�����0��^Lv�.�����Q�BN��X�U�O"�����Am����z���'~������a�B�P������g���`/������,�<��o
}�2-�S���:��/ET�}�f��--�����s������`�+,��������^�~�R[[+�������[�E��k�X8���� ����H�����tI(�4C�� *�#R"
�
*"�� J�t�w�����������~���~r����:�3�c��������i���������}�7q���������N�"N�.�k���@Z�nL��ju43����|�v_�z(�Y|�
��y��y����^���Q����i�3g����vfuI�����OKK�E�[?�&99`��)��=#� 0O}�nv$<(�����a� ������v�W�k�����p�>��Q� �����l���������;9;����� �{�Jd�N�8�L]cQ�������O������I�m)�������M�I!���t���z0��������N�5�����\��r_���������nw����)����#�9�1�z������r��?�NXz��#U��oL��M�����fgg�
���F����Gq�>�����4�����dZ��L�,�����a� ���{d�[��\�n(������]��**����������%g5J��}��R�0V��f��]��Cw U�Y
ei�-B�%�c�����h�gy��^}�4:�W�|��AiVffx�������L:a��'��A��-���2�t����*
���_ �8�������?'tD���a�E����D��Y���V�=��&J�Q33����vL���e���S6�������A�ct���@��r����%�5p���.���]4�����9�g�����{���mm
m"�H�������R� ��H/���m��$������ZON@#o����{��Qgh
���W�������-cG[�Xx�������K���lz������Y�>q*@�����\��>l_J��eWv������F�D���X ���3_G]w��y;'(kV�MKNN �MY�]8v�7�Q�^�4=������\�x&��#emS�e�a�y~�
����}|J�m�������'K��DL����~��
6�~�,|��[�����98��t�[W���y%%G�_���s]�`x�2�E�������)
�,��t������~A'&�!�RKC�&�n��|���7���]D�m�m�?j�'�g�N�X��� u������,������c����t����}*W�<�cQ`J��|b/9���!AA��� m��M$����������X�$L��=0��Y�g]5��:��ZAM��6��{Z!h=y�����G�X ge�P���]��}�E-��{���u���&_::;��i�����UH�D�3,���`������4z��z<�!�rg�
�����=�R�$�`��T`W��G'C�����> _����8����'��,�\,e|���Q�/�;����*�����@���<~�c`O��{L�G+�/\� &��Fw���@��[* @9��^�������l�u�P���`�RYYE�x#B���8B|�%$��7�U�IS9��b�ghBz1@W���E?���:.n�Pd����H��������e|~:�4�}-`d�������~iL
{}`l��6����8�W�PCS�|g{;:&���.������!S��>����V����|���{c��_�����v���7��Sc|v�%����(hy�|���PC����+�p�z��Nt'ljjJ�j8�}���^\�^�
��+��wss���P�$�%�����^�y@�������{��������{���$�Ta���������������/���E���d!����. N:���+ LAZ
?@������_-Jb���3�i��hh��}�v7r��� �{���=M��n���� �EY�Mkhh@=- Ae�D9j&r|5RYZ���q��+� N�����/�41�� 1;
C�U:/�^�v
"���'�A��|������,��gH�]nPB����E�������}���O��W�����,rY��e���W������������vSSs;��2�P�3`��X�I��>X:d��?�;�-����kt����+7�=�fYE�����R@������PcM
g��_L��x;Daf��U���:��v@QX���wD�cm������2(� a.��-���[�IKK\())Q<�������>�[�0�| ��$��V��s�������'���"���W���V��u��H���j�`T� s �&6��`l��S(�Q;��������������]��-�p��*^����;�s�J �"EHx
(�S�k�7��%g3�OO���([f3�:p����7���L���iq��������LLM�-��D������)��5��?������:�,? ��>�Ob�;soj��lleQ\jQ "���iZ�]Xp:X����&��X'~��q��i�|}}�������0=M�����`��PEo;@� M�b�S��j�]���j]u@�*�����=?2Z�o����cI����Ml�@�$=��
���{:4�����U=����"�2���m�B�B�7=:$�_S��N�&YM�7�O�������V��`g��T��*�M��:���=fF�ZO���u1\s<7?�-x�����Z��^�H�N�Y���d��EDE�kD,%�z����5},8�05����f�Gi�����w����mmm���o��U��x��a"����n�H�-������e]�[�m�n|"1����S1�����*��������Q���H�p�D������������#��{��h�I�����>�W?�����E<}^I.�|5�-Q��N�YTT�K\\�}��qr�c��a�>e4��b���H �s+�%��i��Y��@P������t!�>=*�����H��~NN��@��SF#�s90+�����K��U���� �n��M��n99�TU��y������-�y��j��=��`Z��1�'XL���e��[����� �_���w���W?�:X�B�s:0=z�����FTF��c�MY+ ��7o+���v<��{�|MoW���H[��?���\���z��������3v���KIw��[���"��D�o�*>>u���v�)��f�B��>#�K�/~�����������@���������2�EGG�����a�t�~�N�;gv�J���
$��]�]���U}uy���T��D�^��4����POl��N���Z����<��_7J1��r�p��VE�%n���Q�;�B���8W��6�wo��(d� �(��^�z��,=�l�~���l���Q��g�$� �����A�^?�� @w;}�$�f/�o�����e���J���E�:�?�-/�*V�p�Q�cC��#?~��&��3���l������Z�����p��3��#�&�����H����l�V��M���'8�TYP\|�0��s����)�I�� �%�)���q������u�v�TV��/�3����N��~O�|l��0���ez�D�I�b<�2��x��=��-=����e����fM��F�jo���
U9�+b�{�P��<�:~��n1�����:1 ����#�_B1�g�@ ���O/��sr*�i��a;va��<l�� =�c��?�HE�]����t�q�e����6�����VZ�7F��=j�����Isr[����S�^����T�g&_Y���0��W��(�����"����_/������������C_O�<{rH��]}_���L�g7l�r���1�$@Us,�����M�d����Wnk��)���ulWA���5�}y��"-;$u�E>�,�)�HK��ejjj@\p�b��������"�����o��a�xS|��> ��_W�?�L�����E��������"�D b~i������v��x����(Y����K�3DD���������n���������?-��Cs��jj�����6A������ �@}}}����j��{��k��r���X����?.�\)���~`}z������`�j������B�d��Q�:-.��*��������b0�7o.d��KJ�������H�,�V;88���u�&�P��T���)����������/6P{7SSS>Q�v6t+L�����P�xIY���������O�n����h0���h����D9���YY����v��Y���/����$��T���LQ�g�����y0 �z����>�(�����{�^��I�����[���Bc���RX����U3�R��h����"�kk�z�Z�n�m����i^0�n��"��C�����g��dR����O�_�����3��r�a�Dr!��E���%\�B��0S;ggM�XZ*�����uK1������$�#�D1��+s)��:��ct��=�x��MF�#G�+/���� 6�AZZ�_ ��/%9��pE���_����||�w�QP�v;KG�����OKO�M����yBw� �2�<���MMo�����*�72m���u�����c�=r�Q���A�
��or
���p���������>ll8�a��6��>�����hh8������>{n7o�1��3R�2hQ �h��O��3'�&'''(0Pm�c�Bqrzb�#A�#��������2\��
�,�ak����{u������{�L�1�_�]���tR�g����:�}B��se���D@��1��k��L]�J/���Gd�o�tq�Zn��p��q�+�nht���Z/�Y��Y�Urd3������rs�mp��0����_w���*r|����� 5�e����������c:�����z������o��4 ��
�BBC)��k��+I�o������ ];��`�_�x�6��W����{���^������5��{_Y�;�&7;7�w��1��@NB�k~����T� y�NFF�|(s���h��_EI��@ZJ����������---?>�%��Qr����eQQ-��������SKc(�!���F�7<,z�0h�%-�s?;;;9yp9��^0�����=�Id$��-5�RR���������������.%��/��h4��>�
��������Dy99��9�ttt�Q�'���~�e%��E�PPP���'%&��A� .��������� ��������� ��v����C7{I�&F���:�Uc�� ,cooo���Bs����@�d������;]n���B���y���i�D��:�+n��s��1��-s1��g���O�v��g��
���D�6t��oF�{?�D���y��T ��M����J�ze�
�����-��:��$�&��_4m���_�]py�������"�/�Q13�7l%���~�����s�����kY2mq�k�q�zt��.�099 ���aX4������������`"�Y�R7F��?z�)�\M���{�BBlS�yA
���~��}&h�?�9yyJ�o�cy�����y����y���n��R��^�cR��W�K���0�nB����?��Dq��#;�d<...n��&?FQI���I��}�`��Ah���|�B�9���0]L�l�$ �ygbt�9��n������^sg�����-hUUU@V=<$����a����1/ZA�lmUS��8�35���R7��"V���f��C����FFo@"�N�������l3+�/�h��\�Qy'��>����2�^���a�H#-�>?���/rb}�S��� �l�1X�a���_�}�I�r�Q�]v+@6^5�����K�<������M,H�j�Ds
/{��a ��W�g�w�����O�T8����<�Oo�:>�� ���������(+�T�f���$\�r���:X�����-v��k
#n���GH��{r��Y�vs$'�����l�����?���/(Y�n�<ELlfb��������&�f����Cr�� ���s�������5�%.^�j��E��6�9�a�#)�%��8��6�m)Y��_�j�;�D��� �Z�
����n#���f'X:���^����$'%���(C�X
[Vi
=?���r�b�y'��:����q����-3��{�^� �e�$J�D�G��������#F�%�����s��8���xG�T\bbt��p������e1A��33�G�q�o��x��j�!�S0�����|��^��H��aZ���&��� )��o���zL���9���Z�]OOO����Ll�v3 ���j"�td������Y��7����"m�R��� L��1q����T�G��mm���/�V)'I'$&����\��v�vow ������@��#������t/���^��)��I������m�$ ��|L]��7^e�7#{8HcK�90��%%���������H�����/A�IWN:����N��5>��>���p G�@vS������������YXp���|tu�������|z��o �Uzf���B����?�������k��x�i6�ut������N�l�=�P���/T@�=fZZs�X\kww����������?��������4c+**@��c�������666�R�?+�_c���s����'N����������||���� Z�����X�
�E
VN�����)�3��� ��=wN����m��3T9/T�+��j������0T���^�!,�K=�
����_���(@��j<�l����8�f���
X���y�O�Z�����Kt%��[���y�|bKH�~��}a!��\w����]����f��w��%���e\4�(�%."�������z�>4NR �<8~��;�KK�[�`�����
��`�Oe�};KO?48(<#...6��/ 2TTT����URH$����Orp�7E>�-/�H�����Zi����hN�i�z '2��.t' ���Y������=�G��
��^����QZZ:.�3�liq1$(�����������A�l�{��N��'���=����� ���\�7643�$ �ddd�����=��L����mM;��*���s<fw��g��(�\�����o�B��Cb�����Zr��6n��������`����
�����>��=l�����@ukn���w{�������6^�����z�[���|� �f?��^/��2����>����
���2p���y���qu�t���]Yhq 0@E��" ������w�6c�����(��.Q&�ZH9�T���u/xdccs��\����� ������Z��w��������8��K�N�c?~���(��
V� �ac��n�*���?���� IX�=��]����0�����|C�?���s���]<���?�>�666n{@ -�|}a� �3��b �)�������I��zy��"&7� ����R��O9��������om�5�p��d�r�M�T`��P�gh+s��)�Xr���V#��Y1mlPKrh���$7�<`�>��xj7���l,��DWOO/V�S��Yu��i�}�Q'��K���I�==����R��������Y~��Bi+���K�(.F
yYYYw����������y���/M��TW����*%���2���L�y���Aj�q_�.:uW]�4VW�)��][^�nQh������6����!�2�` ^�c��� Qhd�r���;d���~����d�Q��G0�;�F*0P'Z��#e�I����x�f'���6���������b?��������8*��j ��wg
�'����d�p�(����l`5X8��,���OM��d�!
k{��������={�ZK�d��0�r�%�u��.�x�n�)"-}p��%H
&66��r�+?� "