From 9d7e504b50bdb8f709d51227aa9904fbcae7a61e Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyakova@postgrespro.ru>
Date: Thu, 1 Feb 2018 07:32:39 +0300
Subject: [PATCH v8] 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.

Note: caching of domain constraints is not currently supported, as we cannot
assume that the planner has access to the same domain constraints that will
apply at runtime.
---
 contrib/postgres_fdw/deparse.c                     |   11 +
 doc/src/sgml/ref/create_function.sgml              |   14 +
 doc/src/sgml/xfunc.sgml                            |   13 +-
 src/backend/commands/explain.c                     |    5 +-
 src/backend/commands/prepare.c                     |    4 +-
 src/backend/executor/execExpr.c                    |   87 +
 src/backend/executor/execExprInterp.c              |   70 +
 src/backend/executor/execSRF.c                     |   10 +
 src/backend/executor/spi.c                         |    8 +-
 src/backend/nodes/copyfuncs.c                      |   16 +
 src/backend/nodes/equalfuncs.c                     |   11 +
 src/backend/nodes/nodeFuncs.c                      |   96 +
 src/backend/nodes/outfuncs.c                       |   11 +
 src/backend/nodes/readfuncs.c                      |   15 +
 src/backend/optimizer/path/clausesel.c             |   12 +
 src/backend/optimizer/path/costsize.c              |   21 +
 src/backend/optimizer/prep/prepqual.c              |   28 +
 src/backend/optimizer/util/clauses.c               | 1123 +++-
 src/backend/optimizer/util/var.c                   |   40 +
 src/backend/tcop/postgres.c                        |    2 +-
 src/backend/utils/adt/arrayfuncs.c                 |   13 +
 src/backend/utils/adt/ruleutils.c                  |   25 +-
 src/backend/utils/cache/plancache.c                |  295 +-
 src/backend/utils/fmgr/funcapi.c                   |   10 +-
 src/include/executor/execExpr.h                    |   31 +
 src/include/executor/executor.h                    |   14 +-
 src/include/nodes/execnodes.h                      |   19 +
 src/include/nodes/nodeFuncs.h                      |    8 +
 src/include/nodes/nodes.h                          |    2 +
 src/include/nodes/params.h                         |   15 +-
 src/include/nodes/primnodes.h                      |   76 +-
 src/include/utils/plancache.h                      |   13 +-
 src/pl/plpgsql/src/pl_exec.c                       |    7 +
 .../expected/precalculate_stable_functions.out     | 6194 ++++++++++++++++++++
 .../expected/precalculate_stable_functions_1.out   | 5840 ++++++++++++++++++
 src/test/regress/parallel_schedule                 |    2 +-
 src/test/regress/serial_schedule                   |    1 +
 .../regress/sql/precalculate_stable_functions.sql  | 1946 ++++++
 38 files changed, 15907 insertions(+), 201 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/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index e111b09..63088c4 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -771,6 +771,14 @@ foreign_expr_walker(Node *node,
 					state = FDW_COLLATE_UNSAFE;
 			}
 			break;
+		case T_CachedExpr:
+			{
+				CachedExpr *cachedexpr = (CachedExpr *) node;
+
+				return foreign_expr_walker((Node *) cachedexpr->subexpr,
+										   glob_cxt, outer_cxt);
+			}
+			break;
 		default:
 
 			/*
@@ -2162,6 +2170,9 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
 		case T_Aggref:
 			deparseAggref((Aggref *) node, context);
 			break;
+		case T_CachedExpr:
+			deparseExpr((Expr *) ((CachedExpr *) node)->subexpr, context);
+			break;
 		default:
 			elog(ERROR, "unsupported expression type for deparse: %d",
 				 (int) nodeTag(node));
diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml
index c0adb8c..21e2f5f 100644
--- a/doc/src/sgml/ref/create_function.sgml
+++ b/doc/src/sgml/ref/create_function.sgml
@@ -336,6 +336,20 @@ CREATE [ OR REPLACE ] FUNCTION
        <literal>setval()</literal>.
       </para>
 
+      <note>
+       <para>
+        Stable, immutable functions and other nonvolatile 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 domain constraints (see <xref
+        linkend="sql-createdomain"/>).
+       </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..169513f 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1533,9 +1533,20 @@ 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 nonvolatile 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 this feature is not supported for domain constraints (see <xref
+     linkend="sql-createdomain"/>).
+    </para>
+   </note>
+
    <para>
     Any function with side-effects <emphasis>must</emphasis> be labeled
     <literal>VOLATILE</literal>, so that calls to it cannot be optimized away.
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 41cd47e..f7d56f6 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2822,10 +2822,11 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
 				if (list_length(fscan->functions) == 1)
 				{
 					RangeTblFunction *rtfunc = (RangeTblFunction *) linitial(fscan->functions);
+					FuncExpr   *funcexpr = cast_node_if_cached(rtfunc->funcexpr,
+															   FuncExpr);
 
-					if (IsA(rtfunc->funcexpr, FuncExpr))
+					if (funcexpr)
 					{
-						FuncExpr   *funcexpr = (FuncExpr *) rtfunc->funcexpr;
 						Oid			funcid = funcexpr->funcid;
 
 						objectname = get_func_name(funcid);
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b945b15..ef9f134 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -243,7 +243,7 @@ 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, paramLI, false, NULL, true);
 	plan_list = cplan->stmt_list;
 
 	/*
@@ -670,7 +670,7 @@ 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, paramLI, true, queryEnv, true);
 
 	INSTR_TIME_SET_CURRENT(planduration);
 	INSTR_TIME_SUBTRACT(planduration, planstart);
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index c6eb3eb..39110b9 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -130,6 +130,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
 	state->parent = parent;
 	state->ext_params = NULL;
 
+	/* after the first run, set it to false */
+	state->own_execute_cached_expressions = palloc(sizeof(bool));
+	*(state->own_execute_cached_expressions) = true;
+	/* there's no no upper state */
+	state->top_execute_cached_expressions = NULL;
+
 	/* Insert EEOP_*_FETCHSOME steps as needed */
 	ExecInitExprSlots(state, (Node *) node);
 
@@ -167,6 +173,12 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
 	state->parent = NULL;
 	state->ext_params = ext_params;
 
+	/* after the first run, set it to false */
+	state->own_execute_cached_expressions = palloc(sizeof(bool));
+	*(state->own_execute_cached_expressions) = true;
+	/* there's no no upper state */
+	state->top_execute_cached_expressions = NULL;
+
 	/* Insert EEOP_*_FETCHSOME steps as needed */
 	ExecInitExprSlots(state, (Node *) node);
 
@@ -222,6 +234,12 @@ ExecInitQual(List *qual, PlanState *parent)
 	/* mark expression as to be used with ExecQual() */
 	state->flags = EEO_FLAG_IS_QUAL;
 
+	/* after the first run, set it to false */
+	state->own_execute_cached_expressions = palloc(sizeof(bool));
+	*(state->own_execute_cached_expressions) = true;
+	/* there's no no upper state */
+	state->top_execute_cached_expressions = NULL;
+
 	/* Insert EEOP_*_FETCHSOME steps as needed */
 	ExecInitExprSlots(state, (Node *) qual);
 
@@ -366,6 +384,12 @@ ExecBuildProjectionInfo(List *targetList,
 
 	state->resultslot = slot;
 
+	/* after the first run, set it to false */
+	state->own_execute_cached_expressions = palloc(sizeof(bool));
+	*(state->own_execute_cached_expressions) = true;
+	/* there's no no upper state */
+	state->top_execute_cached_expressions = NULL;
+
 	/* Insert EEOP_*_FETCHSOME steps as needed */
 	ExecInitExprSlots(state, (Node *) targetList);
 
@@ -863,6 +887,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->resnull = false;
+				scratch.d.cachedexpr.state->resvalue = (Datum) 0;
+				scratch.d.cachedexpr.state->isExecuted = false;
+				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;
@@ -1320,6 +1376,31 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				elemstate->parent = state->parent;
 				elemstate->ext_params = state->ext_params;
 
+				/*
+				 * Use the information from the upper state to get the first
+				 * element, but for other elements always set it to false. This
+				 * means that we will use our own information about it during
+				 * execution.
+				 */
+
+				elemstate->own_execute_cached_expressions =
+					palloc(sizeof(bool));
+
+				/* does not matter, set it at runtime */
+				*(elemstate->own_execute_cached_expressions) = true;
+
+				if (state->top_execute_cached_expressions)
+				{
+					elemstate->top_execute_cached_expressions =
+						state->top_execute_cached_expressions;
+				}
+				else
+				{
+					Assert(state->own_execute_cached_expressions);
+					elemstate->top_execute_cached_expressions =
+						state->own_execute_cached_expressions;
+				}
+
 				elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
 				elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
 
@@ -2823,6 +2904,12 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 	state->expr = (Expr *) aggstate;
 	state->parent = parent;
 
+	/* after the first run, set it to false */
+	state->own_execute_cached_expressions = palloc(sizeof(bool));
+	*(state->own_execute_cached_expressions) = true;
+	/* there's no no upper state */
+	state->top_execute_cached_expressions = NULL;
+
 	scratch.resvalue = &state->resvalue;
 	scratch.resnull = &state->resnull;
 
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index f646fd9..c13df03 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -391,6 +391,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_AGG_PLAIN_TRANS,
 		&&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
 		&&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
+		&&CASE_EEOP_CACHEDEXPR_IF_CACHED,
+		&&CASE_EEOP_CACHEDEXPR_SUBEXPR_END,
 		&&CASE_EEOP_LAST
 	};
 
@@ -1755,6 +1757,74 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		EEO_CASE(EEOP_CACHEDEXPR_IF_CACHED)
+		{
+			/*
+			 * Always execute if we do not have a cached result...
+			 */
+			bool		execute = !op->d.cachedexpr.state->isExecuted;
+
+			/*
+			 * ...but sometimes we must execute even if we have the cached
+			 * result.
+			 */
+			if (state->own_execute_cached_expressions)
+			{
+				execute |= *(state->own_execute_cached_expressions);
+			}
+			else
+			{
+				Assert(state->top_execute_cached_expressions);
+				execute |= *(state->top_execute_cached_expressions);
+			}
+
+			if (!execute)
+			{
+				/* 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/execSRF.c b/src/backend/executor/execSRF.c
index b97b8d7..bd081e3 100644
--- a/src/backend/executor/execSRF.c
+++ b/src/backend/executor/execSRF.c
@@ -58,6 +58,16 @@ ExecInitTableFunctionResult(Expr *expr,
 {
 	SetExprState *state = makeNode(SetExprState);
 
+	/*
+	 * Although SRF expressions are not cached, expressions that return RECORD
+	 * can be cached. If such cached expression is executed in FROM (ROWS FROM),
+	 * this means that it is executed only once so we can treat it as a
+	 * non-cached expression (note that cached expressions always are calculated
+	 * as many times as they are mentioned in the query).
+	 */
+	if (IsA(expr, CachedExpr))
+		expr = (Expr *) ((CachedExpr *) expr)->subexpr;
+
 	state->funcReturnsSet = false;
 	state->expr = expr;
 	state->func.fn_oid = InvalidOid;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 9fc4431..e67f365 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1290,7 +1290,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, paramLI, false, _SPI_current->queryEnv,
+						  false);
 	stmt_list = cplan->stmt_list;
 
 	if (!plan->saved)
@@ -1724,7 +1725,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 */
@@ -2114,7 +2115,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, paramLI, plan->saved,
+							  _SPI_current->queryEnv, false);
 		stmt_list = cplan->stmt_list;
 
 		/*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index fd3001c..5783f09 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1332,6 +1332,19 @@ _copyConst(const Const *from)
 }
 
 /*
+ * _copyCachedExpr
+ */
+static CachedExpr *
+_copyCachedExpr(const CachedExpr *from)
+{
+	CachedExpr *newnode = makeNode(CachedExpr);
+
+	COPY_NODE_FIELD(subexpr);
+
+	return newnode;
+}
+
+/*
  * _copyParam
  */
 static Param *
@@ -4874,6 +4887,9 @@ copyObjectImpl(const void *from)
 		case T_Const:
 			retval = _copyConst(from);
 			break;
+		case T_CachedExpr:
+			retval = _copyCachedExpr(from);
+			break;
 		case T_Param:
 			retval = _copyParam(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 7d2aa1a..b8d5b31 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -195,6 +195,14 @@ _equalConst(const Const *a, const Const *b)
 }
 
 static bool
+_equalCachedExpr(const CachedExpr *a, const CachedExpr *b)
+{
+	COMPARE_NODE_FIELD(subexpr);
+
+	return true;
+}
+
+static bool
 _equalParam(const Param *a, const Param *b)
 {
 	COMPARE_SCALAR_FIELD(paramkind);
@@ -3024,6 +3032,9 @@ equal(const void *a, const void *b)
 		case T_Const:
 			retval = _equalConst(a, b);
 			break;
+		case T_CachedExpr:
+			retval = _equalCachedExpr(a, b);
+			break;
 		case T_Param:
 			retval = _equalParam(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 6c76c41..0b0137c 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -54,6 +54,10 @@ exprType(const Node *expr)
 		case T_Const:
 			type = ((const Const *) expr)->consttype;
 			break;
+		case T_CachedExpr:
+			type =
+				exprType((const Node *) ((const CachedExpr *) expr)->subexpr);
+			break;
 		case T_Param:
 			type = ((const Param *) expr)->paramtype;
 			break;
@@ -284,6 +288,9 @@ exprTypmod(const Node *expr)
 			return ((const Var *) expr)->vartypmod;
 		case T_Const:
 			return ((const Const *) expr)->consttypmod;
+		case T_CachedExpr:
+			return
+				exprTypmod((const Node *) ((const CachedExpr *) expr)->subexpr);
 		case T_Param:
 			return ((const Param *) expr)->paramtypmod;
 		case T_ArrayRef:
@@ -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);
@@ -732,6 +751,10 @@ exprCollation(const Node *expr)
 		case T_Const:
 			coll = ((const Const *) expr)->constcollid;
 			break;
+		case T_CachedExpr:
+			coll = exprCollation(
+				(const Node *) ((const CachedExpr *) expr)->subexpr);
+			break;
 		case T_Param:
 			coll = ((const Param *) expr)->paramcollid;
 			break;
@@ -927,6 +950,10 @@ exprInputCollation(const Node *expr)
 
 	switch (nodeTag(expr))
 	{
+		case T_CachedExpr:
+			coll = exprInputCollation(
+				(const Node *) ((const CachedExpr *) expr)->subexpr);
+			break;
 		case T_Aggref:
 			coll = ((const Aggref *) expr)->inputcollid;
 			break;
@@ -976,6 +1003,10 @@ exprSetCollation(Node *expr, Oid collation)
 		case T_Const:
 			((Const *) expr)->constcollid = collation;
 			break;
+		case T_CachedExpr:
+			exprSetCollation((Node *) ((CachedExpr *) expr)->subexpr,
+							 collation);
+			break;
 		case T_Param:
 			((Param *) expr)->paramcollid = collation;
 			break;
@@ -1123,6 +1154,10 @@ exprSetInputCollation(Node *expr, Oid inputcollation)
 {
 	switch (nodeTag(expr))
 	{
+		case T_CachedExpr:
+			exprSetInputCollation((Node *) ((CachedExpr *) expr)->subexpr,
+								  inputcollation);
+			break;
 		case T_Aggref:
 			((Aggref *) expr)->inputcollid = inputcollation;
 			break;
@@ -1203,6 +1238,10 @@ exprLocation(const Node *expr)
 		case T_Const:
 			loc = ((const Const *) expr)->location;
 			break;
+		case T_CachedExpr:
+			loc = exprLocation(
+				(const Node *) ((const CachedExpr *) expr)->subexpr);
+			break;
 		case T_Param:
 			loc = ((const Param *) expr)->location;
 			break;
@@ -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))
@@ -1653,6 +1695,9 @@ check_functions_in_node(Node *node, check_function_callback checker,
 {
 	switch (nodeTag(node))
 	{
+		case T_CachedExpr:
+			return check_functions_in_node(
+				(Node *) ((CachedExpr *) node)->subexpr, checker, context);
 		case T_Aggref:
 			{
 				Aggref	   *expr = (Aggref *) node;
@@ -1868,6 +1913,18 @@ expression_tree_walker(Node *node,
 			break;
 		case T_WithCheckOption:
 			return walker(((WithCheckOption *) node)->qual, context);
+		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_Aggref:
 			{
 				Aggref	   *expr = (Aggref *) node;
@@ -2478,6 +2535,25 @@ expression_tree_mutator(Node *node,
 				MUTATE(newnode->qual, wco->qual, Node *);
 				return (Node *) newnode;
 			}
+		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_Aggref:
 			{
 				Aggref	   *aggref = (Aggref *) node;
@@ -3817,3 +3893,23 @@ planstate_walk_members(List *plans, PlanState **planstates,
 
 	return false;
 }
+
+/*
+ * cast_node_if_cached_impl: return a node of this type (perhaps get it from the
+ * source cached expression) or NULL.
+ */
+Node *
+cast_node_if_cached_impl(Node *node, NodeTag tag)
+{
+	if (nodeTag(node) == tag)
+		return node;
+
+	if (nodeTag(node) == T_CachedExpr)
+	{
+		return cast_node_if_cached_impl(
+							(Node *) (castNode(CachedExpr, node))->subexpr,
+							tag);
+	}
+
+	return NULL;
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e0f4bef..b9aa22b 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1115,6 +1115,14 @@ _outConst(StringInfo str, const Const *node)
 }
 
 static void
+_outCachedExpr(StringInfo str, const CachedExpr *node)
+{
+	WRITE_NODE_TYPE("CACHEDEXPR");
+
+	WRITE_NODE_FIELD(subexpr);
+}
+
+static void
 _outParam(StringInfo str, const Param *node)
 {
 	WRITE_NODE_TYPE("PARAM");
@@ -3785,6 +3793,9 @@ outNode(StringInfo str, const void *obj)
 			case T_Const:
 				_outConst(str, obj);
 				break;
+			case T_CachedExpr:
+				_outCachedExpr(str, obj);
+				break;
 			case T_Param:
 				_outParam(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 22d8b9d..362cd9d 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -554,6 +554,19 @@ _readConst(void)
 }
 
 /*
+ * _readCachedExpr
+ */
+static CachedExpr *
+_readCachedExpr(void)
+{
+	READ_LOCALS(CachedExpr);
+
+	READ_NODE_FIELD(subexpr);
+
+	READ_DONE();
+}
+
+/*
  * _readParam
  */
 static Param *
@@ -2475,6 +2488,8 @@ parseNodeString(void)
 		return_value = _readVar();
 	else if (MATCH("CONST", 5))
 		return_value = _readConst();
+	else if (MATCH("CACHEDEXPR", 10))
+		return_value = _readCachedExpr();
 	else if (MATCH("PARAM", 5))
 		return_value = _readParam();
 	else if (MATCH("AGGREF", 6))
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index f471794..3122db1 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 8679b14..d5cf07d 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -3958,6 +3958,27 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 		 */
 		return false;
 	}
+	else if (IsA(node, CachedExpr))
+	{
+		/*
+		 * Calculate subexpression cost as usual and add it to startup cost
+		 * (because subexpression will be executed only once for all tuples).
+		 */
+		cost_qual_eval_context subexpr_context;
+
+		subexpr_context.root = context->root;
+		subexpr_context.total.startup = 0;
+		subexpr_context.total.per_tuple = 0;
+
+		cost_qual_eval_walker((Node *) ((CachedExpr *) node)->subexpr,
+							  &subexpr_context);
+
+		context->total.startup +=
+			(subexpr_context.total.startup + subexpr_context.total.per_tuple);
+
+		/* do NOT recurse into children */
+		return false;
+	}
 
 	/* recurse into children */
 	return expression_tree_walker(node, cost_qual_eval_walker,
diff --git a/src/backend/optimizer/prep/prepqual.c b/src/backend/optimizer/prep/prepqual.c
index cb1f485..d02c655 100644
--- a/src/backend/optimizer/prep/prepqual.c
+++ b/src/backend/optimizer/prep/prepqual.c
@@ -252,6 +252,34 @@ negate_clause(Node *node)
 				return (Node *) newexpr;
 			}
 			break;
+		case T_CachedExpr:
+			{
+				CachedExpr *expr = (CachedExpr *) node;
+				/* Try to simplify its subexpression */
+				Node	   *newsubnode = negate_clause((Node *) expr->subexpr);
+
+				if (IsA(newsubnode, BoolExpr) &&
+					((BoolExpr *) newsubnode)->boolop == NOT_EXPR)
+				{
+					/*
+					 * Simplifying its subexpression did not help so return the
+					 * cached negation of the original node.
+					 */
+					CachedExpr *newexpr = makeNode(CachedExpr);
+					newexpr->subexpr =
+						(CacheableExpr *) make_notclause((Expr *) node);
+					return (Node *) newexpr;
+				}
+				else
+				{
+					/*
+					 * A simplified subexpression may be non-cacheable, so
+					 * return it by itself.
+					 */
+					return newsubnode;
+				}
+			}
+			break;
 		default:
 			/* else fall through */
 			break;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 89f27ce..0febf06 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -96,6 +96,45 @@ typedef struct
 	List	   *safe_param_ids; /* PARAM_EXEC Param IDs to treat as safe */
 } max_parallel_hazard_context;
 
+/*
+ * A detailed result of checking that the subnodes/functions of the node are
+ * safe for evaluation or caching. This is used in the functions
+ * ece_all_arguments_const (which checks the subnodes) and
+ * ece_functions_are_safe (which checks the node functions).
+ *
+ * When investigating subnodes:
+ * - SAFE_FOR_EVALUATION indicates that all the children of this node are Consts.
+ * - SAFE_FOR_CACHING_ONLY indicates that all the children of this node are
+ * Consts or CachedExprs.
+ * - SAFE_FOR_NOTHING indicates that this node has a non-Const and
+ * non-CachedExpr child node.
+ *
+ * When investigating node functions:
+ * - SAFE_FOR_EVALUATION that the node contains only immutable functions (or
+ * also stable functions in the case of estimation).
+ * - SAFE_FOR_CACHING_ONLY indicates the node contains only immutable or stable
+ * functions (so in the case of estimation there's no difference between
+ * SAFE_FOR_EVALUATION and SAFE_FOR_CACHING_ONLY, and the returned value is
+ * always SAFE_FOR_EVALUATION because it is more strict in general).
+ * - SAFE_FOR_NOTHING indicates that the node contains a volatile function.
+ */
+typedef enum
+{
+	SAFE_FOR_EVALUATION,
+	SAFE_FOR_CACHING_ONLY,
+	SAFE_FOR_NOTHING,
+} ece_check_node_safety_detailed;
+
+#define SAFE_FOR_CACHING(detailed) \
+	((detailed) == SAFE_FOR_EVALUATION || (detailed) == SAFE_FOR_CACHING_ONLY)
+
+typedef struct
+{
+	ece_check_node_safety_detailed detailed;	/* detailed result */
+	bool		recurse;		/* we should also check the subnodes? */
+	bool		estimate;		/* are stable functions safe for evaluation? */
+} ece_functions_are_safe_context;
+
 static bool contain_agg_clause_walker(Node *node, void *context);
 static bool get_agg_clause_costs_walker(Node *node,
 							get_agg_clause_costs_context *context);
@@ -113,23 +152,33 @@ static bool contain_leaked_vars_walker(Node *node, void *context);
 static Relids find_nonnullable_rels_walker(Node *node, bool top_level);
 static List *find_nonnullable_vars_walker(Node *node, bool top_level);
 static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
+static ece_check_node_safety_detailed ece_all_arguments_const(Node *node);
 static Node *eval_const_expressions_mutator(Node *node,
 							   eval_const_expressions_context *context);
-static bool contain_non_const_walker(Node *node, void *context);
-static bool ece_function_is_safe(Oid funcid,
-					 eval_const_expressions_context *context);
+static bool contain_non_const_walker(Node *node,
+									 ece_check_node_safety_detailed *detailed);
+static ece_check_node_safety_detailed ece_functions_are_safe(Node *node,
+															 bool recurse,
+															 bool estimate);
+static bool ece_functions_are_safe_walker(
+									Node *node,
+									ece_functions_are_safe_context *context);
 static List *simplify_or_arguments(List *args,
 					  eval_const_expressions_context *context,
-					  bool *haveNull, bool *forceTrue);
+					  bool *haveNull, bool *forceTrue,
+					  bool *all_consts_or_cached);
 static List *simplify_and_arguments(List *args,
 					   eval_const_expressions_context *context,
-					   bool *haveNull, bool *forceFalse);
+					   bool *haveNull, bool *forceFalse,
+					   bool *all_consts_or_cached);
 static Node *simplify_boolean_equality(Oid opno, List *args);
 static Expr *simplify_function(Oid funcid,
 				  Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List **args_p,
-				  bool funcvariadic, bool process_args, bool allow_non_const,
-				  eval_const_expressions_context *context);
+				  bool funcvariadic, bool process_args,
+				  bool allow_only_consts_and_simple_caching,
+				  eval_const_expressions_context *context,
+				  CoercionForm funcformat, Oid opno, int location);
 static List *expand_function_arguments(List *args, Oid result_type,
 						  HeapTuple func_tuple);
 static List *reorder_function_arguments(List *args, HeapTuple func_tuple);
@@ -159,6 +208,13 @@ static Query *substitute_actual_srf_parameters(Query *expr,
 static Node *substitute_actual_srf_parameters_mutator(Node *node,
 										 substitute_actual_srf_parameters_context *context);
 static bool tlist_matches_coltypelist(List *tlist, List *coltypelist);
+static Expr *get_cached_expr_node(CacheableExpr *subexpr);
+static bool is_const_or_cached(const Node *node);
+static Expr *cache_function(Oid funcid, Oid result_type, Oid result_collid,
+							Oid input_collid, List *args, bool funcvariadic,
+							HeapTuple func_tuple,
+							eval_const_expressions_context *context,
+							CoercionForm funcformat, Oid opno, int location);
 
 
 /*****************************************************************************
@@ -394,6 +450,8 @@ make_ands_implicit(Expr *clause)
 			 !((Const *) clause)->constisnull &&
 			 DatumGetBool(((Const *) clause)->constvalue))
 		return NIL;				/* constant TRUE input -> NIL list */
+	else if (IsA(clause, CachedExpr))
+		return make_ands_implicit((Expr *) ((CachedExpr *) clause)->subexpr);
 	else
 		return list_make1(clause);
 }
@@ -857,6 +915,8 @@ contain_subplans_walker(Node *node, void *context)
 		IsA(node, AlternativeSubPlan) ||
 		IsA(node, SubLink))
 		return true;			/* abort the tree traversal and return true */
+	if (IsA(node, CachedExpr))
+		return false;			/* subplans are not cacheable */
 	return expression_tree_walker(node, contain_subplans_walker, context);
 }
 
@@ -983,6 +1043,11 @@ contain_volatile_functions_walker(Node *node, void *context)
 		/* NextValueExpr is volatile */
 		return true;
 	}
+	if (IsA(node, CachedExpr))
+	{
+		/* CachedExpr is not volatile */
+		return false;
+	}
 
 	/*
 	 * See notes in contain_mutable_functions_walker about why we treat
@@ -1030,6 +1095,12 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context)
 								context))
 		return true;
 
+	if (IsA(node, CachedExpr))
+	{
+		/* CachedExpr is not volatile */
+		return false;
+	}
+
 	/*
 	 * See notes in contain_mutable_functions_walker about why we treat
 	 * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable, while
@@ -1290,6 +1361,14 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
 								 context, 0);
 	}
 
+	else if (IsA(node, CachedExpr))
+	{
+		CachedExpr *cachedexpr = (CachedExpr *) node;
+
+		return max_parallel_hazard_walker((Node *) cachedexpr->subexpr,
+										  context);
+	}
+
 	/* Recurse to check arguments */
 	return expression_tree_walker(node,
 								  max_parallel_hazard_walker,
@@ -1495,6 +1574,14 @@ contain_context_dependent_node_walker(Node *node, int *flags)
 			return res;
 		}
 	}
+	if (IsA(node, CachedExpr))
+	{
+		CachedExpr *cachedexpr = (CachedExpr *) node;
+
+		return contain_context_dependent_node_walker(
+												(Node *) cachedexpr->subexpr,
+												flags);
+	}
 	return expression_tree_walker(node, contain_context_dependent_node_walker,
 								  (void *) flags);
 }
@@ -1615,6 +1702,12 @@ contain_leaked_vars_walker(Node *node, void *context)
 			 */
 			return false;
 
+		case T_CachedExpr:
+
+			return contain_leaked_vars_walker(
+										(Node *) ((CachedExpr *) node)->subexpr,
+										context);
+
 		default:
 
 			/*
@@ -2489,6 +2582,9 @@ eval_const_expressions(PlannerInfo *root, Node *node)
  *	  value of the Param.
  * 2. Fold stable, as well as immutable, functions to constants.
  * 3. Reduce PlaceHolderVar nodes to their contained expressions.
+ *
+ * Unlike eval_const_expressions, the cached expressions are never used for
+ * estimation.
  *--------------------
  */
 Node *
@@ -2508,11 +2604,13 @@ estimate_expression_value(PlannerInfo *root, Node *node)
 /*
  * The generic case in eval_const_expressions_mutator is to recurse using
  * expression_tree_mutator, which will copy the given node unchanged but
- * const-simplify its arguments (if any) as far as possible.  If the node
+ * simplify its arguments (if any) as far as possible.  If the node
  * itself does immutable processing, and each of its arguments were reduced
  * to a Const, we can then reduce it to a Const using evaluate_expr.  (Some
  * node types need more complicated logic; for example, a CASE expression
- * might be reducible to a constant even if not all its subtrees are.)
+ * might be reducible to a constant even if not all its subtrees are.) Also if
+ * the note itself does not volatile processing, and each of its arguments were
+ * reduced to Const or CachedExpr nodes, we can then reduce it to a CachedExpr.
  */
 #define ece_generic_processing(node) \
 	expression_tree_mutator((Node *) (node), eval_const_expressions_mutator, \
@@ -2523,8 +2621,13 @@ estimate_expression_value(PlannerInfo *root, Node *node)
  * By going directly to expression_tree_walker, contain_non_const_walker
  * is not applied to the node itself, only to its children.
  */
-#define ece_all_arguments_const(node) \
-	(!expression_tree_walker((Node *) (node), contain_non_const_walker, NULL))
+static ece_check_node_safety_detailed
+ece_all_arguments_const(Node *node)
+{
+	ece_check_node_safety_detailed detailed = SAFE_FOR_EVALUATION;
+	expression_tree_walker(node, contain_non_const_walker, (void *) &detailed);
+	return detailed;
+}
 
 /* Generic macro for applying evaluate_expr */
 #define ece_evaluate_expr(node) \
@@ -2573,7 +2676,8 @@ eval_const_expressions_mutator(Node *node,
 					{
 						/* OK to substitute parameter value? */
 						if (context->estimate ||
-							(prm->pflags & PARAM_FLAG_CONST))
+							((prm->pflags & PARAM_FLAG_CONST) &&
+							 !(prm->pflags & PARAM_FLAG_PRECALCULATED)))
 						{
 							/*
 							 * Return a Const representing the param value.
@@ -2600,6 +2704,13 @@ eval_const_expressions_mutator(Node *node,
 													  prm->isnull,
 													  typByVal);
 						}
+						/* Otherwise OK to cache parameter value? */
+						else if (!context->estimate &&
+								 prm->pflags & PARAM_FLAG_PRECALCULATED)
+						{
+							return (Node *) get_cached_expr_node(
+										(CacheableExpr *) copyObject(param));
+						}
 					}
 				}
 
@@ -2681,8 +2792,11 @@ eval_const_expressions_mutator(Node *node,
 										   &args,
 										   expr->funcvariadic,
 										   true,
-										   true,
-										   context);
+										   false,
+										   context,
+										   expr->funcformat,
+										   InvalidOid,
+										   expr->location);
 				if (simple)		/* successfully simplified it */
 					return (Node *) simple;
 
@@ -2728,26 +2842,15 @@ eval_const_expressions_mutator(Node *node,
 										   &args,
 										   false,
 										   true,
-										   true,
-										   context);
+										   false,
+										   context,
+										   COERCE_EXPLICIT_CALL,
+										   expr->opno,
+										   expr->location);
 				if (simple)		/* successfully simplified it */
 					return (Node *) simple;
 
 				/*
-				 * If the operator is boolean equality or inequality, we know
-				 * how to simplify cases involving one constant and one
-				 * non-constant argument.
-				 */
-				if (expr->opno == BooleanEqualOperator ||
-					expr->opno == BooleanNotEqualOperator)
-				{
-					simple = (Expr *) simplify_boolean_equality(expr->opno,
-																args);
-					if (simple) /* successfully simplified it */
-						return (Node *) simple;
-				}
-
-				/*
 				 * The expression cannot be simplified any further, so build
 				 * and return a replacement OpExpr node using the
 				 * possibly-simplified arguments.
@@ -2810,31 +2913,37 @@ eval_const_expressions_mutator(Node *node,
 					/* one null? then distinct */
 					if (has_null_input)
 						return makeBoolConst(true, false);
+				}
 
-					/* otherwise try to evaluate the '=' operator */
-					/* (NOT okay to try to inline it, though!) */
+				/* otherwise try to evaluate the '=' operator */
+				/* (NOT okay to try to inline it, though!) */
 
-					/*
-					 * Need to get OID of underlying function.  Okay to
-					 * scribble on input to this extent.
-					 */
-					set_opfuncid((OpExpr *) expr);	/* rely on struct
-													 * equivalence */
+				/*
+				 * Need to get OID of underlying function.  Okay to
+				 * scribble on input to this extent.
+				 */
+				set_opfuncid((OpExpr *) expr);	/* rely on struct
+												 * equivalence */
 
-					/*
-					 * Code for op/func reduction is pretty bulky, so split it
-					 * out as a separate function.
-					 */
-					simple = simplify_function(expr->opfuncid,
-											   expr->opresulttype, -1,
-											   expr->opcollid,
-											   expr->inputcollid,
-											   &args,
-											   false,
-											   false,
-											   false,
-											   context);
-					if (simple) /* successfully simplified it */
+				/*
+				 * Code for op/func reduction is pretty bulky, so split it
+				 * out as a separate function.
+				 */
+				simple = simplify_function(expr->opfuncid,
+										   expr->opresulttype, -1,
+										   expr->opcollid,
+										   expr->inputcollid,
+										   &args,
+										   false,
+										   false,
+										   true,
+										   context,
+										   COERCE_EXPLICIT_CALL,
+										   expr->opno,
+										   expr->location);
+				if (simple) /* successfully simplified it */
+				{
+					if (IsA(simple, Const))
 					{
 						/*
 						 * Since the underlying operator is "=", must negate
@@ -2846,6 +2955,31 @@ eval_const_expressions_mutator(Node *node,
 							BoolGetDatum(!DatumGetBool(csimple->constvalue));
 						return (Node *) csimple;
 					}
+
+					/* Else CachedExpr */
+					if (!context->estimate)		/* sanity checks */
+					{
+						/*
+						 * Cache DistinctExpr using information from its cached
+						 * operator.
+						 */
+						CachedExpr *csimple = castNode(CachedExpr, simple);
+						OpExpr	   *subexpr = castNode(OpExpr,
+													   csimple->subexpr);
+						DistinctExpr *newsubexpr = makeNode(DistinctExpr);
+
+						newsubexpr->opno = subexpr->opno;
+						newsubexpr->opfuncid = subexpr->opfuncid;
+						newsubexpr->opresulttype = subexpr->opresulttype;
+						newsubexpr->opretset = subexpr->opretset;
+						newsubexpr->opcollid = subexpr->opcollid;
+						newsubexpr->inputcollid = subexpr->inputcollid;
+						newsubexpr->args = subexpr->args;
+						newsubexpr->location = subexpr->location;
+
+						csimple->subexpr = (CacheableExpr *) newsubexpr;
+						return (Node *) simple;
+					}
 				}
 
 				/*
@@ -2864,24 +2998,180 @@ eval_const_expressions_mutator(Node *node,
 				newexpr->location = expr->location;
 				return (Node *) newexpr;
 			}
+		case T_NullIfExpr:
+			{
+				NullIfExpr *expr = (NullIfExpr *) node;
+				List	   *args = expr->args;
+				ListCell   *arg;
+				bool		has_null_input = false;
+				bool		has_nonconst_input = false;
+				Expr	   *simple;
+				NullIfExpr *newexpr;
+
+				/*
+				 * Reduce constants in the NullIfExpr's arguments.  We know
+				 * args is either NIL or a List node, so we can call
+				 * expression_tree_mutator directly rather than recursing to
+				 * self.
+				 */
+				args = (List *) expression_tree_mutator((Node *) expr->args,
+														eval_const_expressions_mutator,
+														(void *) context);
+
+				/*
+				 * We must do our own check for NULLs because NullIfExpr has
+				 * different results for NULL input than the underlying
+				 * operator does.
+				 */
+				foreach(arg, args)
+				{
+					if (IsA(lfirst(arg), Const))
+						has_null_input |= ((Const *) lfirst(arg))->constisnull;
+					else
+						has_nonconst_input = true;
+				}
+
+				/*
+				 * All constants and has null? Then const arguments aren't
+				 * equal, so return the first one.
+				 */
+				if (!has_nonconst_input && has_null_input)
+					return linitial(args);
+
+				/* Try to evaluate the '=' operator */
+				/* (NOT okay to try to inline it, though!) */
+
+				/*
+				 * Need to get OID of underlying function.  Okay to scribble
+				 * on input to this extent.
+				 */
+				set_opfuncid((OpExpr *) expr);	/* rely on struct
+												 * equivalence */
+
+				/*
+				 * Code for op/func reduction is pretty bulky, so split it out
+				 * as a separate function.
+				 */
+				simple = simplify_function(expr->opfuncid,
+										   BOOLOID, -1,
+										   InvalidOid,
+										   expr->inputcollid,
+										   &args,
+										   false,
+										   false,
+										   true,
+										   context,
+										   COERCE_EXPLICIT_CALL,
+										   expr->opno,
+										   expr->location);
+
+				if (simple) /* successfully simplified it */
+				{
+					if (IsA(simple, Const))
+					{
+						/*
+						 * Since the underlying operator is "=", return the
+						 * first argument if the arguments are not equal and
+						 * NULL otherwise.
+						 */
+						Const	   *csimple = castNode(Const, simple);
+
+						if (DatumGetBool(csimple->constvalue))
+						{
+							return (Node *) makeNullConst(
+													expr->opresulttype,
+													exprTypmod(linitial(args)),
+													expr->opcollid);
+						}
+						else
+						{
+							return linitial(args);
+						}
+					}
+
+					/* Else CachedExpr */
+					if (!context->estimate)		/* sanity checks */
+					{
+						/*
+						 * Cache NullIfExpr using information from its cached
+						 * operator.
+						 */
+						CachedExpr *csimple = castNode(CachedExpr, simple);
+						OpExpr	   *subexpr = castNode(OpExpr,
+													   csimple->subexpr);
+						NullIfExpr *newsubexpr = makeNode(NullIfExpr);
+
+						newsubexpr->opno = subexpr->opno;
+						newsubexpr->opfuncid = subexpr->opfuncid;
+						newsubexpr->opresulttype = expr->opresulttype;
+						newsubexpr->opretset = subexpr->opretset;
+						newsubexpr->opcollid = expr->opcollid;
+						newsubexpr->inputcollid = subexpr->inputcollid;
+						newsubexpr->args = subexpr->args;
+						newsubexpr->location = subexpr->location;
+
+						csimple->subexpr = (CacheableExpr *) newsubexpr;
+						return (Node *) simple;
+					}
+				}
+
+				/*
+				 * The expression cannot be simplified any further, so build
+				 * and return a replacement NullIfExpr node using the
+				 * possibly-simplified arguments.
+				 */
+				newexpr = makeNode(NullIfExpr);
+				newexpr->opno = expr->opno;
+				newexpr->opfuncid = expr->opfuncid;
+				newexpr->opresulttype = expr->opresulttype;
+				newexpr->opretset = expr->opretset;
+				newexpr->opcollid = expr->opcollid;
+				newexpr->inputcollid = expr->inputcollid;
+				newexpr->args = args;
+				newexpr->location = expr->location;
+				return (Node *) newexpr;
+			}
 		case T_ScalarArrayOpExpr:
+		case T_RowCompareExpr:
 			{
-				ScalarArrayOpExpr *saop;
+				/*
+				 * Generic handling for node types whose own processing checks
+				 * that their functions and arguments are safe for evaluation or
+				 * caching.
+				 */
+				ece_check_node_safety_detailed args_detailed,
+											   func_detailed;
 
-				/* Copy the node and const-simplify its arguments */
-				saop = (ScalarArrayOpExpr *) ece_generic_processing(node);
+				/* Copy the node and simplify its arguments */
+				node = ece_generic_processing(node);
 
-				/* Make sure we know underlying function */
-				set_sa_opfuncid(saop);
+				/* Check node args and functions */
+				args_detailed = ece_all_arguments_const(node);
+				func_detailed = ece_functions_are_safe(node, false,
+													   context->estimate);
 
 				/*
-				 * If all arguments are Consts, and it's a safe function, we
-				 * can fold to a constant
+				 * If all arguments are Consts, and node functions are safe for
+				 * evaluation, we can fold to a constant
 				 */
-				if (ece_all_arguments_const(saop) &&
-					ece_function_is_safe(saop->opfuncid, context))
-					return ece_evaluate_expr(saop);
-				return (Node *) saop;
+				if (args_detailed == SAFE_FOR_EVALUATION &&
+					func_detailed == SAFE_FOR_EVALUATION)
+					return ece_evaluate_expr(node);
+
+				/*
+				 * If we do not perform estimation, all arguments are Consts or
+				 * CachedExprs, and node functions are safe for caching, we can
+				 * fold to a CachedExpr
+				 */
+				if (!context->estimate &&
+					SAFE_FOR_CACHING(args_detailed) &&
+					SAFE_FOR_CACHING(func_detailed))
+				{
+					return (Node *) get_cached_expr_node(
+													(CacheableExpr *) node);
+				}
+
+				return node;
 			}
 		case T_BoolExpr:
 			{
@@ -2894,11 +3184,14 @@ eval_const_expressions_mutator(Node *node,
 							List	   *newargs;
 							bool		haveNull = false;
 							bool		forceTrue = false;
-
-							newargs = simplify_or_arguments(expr->args,
-															context,
-															&haveNull,
-															&forceTrue);
+							bool		all_consts_or_cached = true;
+
+							newargs = simplify_or_arguments(
+														expr->args,
+														context,
+														&haveNull,
+														&forceTrue,
+														&all_consts_or_cached);
 							if (forceTrue)
 								return makeBoolConst(true, false);
 							if (haveNull)
@@ -2913,20 +3206,37 @@ eval_const_expressions_mutator(Node *node,
 							 * result
 							 */
 							if (list_length(newargs) == 1)
+							{
 								return (Node *) linitial(newargs);
-							/* Else we still need an OR node */
-							return (Node *) make_orclause(newargs);
+							}
+							else
+							{
+								/* We still need an OR node */
+								Expr	   *newexpr = make_orclause(newargs);
+
+								/* Check if we can cache the new expression */
+								if (!context->estimate &&
+									all_consts_or_cached)
+								{
+									return (Node *) get_cached_expr_node(
+													(CacheableExpr *) newexpr);
+								}
+								return (Node *) newexpr;
+							}
 						}
 					case AND_EXPR:
 						{
 							List	   *newargs;
 							bool		haveNull = false;
 							bool		forceFalse = false;
-
-							newargs = simplify_and_arguments(expr->args,
-															 context,
-															 &haveNull,
-															 &forceFalse);
+							bool		all_consts_or_cached = true;
+
+							newargs = simplify_and_arguments(
+														expr->args,
+														context,
+														&haveNull,
+														&forceFalse,
+														&all_consts_or_cached);
 							if (forceFalse)
 								return makeBoolConst(false, false);
 							if (haveNull)
@@ -2941,13 +3251,28 @@ eval_const_expressions_mutator(Node *node,
 							 * result
 							 */
 							if (list_length(newargs) == 1)
+							{
 								return (Node *) linitial(newargs);
-							/* Else we still need an AND node */
-							return (Node *) make_andclause(newargs);
+							}
+							else
+							{
+								/* We still need an AND node */
+								Expr	   *newexpr = make_andclause(newargs);
+
+								/* Check if we can cache the new expression */
+								if (!context->estimate &&
+									all_consts_or_cached)
+								{
+									return (Node *) get_cached_expr_node(
+													(CacheableExpr *) newexpr);
+								}
+								return (Node *) newexpr;
+							}
 						}
 					case NOT_EXPR:
 						{
 							Node	   *arg;
+							Expr	   *newexpr;
 
 							Assert(list_length(expr->args) == 1);
 							arg = eval_const_expressions_mutator(linitial(expr->args),
@@ -2957,7 +3282,24 @@ eval_const_expressions_mutator(Node *node,
 							 * Use negate_clause() to see if we can simplify
 							 * away the NOT.
 							 */
-							return negate_clause(arg);
+							newexpr = (Expr *) negate_clause(arg);
+
+							if ((IsA(newexpr, BoolExpr) &&
+								 ((BoolExpr *) newexpr)->boolop == NOT_EXPR) ||
+								IsA(newexpr, CachedExpr))
+							{
+								/*
+								 * Simplification did not help and we again got
+								 * the NOT node, or the expression is already
+								 * cached.
+								 */
+								return (Node *) newexpr;
+							}
+
+							/* Try to simplify the whole new expression */
+							return eval_const_expressions_mutator(
+															(Node *) newexpr,
+															context);
 						}
 					default:
 						elog(ERROR, "unrecognized boolop: %d",
@@ -2982,7 +3324,8 @@ eval_const_expressions_mutator(Node *node,
 				 * If we can simplify the input to a constant, then we don't
 				 * need the RelabelType node anymore: just change the type
 				 * field of the Const node.  Otherwise, must copy the
-				 * RelabelType node.
+				 * RelabelType node; cache it if its arg is also cached and we
+				 * do not perform estimation.
 				 */
 				RelabelType *relabel = (RelabelType *) node;
 				Node	   *arg;
@@ -3016,6 +3359,13 @@ eval_const_expressions_mutator(Node *node,
 					newrelabel->resultcollid = relabel->resultcollid;
 					newrelabel->relabelformat = relabel->relabelformat;
 					newrelabel->location = relabel->location;
+
+					/* Check if we can cache this node */
+					if (!context->estimate && arg && IsA(arg, CachedExpr))
+					{
+						return (Node *) get_cached_expr_node(
+												(CacheableExpr *) newrelabel);
+					}
 					return (Node *) newrelabel;
 				}
 			}
@@ -3029,6 +3379,7 @@ eval_const_expressions_mutator(Node *node,
 				Oid			intypioparam;
 				Expr	   *simple;
 				CoerceViaIO *newexpr;
+				bool		cache = false;
 
 				/* Make a List so we can use simplify_function */
 				args = list_make1(expr->arg);
@@ -3054,8 +3405,11 @@ eval_const_expressions_mutator(Node *node,
 										   &args,
 										   false,
 										   true,
-										   true,
-										   context);
+										   false,
+										   context,
+										   COERCE_EXPLICIT_CALL,
+										   InvalidOid,
+										   -1);
 				if (simple)		/* successfully simplified output fn */
 				{
 					/*
@@ -3086,10 +3440,23 @@ eval_const_expressions_mutator(Node *node,
 											   &args,
 											   false,
 											   false,
-											   true,
-											   context);
+											   false,
+											   context,
+											   COERCE_EXPLICIT_CALL,
+											   InvalidOid,
+											   -1);
 					if (simple) /* successfully simplified input fn */
-						return (Node *) simple;
+					{
+						if (IsA(simple, CachedExpr))
+						{
+							/* return later cached CoerceViaIO node */
+							cache = true;
+						}
+						else
+						{
+							return (Node *) simple;
+						}
+					}
 				}
 
 				/*
@@ -3103,27 +3470,50 @@ eval_const_expressions_mutator(Node *node,
 				newexpr->resultcollid = expr->resultcollid;
 				newexpr->coerceformat = expr->coerceformat;
 				newexpr->location = expr->location;
+				/* Check if we can cache the new expression */
+				if (cache)
+				{
+					return (Node *) get_cached_expr_node(
+													(CacheableExpr *) newexpr);
+				}
 				return (Node *) newexpr;
 			}
 		case T_ArrayCoerceExpr:
 			{
 				ArrayCoerceExpr *ac;
+				ece_check_node_safety_detailed func_detailed;
 
-				/* Copy the node and const-simplify its arguments */
+				/* Copy the node and simplify its arguments */
 				ac = (ArrayCoerceExpr *) ece_generic_processing(node);
 
+				/* Check the functions in the per-element expression */
+				func_detailed = ece_functions_are_safe((Node *) ac->elemexpr,
+													   true, false);
+
 				/*
 				 * If constant argument and the per-element expression is
 				 * immutable, we can simplify the whole thing to a constant.
-				 * Exception: although contain_mutable_functions considers
+				 * Exception: although ece_functions_are_safe considers
 				 * CoerceToDomain immutable for historical reasons, let's not
 				 * do so here; this ensures coercion to an array-over-domain
 				 * does not apply the domain's constraints until runtime.
 				 */
 				if (ac->arg && IsA(ac->arg, Const) &&
 					ac->elemexpr && !IsA(ac->elemexpr, CoerceToDomain) &&
-					!contain_mutable_functions((Node *) ac->elemexpr))
+					func_detailed == SAFE_FOR_EVALUATION)
 					return ece_evaluate_expr(ac);
+
+				/*
+				 * If not estimation mode, constant or cached argument, and the
+				 * per-element expression is immutable or stable, we can cache
+				 * the whole thing.
+				 */
+				if (!context->estimate &&
+					is_const_or_cached((Node *) ac->arg) &&
+					ac->elemexpr && !IsA(ac->elemexpr, CoerceToDomain) &&
+					SAFE_FOR_CACHING(func_detailed))
+					return (Node *) get_cached_expr_node((CacheableExpr *) ac);
+
 				return (Node *) ac;
 			}
 		case T_CollateExpr:
@@ -3169,7 +3559,9 @@ eval_const_expressions_mutator(Node *node,
 						arg = (Node *) ((RelabelType *) arg)->arg;
 					relabel->arg = (Expr *) arg;
 
-					return (Node *) relabel;
+					/* Try to cache the new expression */
+					return eval_const_expressions_mutator((Node *) relabel,
+														  context);
 				}
 			}
 		case T_CaseExpr:
@@ -3202,6 +3594,10 @@ eval_const_expressions_mutator(Node *node,
 				 * expression when executing the CASE, since any contained
 				 * CaseTestExprs that might have referred to it will have been
 				 * replaced by the constant.
+				 *
+				 * If we do not perform estimation and  all expressions in the
+				 * CASE expression are constant or cached, the CASE expression
+				 * will also be cached.
 				 *----------
 				 */
 				CaseExpr   *caseexpr = (CaseExpr *) node;
@@ -3212,6 +3608,7 @@ eval_const_expressions_mutator(Node *node,
 				bool		const_true_cond;
 				Node	   *defresult = NULL;
 				ListCell   *arg;
+				bool		all_subexpr_const_or_cached = true;
 
 				/* Simplify the test expression, if any */
 				newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
@@ -3224,8 +3621,17 @@ eval_const_expressions_mutator(Node *node,
 					context->case_val = newarg;
 					newarg = NULL;	/* not needed anymore, see above */
 				}
+				else if (newarg && IsA(newarg, CachedExpr))
+				{
+					/* create dummy CachedExpr node */
+					context->case_val = (Node *) get_cached_expr_node(NULL);
+				}
 				else
+				{
 					context->case_val = NULL;
+					if (newarg)
+						all_subexpr_const_or_cached = false;
+				}
 
 				/* Simplify the WHEN clauses */
 				newargs = NIL;
@@ -3240,26 +3646,43 @@ eval_const_expressions_mutator(Node *node,
 					casecond = eval_const_expressions_mutator((Node *) oldcasewhen->expr,
 															  context);
 
-					/*
-					 * If the test condition is constant FALSE (or NULL), then
-					 * drop this WHEN clause completely, without processing
-					 * the result.
-					 */
-					if (casecond && IsA(casecond, Const))
+					if (casecond)
 					{
-						Const	   *const_input = (Const *) casecond;
-
-						if (const_input->constisnull ||
-							!DatumGetBool(const_input->constvalue))
-							continue;	/* drop alternative with FALSE cond */
-						/* Else it's constant TRUE */
-						const_true_cond = true;
+						/*
+						 * If the test condition is constant FALSE (or NULL),
+						 * then drop this WHEN clause completely, without
+						 * processing the result.
+						 */
+						if (IsA(casecond, Const))
+						{
+							Const	   *const_input = (Const *) casecond;
+
+							if (const_input->constisnull ||
+								!DatumGetBool(const_input->constvalue))
+							{
+								/* Drop alternative with FALSE cond */
+								continue;
+							}
+							/* Else it's constant TRUE */
+							const_true_cond = true;
+						}
+						else if (IsA(casecond, CachedExpr))
+						{
+							/* OK */
+						}
+						else
+						{
+							all_subexpr_const_or_cached = false;
+						}
 					}
 
 					/* Simplify this alternative's result value */
 					caseresult = eval_const_expressions_mutator((Node *) oldcasewhen->result,
 																context);
 
+					all_subexpr_const_or_cached &=
+						is_const_or_cached(caseresult);
+
 					/* If non-constant test condition, emit a new WHEN node */
 					if (!const_true_cond)
 					{
@@ -3283,9 +3706,14 @@ eval_const_expressions_mutator(Node *node,
 
 				/* Simplify the default result, unless we replaced it above */
 				if (!const_true_cond)
+				{
 					defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult,
 															   context);
 
+					all_subexpr_const_or_cached &=
+						is_const_or_cached(defresult);
+				}
+
 				context->case_val = save_case_val;
 
 				/*
@@ -3302,35 +3730,67 @@ eval_const_expressions_mutator(Node *node,
 				newcase->args = newargs;
 				newcase->defresult = (Expr *) defresult;
 				newcase->location = caseexpr->location;
+				/* Check if we can cache this node */
+				if (!context->estimate && all_subexpr_const_or_cached)
+				{
+					return (Node *) get_cached_expr_node(
+													(CacheableExpr *) newcase);
+				}
 				return (Node *) newcase;
 			}
 		case T_CaseTestExpr:
 			{
+				Node	   *case_val = context->case_val;
+
 				/*
-				 * If we know a constant test value for the current CASE
+				 * If we know that the test node for the current CASE is cached,
+				 * return the cached current node.
+				 * Else if we know a constant test value for the current CASE
 				 * construct, substitute it for the placeholder.  Else just
 				 * return the placeholder as-is.
 				 */
-				if (context->case_val)
-					return copyObject(context->case_val);
-				else
-					return copyObject(node);
+				if (case_val)
+				{
+					if (IsA(case_val, CachedExpr))
+					{
+						return (Node *) get_cached_expr_node(
+											(CacheableExpr *) copyObject(node));
+					}
+					return copyObject(case_val);
+				}
+				return copyObject(node);
 			}
 		case T_ArrayRef:
 		case T_ArrayExpr:
 		case T_RowExpr:
+		case T_ConvertRowtypeExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
 			{
 				/*
 				 * Generic handling for node types whose own processing is
 				 * known to be immutable, and for which we need no smarts
-				 * beyond "simplify if all inputs are constants".
+				 * beyond "simplify if all inputs are constants or cached
+				 * expressions".
 				 */
+				ece_check_node_safety_detailed args_detailed;
 
-				/* Copy the node and const-simplify its arguments */
+				/* Copy the node and simplify its arguments */
 				node = ece_generic_processing(node);
 				/* If all arguments are Consts, we can fold to a constant */
-				if (ece_all_arguments_const(node))
+				args_detailed = ece_all_arguments_const(node);
+				if (args_detailed == SAFE_FOR_EVALUATION)
 					return ece_evaluate_expr(node);
+				/*
+				 * If we do not perform estimation, and all arguments are Consts
+				 * or CachedExprs, we can cache the result of this node.
+				 */
+				if (!context->estimate &&
+					args_detailed == SAFE_FOR_CACHING_ONLY)
+				{
+					return (Node *) get_cached_expr_node(
+													(CacheableExpr *) node);
+				}
 				return node;
 			}
 		case T_CoalesceExpr:
@@ -3339,6 +3799,7 @@ eval_const_expressions_mutator(Node *node,
 				CoalesceExpr *newcoalesce;
 				List	   *newargs;
 				ListCell   *arg;
+				bool		all_args_are_const_or_cached = true;
 
 				newargs = NIL;
 				foreach(arg, coalesceexpr->args)
@@ -3365,6 +3826,14 @@ eval_const_expressions_mutator(Node *node,
 						newargs = lappend(newargs, e);
 						break;
 					}
+					else if (IsA(e, CachedExpr))
+					{
+						/* OK */
+					}
+					else
+					{
+						all_args_are_const_or_cached = false;
+					}
 					newargs = lappend(newargs, e);
 				}
 
@@ -3382,6 +3851,12 @@ eval_const_expressions_mutator(Node *node,
 				newcoalesce->coalescecollid = coalesceexpr->coalescecollid;
 				newcoalesce->args = newargs;
 				newcoalesce->location = coalesceexpr->location;
+				/* Check if we can cache this node */
+				if (!context->estimate && all_args_are_const_or_cached)
+				{
+					return (Node *) get_cached_expr_node(
+												(CacheableExpr *) newcoalesce);
+				}
 				return (Node *) newcoalesce;
 			}
 		case T_SQLValueFunction:
@@ -3389,7 +3864,7 @@ eval_const_expressions_mutator(Node *node,
 				/*
 				 * All variants of SQLValueFunction are stable, so if we are
 				 * estimating the expression's value, we should evaluate the
-				 * current function value.  Otherwise just copy.
+				 * current function value.  Otherwise copy and cache it.
 				 */
 				SQLValueFunction *svf = (SQLValueFunction *) node;
 
@@ -3399,7 +3874,8 @@ eval_const_expressions_mutator(Node *node,
 												  svf->typmod,
 												  InvalidOid);
 				else
-					return copyObject((Node *) svf);
+					return (Node *) get_cached_expr_node(
+											(CacheableExpr *) copyObject(node));
 			}
 		case T_FieldSelect:
 			{
@@ -3487,6 +3963,13 @@ eval_const_expressions_mutator(Node *node,
 											  newfselect->resultcollid))
 						return ece_evaluate_expr(newfselect);
 				}
+				/* Check if we can cache this node */
+				if (!context->estimate &&
+					arg && (IsA(arg, Const) || IsA(arg, CachedExpr)))
+				{
+					return (Node *) get_cached_expr_node(
+												(CacheableExpr *) newfselect);
+				}
 				return (Node *) newfselect;
 			}
 		case T_NullTest:
@@ -3494,10 +3977,28 @@ eval_const_expressions_mutator(Node *node,
 				NullTest   *ntest = (NullTest *) node;
 				NullTest   *newntest;
 				Node	   *arg;
+				RowExpr    *rarg = NULL;
+				bool		rarg_cached = false;
 
 				arg = eval_const_expressions_mutator((Node *) ntest->arg,
 													 context);
-				if (ntest->argisrow && arg && IsA(arg, RowExpr))
+
+				/* Check if arg is RowExpr or cached RowExpr */
+				if (arg && IsA(arg, RowExpr))
+				{
+					rarg = (RowExpr *) arg;
+				}
+				else if (arg && IsA(arg, CachedExpr))
+				{
+					CacheableExpr *subexpr = ((CachedExpr *) arg)->subexpr;
+					if (IsA(subexpr, RowExpr))
+					{
+						rarg = (RowExpr *) subexpr;
+						rarg_cached = true;
+					}
+				}
+
+				if (ntest->argisrow && rarg)
 				{
 					/*
 					 * We break ROW(...) IS [NOT] NULL into separate tests on
@@ -3505,7 +4006,6 @@ eval_const_expressions_mutator(Node *node,
 					 * efficient to evaluate, as well as being more amenable
 					 * to optimization.
 					 */
-					RowExpr    *rarg = (RowExpr *) arg;
 					List	   *newargs = NIL;
 					ListCell   *l;
 
@@ -3546,9 +4046,27 @@ eval_const_expressions_mutator(Node *node,
 						return makeBoolConst(true, false);
 					/* If only one nonconst input, it's the result */
 					if (list_length(newargs) == 1)
+					{
 						return (Node *) linitial(newargs);
-					/* Else we need an AND node */
-					return (Node *) make_andclause(newargs);
+					}
+					else
+					{
+						/* We need an AND node */
+						Node	   *newnode = (Node *) make_andclause(newargs);
+
+						/*
+						 * We can cache the result if we do not perform
+						 * estimation, and the input also was cached (since only
+						 * the const args were ommitted and they do
+						 * not change this).
+						 */
+						if (!context->estimate && rarg_cached)
+						{
+							return (Node *) get_cached_expr_node(
+													(CacheableExpr *) newnode);
+						}
+						return newnode;
+					}
 				}
 				if (!ntest->argisrow && arg && IsA(arg, Const))
 				{
@@ -3578,6 +4096,12 @@ eval_const_expressions_mutator(Node *node,
 				newntest->nulltesttype = ntest->nulltesttype;
 				newntest->argisrow = ntest->argisrow;
 				newntest->location = ntest->location;
+				/* Check if we can cache this node */
+				if (!context->estimate && is_const_or_cached(arg))
+				{
+					return (Node *) get_cached_expr_node(
+													(CacheableExpr *) newntest);
+				}
 				return (Node *) newntest;
 			}
 		case T_BooleanTest:
@@ -3638,6 +4162,12 @@ eval_const_expressions_mutator(Node *node,
 				newbtest->arg = (Expr *) arg;
 				newbtest->booltesttype = btest->booltesttype;
 				newbtest->location = btest->location;
+				/* Check if we can cache this node */
+				if (!context->estimate && is_const_or_cached(arg))
+				{
+					return (Node *) get_cached_expr_node(
+													(CacheableExpr *) newbtest);
+				}
 				return (Node *) newbtest;
 			}
 		case T_PlaceHolderVar:
@@ -3657,13 +4187,23 @@ eval_const_expressions_mutator(Node *node,
 													  context);
 			}
 			break;
+		case T_CachedExpr:
+			{
+				/*
+				 * Process it, because maybe we can get the Const node or we
+				 * perform estimation and there should be no cached expressions.
+				 */
+				return eval_const_expressions_mutator(
+										(Node *) ((CachedExpr *) node)->subexpr,
+										context);
+			}
 		default:
 			break;
 	}
 
 	/*
 	 * For any node type not handled above, copy the node unchanged but
-	 * const-simplify its subexpressions.  This is the correct thing for node
+	 * simplify its subexpressions.  This is the correct thing for node
 	 * types whose behavior might change between planning and execution, such
 	 * as CoerceToDomain.  It's also a safe default for new node types not
 	 * known to this routine.
@@ -3672,33 +4212,80 @@ eval_const_expressions_mutator(Node *node,
 }
 
 /*
- * Subroutine for eval_const_expressions: check for non-Const nodes.
+ * Subroutine for eval_const_expressions: check for non-Const or non-CachedExpr
+ * nodes.
  *
- * We can abort recursion immediately on finding a non-Const node.  This is
+ * We can abort recursion immediately on finding a non-Const and non-CachedExpr
+ * node.  This is
  * critical for performance, else eval_const_expressions_mutator would take
  * O(N^2) time on non-simplifiable trees.  However, we do need to descend
  * into List nodes since expression_tree_walker sometimes invokes the walker
  * function directly on List subtrees.
+ *
+ * The output argument *detailed must be initialized SAFE_FOR_EVALUATION by the
+ * caller. It will be set SAFE_FOR_CACHING_ONLY if only constant and cached
+ * nodes are detected anywhere in the argument list. It will be set
+ * SAFE_FOR_NOTHING if a non-constant or non-cached node is detected anywhere in
+ * the argument list.
  */
 static bool
-contain_non_const_walker(Node *node, void *context)
+contain_non_const_walker(Node *node, ece_check_node_safety_detailed *detailed)
 {
 	if (node == NULL)
+	{
+		/* this does not affect the value of the detailed result */
 		return false;
+	}
 	if (IsA(node, Const))
+	{
+		/* this does not affect the value of the detailed result */
 		return false;
+	}
+	if (IsA(node, CachedExpr))
+	{
+		*detailed = SAFE_FOR_CACHING_ONLY;
+		return false;
+	}
 	if (IsA(node, List))
-		return expression_tree_walker(node, contain_non_const_walker, context);
+	{
+		return expression_tree_walker(node, contain_non_const_walker,
+									  (void *) detailed);
+	}
 	/* Otherwise, abort the tree traversal and return true */
+	*detailed = SAFE_FOR_NOTHING;
 	return true;
 }
 
 /*
- * Subroutine for eval_const_expressions: check if a function is OK to evaluate
+ * ece_functions_are_safe
+ *	  Search for noncacheable functions within a clause (possibly recursively).
+ *
+ *	  Returns true if any non-cacheable function found.
+ *
+ * The output argument context->detailed must be initialized
+ * SAFE_FOR_EVALUATION by the caller. It will be set SAFE_FOR_CACHING_ONLY if
+ * only immutable functions (or also stable functions in case of estimation)
+ * are found. It will be set SAFE_FOR_NOTHING if a volatile function is detected
+ * anywhere in the subexpressions.
  */
+static ece_check_node_safety_detailed
+ece_functions_are_safe(Node *node, bool recurse, bool estimate)
+{
+	ece_functions_are_safe_context context;
+
+	context.detailed = SAFE_FOR_EVALUATION;
+	context.recurse = recurse;
+	context.estimate = estimate;
+
+	ece_functions_are_safe_walker(node, &context);
+	return context.detailed;
+}
+
 static bool
-ece_function_is_safe(Oid funcid, eval_const_expressions_context *context)
+ece_functions_are_safe_checker(Oid funcid, void *context)
 {
+	ece_functions_are_safe_context *safe_context =
+		(ece_functions_are_safe_context *) context;
 	char		provolatile = func_volatile(funcid);
 
 	/*
@@ -3709,10 +4296,74 @@ ece_function_is_safe(Oid funcid, eval_const_expressions_context *context)
 	 * to estimate the value at all.
 	 */
 	if (provolatile == PROVOLATILE_IMMUTABLE)
+	{
+		/* this does not affect the value of the detailed result */
+		return false;
+	}
+	else if (provolatile == PROVOLATILE_STABLE)
+	{
+		if (safe_context->estimate)
+		{
+			/* this does not affect the value of the detailed result */
+			return false;
+		}
+		else
+		{
+			safe_context->detailed = SAFE_FOR_CACHING_ONLY;
+			return false;
+		}
+	}
+	else
+	{
+		safe_context->detailed = SAFE_FOR_NOTHING;
 		return true;
-	if (context->estimate && provolatile == PROVOLATILE_STABLE)
+	}
+}
+
+static bool
+ece_functions_are_safe_walker(Node *node,
+							  ece_functions_are_safe_context *context)
+{
+	if (node == NULL)
+		return false;
+	/* Check for functions in node itself */
+	if (check_functions_in_node(node, ece_functions_are_safe_checker,
+								(void *) context))
 		return true;
-	return false;
+
+	if (IsA(node, SQLValueFunction))
+	{
+		/* all variants of SQLValueFunction are stable */
+		if (!context->estimate)
+			context->detailed = SAFE_FOR_CACHING_ONLY;
+		return false;
+	}
+
+	if (IsA(node, NextValueExpr))
+	{
+		/* NextValueExpr is volatile */
+		context->detailed = SAFE_FOR_NOTHING;
+		return true;
+	}
+
+	/*
+	 * See notes in contain_mutable_functions_walker about why we treat
+	 * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable.  Hence, immutable
+	 * functions do not affect the value of the detailed result.
+	 */
+
+	if (!context->recurse)
+		return false;
+
+	/* Recurse to check arguments */
+	if (IsA(node, Query))
+	{
+		/* Recurse into subselects */
+		return query_tree_walker((Query *) node, ece_functions_are_safe_walker,
+								 (void *) context, 0);
+	}
+	return expression_tree_walker(node, ece_functions_are_safe_walker,
+								  (void *) context);
 }
 
 /*
@@ -3732,12 +4383,16 @@ ece_function_is_safe(Oid funcid, eval_const_expressions_context *context)
  *
  * The output arguments *haveNull and *forceTrue must be initialized false
  * by the caller.  They will be set true if a NULL constant or TRUE constant,
- * respectively, is detected anywhere in the argument list.
+ * respectively, is detected anywhere in the argument list. The output argument
+ * *all_consts_or_cached must be initialized true by the caller. It will be set
+ * false if a non-constant or non-cached node is detected anywhere in the
+ * argument list.
  */
 static List *
 simplify_or_arguments(List *args,
 					  eval_const_expressions_context *context,
-					  bool *haveNull, bool *forceTrue)
+					  bool *haveNull, bool *forceTrue,
+					  bool *all_consts_or_cached)
 {
 	List	   *newargs = NIL;
 	List	   *unprocessed_args;
@@ -3819,6 +4474,14 @@ simplify_or_arguments(List *args,
 			/* otherwise, we can drop the constant-false input */
 			continue;
 		}
+		else if (IsA(arg, CachedExpr))
+		{
+			/* OK */
+		}
+		else
+		{
+			*all_consts_or_cached = false;
+		}
 
 		/* else emit the simplified arg into the result list */
 		newargs = lappend(newargs, arg);
@@ -3844,12 +4507,16 @@ simplify_or_arguments(List *args,
  *
  * The output arguments *haveNull and *forceFalse must be initialized false
  * by the caller.  They will be set true if a null constant or false constant,
- * respectively, is detected anywhere in the argument list.
+ * respectively, is detected anywhere in the argument list. The output argument
+ * *all_consts_or_cached must be initialized true by the caller. It will be set
+ * false if a non-constant or non-cached node is detected anywhere in the
+ * argument list.
  */
 static List *
 simplify_and_arguments(List *args,
 					   eval_const_expressions_context *context,
-					   bool *haveNull, bool *forceFalse)
+					   bool *haveNull, bool *forceFalse,
+					   bool *all_consts_or_cached)
 {
 	List	   *newargs = NIL;
 	List	   *unprocessed_args;
@@ -3921,6 +4588,14 @@ simplify_and_arguments(List *args,
 			/* otherwise, we can drop the constant-true input */
 			continue;
 		}
+		else if (IsA(arg, CachedExpr))
+		{
+			/* OK */
+		}
+		else
+		{
+			*all_consts_or_cached = false;
+		}
 
 		/* else emit the simplified arg into the result list */
 		newargs = lappend(newargs, arg);
@@ -4001,8 +4676,10 @@ simplify_boolean_equality(Oid opno, List *args)
  * Inputs are the function OID, actual result type OID (which is needed for
  * polymorphic functions), result typmod, result collation, the input
  * collation to use for the function, the original argument list (not
- * const-simplified yet, unless process_args is false), and some flags;
- * also the context data for eval_const_expressions.
+ * simplified yet, unless process_args is false), the coercion form of the
+ * function output, the operator OID (InvalidOid if the function itself), the
+ * location (used only for simple caching), and some flags; also the context
+ * data for eval_const_expressions.
  *
  * Returns a simplified expression if successful, or NULL if cannot
  * simplify the function call.
@@ -4011,15 +4688,17 @@ simplify_boolean_equality(Oid opno, List *args)
  * lists into positional notation and/or adding any needed default argument
  * expressions; which is a bit grotty, but it avoids extra fetches of the
  * function's pg_proc tuple.  For this reason, the args list is
- * pass-by-reference.  Conversion and const-simplification of the args list
+ * pass-by-reference.  Conversion and simplification of the args list
  * will be done even if simplification of the function call itself is not
  * possible.
  */
 static Expr *
 simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List **args_p,
-				  bool funcvariadic, bool process_args, bool allow_non_const,
-				  eval_const_expressions_context *context)
+				  bool funcvariadic, bool process_args,
+				  bool allow_only_consts_and_simple_caching,
+				  eval_const_expressions_context *context,
+				  CoercionForm funcformat, Oid opno, int location)
 {
 	List	   *args = *args_p;
 	HeapTuple	func_tuple;
@@ -4027,16 +4706,18 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 	Expr	   *newexpr;
 
 	/*
-	 * We have three strategies for simplification: execute the function to
+	 * We have four strategies for simplification: execute the function to
 	 * deliver a constant result, use a transform function to generate a
-	 * substitute node tree, or expand in-line the body of the function
+	 * substitute node tree, expand in-line the body of the function
 	 * definition (which only works for simple SQL-language functions, but
-	 * that is a common case).  Each case needs access to the function's
-	 * pg_proc tuple, so fetch it just once.
+	 * that is a common case), or cache this function call.
+	 * Each case needs access to the function's pg_proc tuple, so fetch it just
+	 * once.
 	 *
-	 * Note: the allow_non_const flag suppresses both the second and third
-	 * strategies; so if !allow_non_const, simplify_function can only return a
-	 * Const or NULL.  Argument-list rewriting happens anyway, though.
+	 * Note: the allow_only_consts_and_simple_caching flag suppresses both the
+	 * second and third strategies; so if allow_only_consts_and_simple_caching,
+	 * simplify_function can only return a Const, CachedExpr or NULL.
+	 * Argument-list rewriting happens anyway, though.
 	 */
 	func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
 	if (!HeapTupleIsValid(func_tuple))
@@ -4066,7 +4747,9 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 								args, funcvariadic,
 								func_tuple, context);
 
-	if (!newexpr && allow_non_const && OidIsValid(func_form->protransform))
+	if (!newexpr &&
+		!allow_only_consts_and_simple_caching &&
+		OidIsValid(func_form->protransform))
 	{
 		/*
 		 * Build a dummy FuncExpr node containing the simplified arg list.  We
@@ -4089,13 +4772,46 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 		newexpr = (Expr *)
 			DatumGetPointer(OidFunctionCall1(func_form->protransform,
 											 PointerGetDatum(&fexpr)));
+
+		if (newexpr && !context->estimate && !IsA(newexpr, CachedExpr))
+		{
+			/* Try to cache the new expression. */
+			newexpr = (Expr *) eval_const_expressions_mutator((Node *) newexpr,
+															  context);
+		}
 	}
 
-	if (!newexpr && allow_non_const)
+	if (!newexpr && !allow_only_consts_and_simple_caching)
 		newexpr = inline_function(funcid, result_type, result_collid,
 								  input_collid, args, funcvariadic,
 								  func_tuple, context);
 
+	/*
+	 * If the operator is boolean equality or inequality, we know
+	 * how to simplify cases involving one constant and one
+	 * non-constant argument.
+	 */
+	if (!newexpr &&
+		(opno == BooleanEqualOperator ||
+		 opno == BooleanNotEqualOperator))
+	{
+		newexpr = (Expr *) simplify_boolean_equality(opno, args);
+
+		if (newexpr && !context->estimate && !IsA(newexpr, CachedExpr))
+		{
+			/* Try to cache the new expression. */
+			newexpr = (Expr *) eval_const_expressions_mutator((Node *) newexpr,
+															  context);
+		}
+	}
+
+	if (!newexpr && !context->estimate)
+	{
+		newexpr = cache_function(funcid, result_type, result_collid,
+								 input_collid, args, funcvariadic, func_tuple,
+								 context, funcformat, opno, location);
+	}
+
 	ReleaseSysCache(func_tuple);
 
 	return newexpr;
@@ -4802,6 +5518,14 @@ substitute_actual_parameters_mutator(Node *node,
 		/* We don't need to copy at this time (it'll get done later) */
 		return list_nth(context->args, param->paramid - 1);
 	}
+	if (IsA(node, CachedExpr))
+	{
+		CachedExpr *cachedexpr = (CachedExpr *) node;
+
+		return substitute_actual_parameters_mutator(
+												(Node *) cachedexpr->subexpr,
+												context);
+	}
 	return expression_tree_mutator(node, substitute_actual_parameters_mutator,
 								   (void *) context);
 }
@@ -4962,6 +5686,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 		return NULL;
 	rtfunc = (RangeTblFunction *) linitial(rte->functions);
 
+	/*
+	 * Do not check whether this is CachedExpr with a FuncExpr subexpression
+	 * because STF are not cached.
+	 */
 	if (!IsA(rtfunc->funcexpr, FuncExpr))
 		return NULL;
 	fexpr = (FuncExpr *) rtfunc->funcexpr;
@@ -5261,6 +5989,14 @@ substitute_actual_srf_parameters_mutator(Node *node,
 			return result;
 		}
 	}
+	if (IsA(node, CachedExpr))
+	{
+		CachedExpr *cachedexpr = (CachedExpr *) node;
+
+		return substitute_actual_srf_parameters_mutator(
+													(Node *) cachedexpr->subexpr,
+													context);
+	}
 	return expression_tree_mutator(node,
 								   substitute_actual_srf_parameters_mutator,
 								   (void *) context);
@@ -5308,3 +6044,106 @@ tlist_matches_coltypelist(List *tlist, List *coltypelist)
 
 	return true;
 }
+
+/*
+ * Return the new CachedExpr node.
+ *
+ * This is an auxiliary function for the functions cache_function and
+ * eval_const_expressions_mutator: checking that this subexpression can be
+ * cached is performed earlier in them.
+ */
+static Expr *
+get_cached_expr_node(CacheableExpr *subexpr)
+{
+	CachedExpr *cached_expr = makeNode(CachedExpr);
+	cached_expr->subexpr = subexpr;
+	return (Expr *) cached_expr;
+}
+
+/*
+ * Return true if node is null or Const or CachedExpr.
+ */
+static bool
+is_const_or_cached(const Node *node)
+{
+	if (node == NULL)
+		return false;
+
+	return (IsA(node, Const) || IsA(node, CachedExpr));
+}
+
+/*
+ * cache_function: try to cache a result of calling this function
+ *
+ * We can do this if the function does not return a set, is not volatile itself,
+ * and its arguments are constants or recursively cached expressions. Also we do
+ * not cache during estimation.
+ *
+ * Returns a cached expression if successful, or NULL if cannot cache the
+ * function. Returns an expression for the cached operator if opno is not
+ * InvalidOid.
+ */
+static Expr *
+cache_function(Oid funcid, Oid result_type, Oid result_collid, Oid input_collid,
+			   List *args, bool funcvariadic, HeapTuple func_tuple,
+			   eval_const_expressions_context *context, CoercionForm funcformat,
+			   Oid opno, int location)
+{
+	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+	Expr	   *newexpr;
+
+	/* Sanity checks */
+	if (context->estimate)
+		return NULL;
+
+	/* Can't cache the result of the function if it returns a set */
+	if (funcform->proretset)
+		return NULL;
+
+	/* Check for non-constant or non-cached inputs */
+	if (!SAFE_FOR_CACHING(ece_all_arguments_const((Node *) args)))
+		return NULL;
+
+	/* Can't cache volatile functions */
+	if (funcform->provolatile == PROVOLATILE_IMMUTABLE)
+		/* okay */ ;
+	else if (funcform->provolatile == PROVOLATILE_STABLE)
+		/* okay */ ;
+	else
+		return NULL;
+
+	/* Create an expression for this function/operator call */
+	if (opno == InvalidOid)
+	{
+		FuncExpr   *funcexpr = makeNode(FuncExpr);
+
+		funcexpr->funcid = funcid;
+		funcexpr->funcresulttype = result_type;
+		funcexpr->funcretset = false;
+		funcexpr->funcvariadic = funcvariadic;
+		funcexpr->funcformat = funcformat;
+		funcexpr->funccollid = result_collid;
+		funcexpr->inputcollid = input_collid;
+		funcexpr->args = args;
+		funcexpr->location = location;
+
+		newexpr = (Expr *) funcexpr;
+	}
+	else
+	{
+		OpExpr	   *opexpr = makeNode(OpExpr);
+
+		opexpr->opno = opno;
+		opexpr->opfuncid = funcid;
+		opexpr->opresulttype = result_type;
+		opexpr->opretset = false;
+		opexpr->opcollid = result_collid;
+		opexpr->inputcollid = input_collid;
+		opexpr->args = args;
+		opexpr->location = location;
+
+		newexpr = (Expr *) opexpr;
+	}
+
+	return get_cached_expr_node((CacheableExpr *) newexpr);
+}
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index b16b1e4..6cd9845 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -196,6 +196,11 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
 		context->sublevels_up--;
 		return result;
 	}
+	if (IsA(node, CachedExpr))
+	{
+		/* no vars in cached expressions */
+		return false;
+	}
 	return expression_tree_walker(node, pull_varnos_walker,
 								  (void *) context);
 }
@@ -243,6 +248,11 @@ pull_varattnos_walker(Node *node, pull_varattnos_context *context)
 							   var->varattno - FirstLowInvalidHeapAttributeNumber);
 		return false;
 	}
+	if (IsA(node, CachedExpr))
+	{
+		/* no vars in cached expressions */
+		return false;
+	}
 
 	/* Should not find an unplanned subquery */
 	Assert(!IsA(node, Query));
@@ -312,6 +322,11 @@ pull_vars_walker(Node *node, pull_vars_context *context)
 		context->sublevels_up--;
 		return result;
 	}
+	if (IsA(node, CachedExpr))
+	{
+		/* no vars in cached expressions */
+		return false;
+	}
 	return expression_tree_walker(node, pull_vars_walker,
 								  (void *) context);
 }
@@ -352,6 +367,11 @@ contain_var_clause_walker(Node *node, void *context)
 			return true;		/* abort the tree traversal and return true */
 		/* else fall through to check the contained expr */
 	}
+	if (IsA(node, CachedExpr))
+	{
+		/* no vars in cached expressions */
+		return false;
+	}
 	return expression_tree_walker(node, contain_var_clause_walker, context);
 }
 
@@ -412,6 +432,11 @@ contain_vars_of_level_walker(Node *node, int *sublevels_up)
 		(*sublevels_up)--;
 		return result;
 	}
+	if (IsA(node, CachedExpr))
+	{
+		/* no vars in cached expressions */
+		return false;
+	}
 	return expression_tree_walker(node,
 								  contain_vars_of_level_walker,
 								  (void *) sublevels_up);
@@ -486,6 +511,11 @@ locate_var_of_level_walker(Node *node,
 		context->sublevels_up--;
 		return result;
 	}
+	if (IsA(node, CachedExpr))
+	{
+		/* no vars in cached expressions */
+		return false;
+	}
 	return expression_tree_walker(node,
 								  locate_var_of_level_walker,
 								  (void *) context);
@@ -636,6 +666,11 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
 		else
 			elog(ERROR, "PlaceHolderVar found where not expected");
 	}
+	if (IsA(node, CachedExpr))
+	{
+		/* no vars in cached expressions */
+		return false;
+	}
 	return expression_tree_walker(node, pull_var_clause_walker,
 								  (void *) context);
 }
@@ -807,6 +842,11 @@ flatten_join_alias_vars_mutator(Node *node,
 		context->sublevels_up--;
 		return (Node *) newnode;
 	}
+	if (IsA(node, CachedExpr))
+	{
+		/* no vars in cached expressions */
+		return false;
+	}
 	/* Already-planned tree not supported */
 	Assert(!IsA(node, SubPlan));
 	/* Shouldn't need to handle these planner auxiliary nodes here */
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index ddc3ec8..a20d5a9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1794,7 +1794,7 @@ 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, params, false, NULL, false);
 
 	/*
 	 * Now we can define the portal.
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 0cbdbe5..cdeda0b 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3196,6 +3196,19 @@ array_map(Datum arrayd,
 	array_iter_setup(&iter, v);
 	hasnulls = false;
 
+	/* we must have our own information for executing cached expressions */
+	Assert(exprstate->own_execute_cached_expressions);
+	/* for the first element get it from the top */
+	if (exprstate->top_execute_cached_expressions)
+	{
+		*(exprstate->own_execute_cached_expressions) =
+			*(exprstate->top_execute_cached_expressions);
+	}
+	else
+	{
+		/* We are top, so all is OK */
+	}
+
 	for (i = 0; i < nitems; i++)
 	{
 		/* Get source element, checking for NULL */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c5f5a1c..9779efb 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7642,6 +7642,19 @@ get_rule_expr(Node *node, deparse_context *context,
 			get_const_expr((Const *) node, context, 0);
 			break;
 
+		case T_CachedExpr:
+			{
+				CachedExpr *cachedexpr = (CachedExpr *) node;
+
+				/*
+				 * Since the cached expression is created only for internal use,
+				 * forget about it and deparse its subexpression.
+				 */
+				get_rule_expr((Node *) cachedexpr->subexpr, context,
+							  showimplicit);
+			}
+			break;
+
 		case T_Param:
 			get_parameter((Param *) node, context);
 			break;
@@ -8867,6 +8880,8 @@ looks_like_function(Node *node)
 		case T_XmlExpr:
 			/* these are all accepted by func_expr_common_subexpr */
 			return true;
+		case T_CachedExpr:
+			return looks_like_function((Node *) ((CachedExpr *) node)->subexpr);
 		default:
 			break;
 	}
@@ -9872,9 +9887,11 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 					foreach(lc, rte->functions)
 					{
 						RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
+						FuncExpr   *funcexpr =
+							cast_node_if_cached(rtfunc->funcexpr, FuncExpr);
 
-						if (!IsA(rtfunc->funcexpr, FuncExpr) ||
-							((FuncExpr *) rtfunc->funcexpr)->funcid != F_ARRAY_UNNEST ||
+						if (funcexpr == NULL ||
+							funcexpr->funcid != F_ARRAY_UNNEST ||
 							rtfunc->funccolnames != NIL)
 						{
 							all_unnest = false;
@@ -9889,7 +9906,9 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 						foreach(lc, rte->functions)
 						{
 							RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
-							List	   *args = ((FuncExpr *) rtfunc->funcexpr)->args;
+							FuncExpr   *funcexpr =
+								cast_node_if_cached(rtfunc->funcexpr, FuncExpr);
+							List	   *args = funcexpr->args;
 
 							allargs = list_concat(allargs, list_copy(args));
 						}
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 8d7d8e0..7282413 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -80,6 +80,15 @@
 	 IsA((plansource)->raw_parse_tree->stmt, TransactionStmt))
 
 /*
+ * This flag is used to reuse the already created generic plan with
+ * precalculated bound parameters: we need it to check if the parameter was
+ * precalculated and will be precalculated because the flag PARAM_FLAG_CONST is
+ * replaced by the flag PARAM_FLAG_PRECALCULATED in the generic plan.
+ */
+#define PARAM_FLAG_ALWAYS_PRECALCULATED ( PARAM_FLAG_CONST | \
+										  PARAM_FLAG_PRECALCULATED )
+
+/*
  * This is the head of the backend's list of "saved" CachedPlanSources (i.e.,
  * those that are in long-lived storage and are examined for sinval events).
  * We thread the structs manually instead of using List cells so that we can
@@ -90,9 +99,11 @@ 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,
+							ParamListInfo boundParams);
 static CachedPlan *BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
-				ParamListInfo boundParams, QueryEnvironment *queryEnv);
+				ParamListInfo boundParams, QueryEnvironment *queryEnv,
+				bool is_generic);
 static bool choose_custom_plan(CachedPlanSource *plansource,
 				   ParamListInfo boundParams);
 static double cached_plan_cost(CachedPlan *plan, bool include_planner);
@@ -105,6 +116,12 @@ static TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
 static void PlanCacheRelCallback(Datum arg, Oid relid);
 static void PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue);
 static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue);
+static ParamExternData *GetParamExternData(ParamListInfo boundParams,
+										   int paramid,
+										   ParamExternData *workspace);
+static bool IsParamValid(const ParamExternData *prm);
+static bool CheckBoundParams(ParamListInfo firstBoundParams,
+							 ParamListInfo secondBoundParams);
 
 
 /*
@@ -794,7 +811,7 @@ RevalidateCachedQuery(CachedPlanSource *plansource,
  * (We must do this for the "true" result to be race-condition-free.)
  */
 static bool
-CheckCachedPlan(CachedPlanSource *plansource)
+CheckCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams)
 {
 	CachedPlan *plan = plansource->gplan;
 
@@ -845,8 +862,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. */
@@ -867,9 +886,13 @@ CheckCachedPlan(CachedPlanSource *plansource)
  * qlist should be the result value from a previous RevalidateCachedQuery,
  * or it can be set to NIL if we need to re-copy the plansource's query_list.
  *
- * To build a generic, parameter-value-independent plan, pass NULL for
- * boundParams.  To build a custom plan, pass the actual parameter values via
- * boundParams.  For best effect, the PARAM_FLAG_CONST flag should be set on
+ * To build a generic, absolutely parameter-value-independent plan, pass NULL
+ * for boundParams.  To build a generic, parameter-value-independent plan with
+ * CachedExpr nodes for constant parameters, pass the actual parameter values
+ * via boundParams and set genericPlanPrecalculateConstBoundParams to true.  To
+ * build a custom plan, pass the actual parameter values via boundParams and set
+ * genericPlanPrecalculateConstBoundParams to false.
+ * For best effect, the PARAM_FLAG_CONST flag should be set on
  * each parameter value; otherwise the planner will treat the value as a
  * hint rather than a hard constant.
  *
@@ -879,7 +902,8 @@ CheckCachedPlan(CachedPlanSource *plansource)
  */
 static CachedPlan *
 BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
-				ParamListInfo boundParams, QueryEnvironment *queryEnv)
+				ParamListInfo boundParams, QueryEnvironment *queryEnv,
+				bool genericPlanPrecalculateConstBoundParams)
 {
 	CachedPlan *plan;
 	List	   *plist;
@@ -888,6 +912,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 	MemoryContext plan_context;
 	MemoryContext oldcxt = CurrentMemoryContext;
 	ListCell   *lc;
+	ParamListInfo planBoundParams;
 
 	/*
 	 * Normally the querytree should be valid already, but if it's not,
@@ -931,10 +956,72 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 		snapshot_set = true;
 	}
 
+	/* Specify boundParams for the planner */
+	if (boundParams == NULL)
+	{
+		/* Absolutely parameter-value-independent generic plan */
+		planBoundParams = NULL;
+	}
+	else if (!genericPlanPrecalculateConstBoundParams)
+	{
+		/* Custom plan */
+		planBoundParams = boundParams;
+	}
+	else
+	{
+		/*
+		 * Parameter-value-independent generic plan with precalculated
+		 * parameters.
+		 */
+
+		Size		size = offsetof(ParamListInfoData, params) +
+			boundParams->numParams * sizeof(ParamExternData);
+
+		planBoundParams = (ParamListInfo) palloc(size);
+		memcpy(planBoundParams, boundParams, size);
+
+		/*
+		 * The generic plan should know as little as possible about the
+		 * parameters, and ideally we should pass boundParams as NULL. But
+		 * on ther other hand we need to know whether they are constant or
+		 * not, so that we can insert the CachedExpr nodes into the plan
+		 * where possible.
+		 *
+		 * Therefore let's put PARAM_FLAG_PRECALCULATED instead of
+		 * PARAM_FLAG_CONST for all parameters (for example, to prevent the
+		 * creation of Const nodes instead of them).
+		 */
+		if (planBoundParams->paramFetch)
+		{
+			/*
+			 * Use the same fetch function, but put PARAM_FLAG_PRECALCULATED
+			 * instead of PARAM_FLAG_CONST after its call.
+			 */
+			planBoundParams->paramFetchArg =
+				(void *) planBoundParams->paramFetch;
+			planBoundParams->paramFetch = ParamFetchPrecalculated;
+		}
+		else
+		{
+			int			index;
+
+			for (index = 0; index < planBoundParams->numParams; ++index)
+			{
+				ParamExternData *prm = &planBoundParams->params[index];
+
+				if (OidIsValid(prm->ptype) && (prm->pflags & PARAM_FLAG_CONST))
+				{
+					prm->pflags &= ~PARAM_FLAG_CONST;
+					prm->pflags |= PARAM_FLAG_PRECALCULATED;
+				}
+			}
+		}
+	}
+
 	/*
 	 * Generate the plan.
 	 */
-	plist = pg_plan_queries(qlist, plansource->cursor_options, boundParams);
+	plist = pg_plan_queries(qlist, plansource->cursor_options, planBoundParams);
 
 	/* Release snapshot if we got one */
 	if (snapshot_set)
@@ -1002,6 +1089,30 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 	plan->is_saved = false;
 	plan->is_valid = true;
 
+	/*
+	 * Set the precalculation parameters of the generic plan. Copy them into the
+	 * new context if the plan is not one-shot.
+	 */
+	if (planBoundParams != NULL && genericPlanPrecalculateConstBoundParams)
+	{
+		if (!plansource->is_oneshot)
+		{
+			Size		size = offsetof(ParamListInfoData, params) +
+				planBoundParams->numParams * sizeof(ParamExternData);
+
+			plan->boundParams = (ParamListInfo) palloc(size);
+			memcpy(plan->boundParams, planBoundParams, size);
+		}
+		else
+		{
+			plan->boundParams = planBoundParams;
+		}
+	}
+	else
+	{
+		plan->boundParams = NULL;
+	}
+
 	/* assign generation number to new plan */
 	plan->generation = ++(plansource->generation);
 
@@ -1130,14 +1241,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/precalculated for the
+ * previous generic plan and is not constant/precalculated now or vice versa.
  */
 CachedPlan *
 GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
-			  bool useResOwner, QueryEnvironment *queryEnv)
+			  bool useResOwner, QueryEnvironment *queryEnv,
+			  bool genericPlanPrecalculateConstBoundParams)
 {
 	CachedPlan *plan = NULL;
 	List	   *qlist;
 	bool		customplan;
+	ParamListInfo genericPlanBoundParams;
 
 	/* Assert caller is doing things in a sane order */
 	Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
@@ -1154,7 +1273,13 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
 
 	if (!customplan)
 	{
-		if (CheckCachedPlan(plansource))
+		/* set the parameters for the generic plan */
+		if (genericPlanPrecalculateConstBoundParams)
+			genericPlanBoundParams = 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 +1288,8 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
 		else
 		{
 			/* Build a new generic plan */
-			plan = BuildCachedPlan(plansource, qlist, NULL, queryEnv);
+			plan = BuildCachedPlan(plansource, qlist, genericPlanBoundParams,
+								   queryEnv, true);
 			/* Just make real sure plansource->gplan is clear */
 			ReleaseGenericPlan(plansource);
 			/* Link the new generic plan into the plansource */
@@ -1208,7 +1334,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
 	if (customplan)
 	{
 		/* Build a custom plan */
-		plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv);
+		plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv, false);
 		/* Accumulate total costs of custom plans, but 'ware overflow */
 		if (plansource->num_custom_plans < INT_MAX)
 		{
@@ -1903,3 +2029,144 @@ ResetPlanCache(void)
 		}
 	}
 }
+
+/*
+ * ParamFetchPrecalculated
+ *		Fetch of parameters in generic plans. Use the same fetch function as in
+ *		the custom plan, but after its call put PARAM_FLAG_PRECALCULATED instead
+ *		of PARAM_FLAG_CONST to keep the plan as generic and prevent, for
+ *		example, the creation of a Const node for this parameter.
+ */
+ParamExternData *
+ParamFetchPrecalculated(ParamListInfo params, int paramid, bool speculative,
+						ParamExternData *workspace)
+{
+	/* Fetch back the hook data */
+	ParamFetchHook paramFetch = (ParamFetchHook) params->paramFetchArg;
+	ParamExternData *prm;
+
+	Assert(paramFetch != NULL);
+
+	prm = (*paramFetch) (params, paramid, speculative, workspace);
+	Assert(prm);
+
+	if (OidIsValid(prm->ptype) && (prm->pflags & PARAM_FLAG_CONST))
+	{
+		prm->pflags &= ~PARAM_FLAG_CONST;
+		prm->pflags |= PARAM_FLAG_PRECALCULATED;
+	}
+
+	return prm;
+}
+
+/*
+ * GetParamExternData: get ParamExternData with this paramid from ParamListInfo.
+ *
+ * If the parameter is dynamic, use speculative fetching, so it should avoid
+ * erroring out if parameter is unavailable.
+ */
+static ParamExternData *
+GetParamExternData(ParamListInfo boundParams, int paramid,
+				   ParamExternData *workspace)
+{
+	if (boundParams == NULL)
+		return NULL;
+
+	/*
+	 * Give hook a chance in case parameter is dynamic.  Tell it that this fetch
+	 * is speculative, so it should avoid erroring out if parameter is
+	 * unavailable.
+	 */
+	if (boundParams->paramFetch != NULL)
+		return boundParams->paramFetch(boundParams, paramid, true, workspace);
+
+	return &boundParams->params[paramid - 1];
+}
+
+/*
+ * IsParamValid: return true if prm is not NULL and its ptype is valid.
+ */
+static bool
+IsParamValid(const ParamExternData *prm)
+{
+	return prm && OidIsValid(prm->ptype);
+}
+
+/*
+ * CheckBoundParams
+ *		Check if bound params are compatible in the generic plan:
+ *		1) Check that the parameters with the same paramid are equal in terms of
+ *		   the CachedExpr node: both are constants/precalculated so they have
+ *		   previously been precalculated and will be precalculated, or both are
+ *		   not.
+ *		2) Check that the other parameters are not constants or precalculated,
+ *		   so they have not previously been precalculated and will not be
+ *		   precalculated.
+ */
+static bool
+CheckBoundParams(ParamListInfo firstBoundParams,
+				 ParamListInfo secondBoundParams)
+{
+	int			maxNumParams = 0,
+				paramid;
+	ParamExternData *first_prm,
+					*second_prm;
+	ParamExternData first_prmdata,
+					second_prmdata;
+
+	/* Get the maximum number of parameters to check */
+	if (firstBoundParams && firstBoundParams->numParams > maxNumParams)
+		maxNumParams = firstBoundParams->numParams;
+	if (secondBoundParams && secondBoundParams->numParams > maxNumParams)
+		maxNumParams = secondBoundParams->numParams;
+
+	/*
+	 * If there're parameters with the same paramid, check that they are equal
+	 * in terms of the CachedExpr node: both are constants/precalculated so they
+	 * have previously been precalculated and will be precalculated, or both are
+	 * not.
+	 *
+	 * Check that the other parameters are not constants or precalculated, so
+	 * they have not previously been precalculated and will not be
+	 * precalculated.
+	 */
+	for (paramid = 1; paramid <= maxNumParams; ++paramid)
+	{
+		first_prm = GetParamExternData(firstBoundParams, paramid,
+									   &first_prmdata);
+		second_prm = GetParamExternData(secondBoundParams, paramid,
+										&second_prmdata);
+
+		if (IsParamValid(first_prm) && IsParamValid(second_prm))
+		{
+			/*
+			 * Check that both are constants/precalculated or both are not.
+			 */
+			if ((first_prm->pflags & PARAM_FLAG_ALWAYS_PRECALCULATED) !=
+				(second_prm->pflags & PARAM_FLAG_ALWAYS_PRECALCULATED))
+				return false;
+		}
+		else if (IsParamValid(first_prm))
+		{
+			/*
+			 * The second parameter with this paramid is not
+			 * constant/precalculated, so check that the first one is also not
+			 * constant/precalculated.
+			 */
+			if (first_prm->pflags & PARAM_FLAG_ALWAYS_PRECALCULATED)
+				return false;
+		}
+		else if (IsParamValid(second_prm))
+		{
+			/*
+			 * The first parameter with this paramid is not
+			 * constant/precalculated, so check that the second one is also not
+			 * constant/precalculated.
+			 */
+			if (second_prm->pflags & PARAM_FLAG_ALWAYS_PRECALCULATED)
+				return false;
+		}
+	}
+
+	return true;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index c0076bf..8968a1c 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -229,15 +229,17 @@ get_expr_result_type(Node *expr,
 					 TupleDesc *resultTupleDesc)
 {
 	TypeFuncClass result;
+	FuncExpr   *funcexpr = cast_node_if_cached(expr, FuncExpr);
+	OpExpr	   *opexpr = cast_node_if_cached(expr, OpExpr);
 
-	if (expr && IsA(expr, FuncExpr))
-		result = internal_get_result_type(((FuncExpr *) expr)->funcid,
+	if (funcexpr)
+		result = internal_get_result_type(funcexpr->funcid,
 										  expr,
 										  NULL,
 										  resultTypeId,
 										  resultTupleDesc);
-	else if (expr && IsA(expr, OpExpr))
-		result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno),
+	else if (opexpr)
+		result = internal_get_result_type(get_opcode(opexpr->opno),
 										  expr,
 										  NULL,
 										  resultTypeId,
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 117fc89..f895fe9 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -230,6 +230,16 @@ typedef enum ExprEvalOp
 	EEOP_AGG_ORDERED_TRANS_DATUM,
 	EEOP_AGG_ORDERED_TRANS_TUPLE,
 
+	/*
+	 * 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;
@@ -634,6 +644,13 @@ typedef struct ExprEvalStep
 			int			transno;
 			int			setoff;
 		}			agg_trans;
+
+		/* for EEOP_CACHEDEXPR_* */
+		struct
+		{
+			/* steps for evaluation the same CachedExpr have the same state */
+			struct CachedExprState *state;
+		}			cachedexpr;
 	}			d;
 } ExprEvalStep;
 
@@ -674,6 +691,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		resnull;
+	Datum		resvalue;
+	bool		isExecuted;		/* in case upper expression jumps over it */
+	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/executor/executor.h b/src/include/executor/executor.h
index 1d824ef..403b78c 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -285,7 +285,14 @@ ExecEvalExpr(ExprState *state,
 			 ExprContext *econtext,
 			 bool *isNull)
 {
-	return state->evalfunc(state, econtext, isNull);
+	Datum		retDatum;
+
+	retDatum = state->evalfunc(state, econtext, isNull);
+	/* update the infomation about cached expressions */
+	if (state->own_execute_cached_expressions)
+		*(state->own_execute_cached_expressions) = false;
+
+	return retDatum;
 }
 #endif
 
@@ -304,7 +311,12 @@ ExecEvalExprSwitchContext(ExprState *state,
 	MemoryContext oldContext;
 
 	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
 	retDatum = state->evalfunc(state, econtext, isNull);
+	/* update the infomation about cached expressions */
+	if (state->own_execute_cached_expressions)
+		*(state->own_execute_cached_expressions) = false;
+
 	MemoryContextSwitchTo(oldContext);
 	return retDatum;
 }
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1bf6745..370c759 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -105,6 +105,25 @@ typedef struct ExprState
 
 	Datum	   *innermost_domainval;
 	bool	   *innermost_domainnull;
+
+	/*
+	 * Usually cached expressions are executed only once and then they use
+	 * cached value until the end of execution. But sometimes the executor is
+	 * used again from the very beginning (for example, for PL/pgSQL simple
+	 * expressions), so the cached expressions must be recalculated in this
+	 * case.
+	 *
+	 * If we do not have an upper state, top_execute_cached_expressions is NULL
+	 * and we use the information from own_execute_cached_expressions.
+	 *
+	 * If not upper states use the information from the upper expression, their
+	 * own_execute_cached_expressions are NULLs. But sometimes we have have
+	 * several own runs (for example, an executor state for the array
+	 * per-element expression) and therefore we need to separate this
+	 * information from the runs of the upper state.
+	 */
+	bool	   *own_execute_cached_expressions;
+	bool	   *top_execute_cached_expressions;
 } ExprState;
 
 
diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h
index 849f34d..2787beb 100644
--- a/src/include/nodes/nodeFuncs.h
+++ b/src/include/nodes/nodeFuncs.h
@@ -77,4 +77,12 @@ struct PlanState;
 extern bool planstate_tree_walker(struct PlanState *planstate, bool (*walker) (),
 								  void *context);
 
+extern Node *cast_node_if_cached_impl(Node *node, NodeTag tag);
+
+/*
+ * A more convenient macro for using the function cast_node_if_cached_impl.
+ */
+#define cast_node_if_cached(node, _type_) \
+	(_type_ *) cast_node_if_cached_impl((Node *) (node), T_##_type_)
+
 #endif							/* NODEFUNCS_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 74b094a..fd643a8 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -145,6 +145,8 @@ typedef enum NodeTag
 	T_Expr,
 	T_Var,
 	T_Const,
+	T_CacheableExpr,
+	T_CachedExpr,
 	T_Param,
 	T_Aggref,
 	T_GroupingFunc,
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index 04b03c7..eee6b5c 100644
--- a/src/include/nodes/params.h
+++ b/src/include/nodes/params.h
@@ -37,10 +37,15 @@ struct ParseState;
  *	  Although parameter numbers are normally consecutive, we allow
  *	  ptype == InvalidOid to signal an unused array entry.
  *
- *	  pflags is a flags field.  Currently the only used bit is:
+ *	  pflags is a flags field.  Currently used bits are:
+ *
  *	  PARAM_FLAG_CONST signals the planner that it may treat this parameter
- *	  as a constant (i.e., generate a plan that works only for this value
- *	  of the parameter).
+ *			as a constant (i.e., generate a plan that works only for this value
+ *			of the parameter).
+ *
+ *	  PARAM_FLAG_PRECALCULATED signals the planner that it cannot be treated as
+ *			a pure constant, such as PARAM_FLAG_CONST. But it can be used as an
+ *			argument to the CachedExpr node.
  *
  *	  In the dynamic approach, all access to parameter values is done through
  *	  hook functions found in the ParamListInfo struct.  In this case,
@@ -85,7 +90,9 @@ struct ParseState;
  *	  and paramCompileArg is rather arbitrary.
  */
 
-#define PARAM_FLAG_CONST	0x0001	/* parameter is constant */
+#define PARAM_FLAG_CONST			0x0001	/* parameter is constant */
+#define PARAM_FLAG_PRECALCULATED	0x0002	/* parameter is precalculated: it is
+											 * cached in the generic plan */
 
 typedef struct ParamExternData
 {
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d7..3ef16ab 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -204,6 +204,38 @@ typedef struct Const
 } Const;
 
 /*
+ * 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 cached 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;
+
+/*
  * Param
  *
  *		paramkind specifies the kind of parameter. The possible values
@@ -240,7 +272,7 @@ typedef enum ParamKind
 
 typedef struct Param
 {
-	Expr		xpr;
+	CacheableExpr xpr;
 	ParamKind	paramkind;		/* kind of parameter. See above */
 	int			paramid;		/* numeric ID for parameter */
 	Oid			paramtype;		/* pg_type OID of parameter's datatype */
@@ -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 */
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index ab20aa0..0743048 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.
+	 */
+	ParamListInfo 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 */
@@ -179,7 +186,11 @@ extern List *CachedPlanGetTargetList(CachedPlanSource *plansource,
 extern CachedPlan *GetCachedPlan(CachedPlanSource *plansource,
 			  ParamListInfo boundParams,
 			  bool useResOwner,
-			  QueryEnvironment *queryEnv);
+			  QueryEnvironment *queryEnv,
+			  bool genericPlanPrecalculateConstBoundParams);
 extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
+extern ParamExternData *ParamFetchPrecalculated(ParamListInfo params,
+												int paramid, bool speculative,
+												ParamExternData *workspace);
 
 #endif							/* PLANCACHE_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 4478c53..e7f0f33 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -5530,6 +5530,13 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
 	expr->expr_simple_in_use = true;
 
 	/*
+	 * The executor is used (again) from the very beginning, so the cached
+	 * expressions must be (re)calculated.
+	 */
+	Assert(expr->expr_simple_state->own_execute_cached_expressions);
+	*(expr->expr_simple_state->own_execute_cached_expressions) = true;
+
+	/*
 	 * Finally we can call the executor to evaluate the expression
 	 */
 	*result = ExecEvalExpr(expr->expr_simple_state,
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..2beee2e
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions.out
@@ -0,0 +1,6194 @@
+--
+-- 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
+);
+-- 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)
+
+-- 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)
+
+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 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;
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..9ff2530
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions_1.out
@@ -0,0 +1,5840 @@
+--
+-- 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
+);
+-- 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)
+
+-- 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)
+
+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 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;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index ad9434f..1c749d8 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 indexing
+test: identity partition_join partition_prune reloptions hash_part indexing 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 27cd498..92044dc 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -185,5 +185,6 @@ test: partition_prune
 test: reloptions
 test: hash_part
 test: indexing
+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..44fcacb
--- /dev/null
+++ b/src/test/regress/sql/precalculate_stable_functions.sql
@@ -0,0 +1,1946 @@
+--
+-- 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
+);
+
+-- 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;
+
+-- 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;
+
+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 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;
-- 
2.7.4

