Here's v2 of the patches. Changes from v1:

And here there's v3 of planning and execution: common executor steps for all types of cached expression.

--
Marina Polyakova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
From 5e89221251670526eb2b5750868ac73eee48f10b Mon Sep 17 00:00:00 2001
From: Marina Polyakova <m.polyak...@postgrespro.ru>
Date: Mon, 15 May 2017 15:31:21 +0300
Subject: [PATCH 2/3] Precalculate stable functions, planning and execution v3

Now in Postgresql only immutable functions are precalculated; stable functions
are calculated for every row so in fact they don't differ from volatile
functions.

This patch includes:
- replacement nonvolatile functions and operators by appropriate cached
expressions
- planning and execution cached expressions
- regression tests
---
 src/backend/executor/execExpr.c                    |   37 +
 src/backend/executor/execExprInterp.c              |   37 +
 src/backend/optimizer/path/allpaths.c              |    9 +-
 src/backend/optimizer/path/clausesel.c             |   13 +
 src/backend/optimizer/plan/planagg.c               |    1 +
 src/backend/optimizer/plan/planner.c               |   28 +
 src/backend/optimizer/util/clauses.c               |   55 +
 src/backend/utils/adt/ruleutils.c                  |    5 +
 src/include/executor/execExpr.h                    |   19 +
 src/include/optimizer/planner.h                    |    3 +
 src/include/optimizer/tlist.h                      |    8 +-
 src/pl/plpgsql/src/pl_exec.c                       |   10 +
 .../expected/precalculate_stable_functions.out     | 2625 ++++++++++++++++++++
 src/test/regress/serial_schedule                   |    1 +
 .../regress/sql/precalculate_stable_functions.sql  |  949 +++++++
 15 files changed, 3797 insertions(+), 3 deletions(-)
 create mode 100644 src/test/regress/expected/precalculate_stable_functions.out
 create mode 100644 src/test/regress/sql/precalculate_stable_functions.sql

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 5a34a46..3c2068d 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -865,6 +865,43 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
 				break;
 			}
 
+		case T_CachedExpr:
+			{
+				int 		adjust_jump;
+
+				/*
+				 * Allocate and fill scratch memory used by all steps of
+				 * CachedExpr evaluation.
+				 */
+				scratch.d.cachedexpr.isExecuted = (bool *) palloc(sizeof(bool));
+				scratch.d.cachedexpr.resnull = (bool *) palloc(sizeof(bool));
+				scratch.d.cachedexpr.resvalue = (Datum *) palloc(sizeof(Datum));
+
+				*scratch.d.cachedexpr.isExecuted = false;
+				*scratch.d.cachedexpr.resnull = false;
+				*scratch.d.cachedexpr.resvalue = (Datum) 0;
+				scratch.d.cachedexpr.jumpdone = -1;
+
+				/* add EEOP_CACHEDEXPR_IF_CACHED step */
+				scratch.opcode = EEOP_CACHEDEXPR_IF_CACHED;
+				ExprEvalPushStep(state, &scratch);
+				adjust_jump = state->steps_len - 1;
+
+				/* add subexpression steps */
+				ExecInitExprRec((Expr *) get_subexpr((CachedExpr *) node),
+								parent, state, resv, resnull);
+
+				/* add EEOP_CACHEDEXPR_SUBEXPR_END step */
+				scratch.opcode = EEOP_CACHEDEXPR_SUBEXPR_END;
+				ExprEvalPushStep(state, &scratch);
+
+				/* adjust jump target */
+				state->steps[adjust_jump].d.cachedexpr.jumpdone =
+					state->steps_len;
+
+				break;
+			}
+
 		case T_ScalarArrayOpExpr:
 			{
 				ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index fed0052..ac7b7f8 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -279,6 +279,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 	TupleTableSlot *innerslot;
 	TupleTableSlot *outerslot;
 	TupleTableSlot *scanslot;
+	MemoryContext oldContext;	/* for EEOP_CACHEDEXPR_* */
 
 	/*
 	 * This array has to be in the same order as enum ExprEvalOp.
@@ -309,6 +310,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_FUNCEXPR_STRICT,
 		&&CASE_EEOP_FUNCEXPR_FUSAGE,
 		&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEOP_CACHEDEXPR_IF_CACHED,
+		&&CASE_EEOP_CACHEDEXPR_SUBEXPR_END,
 		&&CASE_EEOP_BOOL_AND_STEP_FIRST,
 		&&CASE_EEOP_BOOL_AND_STEP,
 		&&CASE_EEOP_BOOL_AND_STEP_LAST,
@@ -721,6 +724,40 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		EEO_CASE(EEOP_CACHEDEXPR_IF_CACHED)
+		{
+			if (*op->d.cachedexpr.isExecuted)
+			{
+				/* use saved result and skip subexpression evaluation */
+				*op->resnull = *op->d.cachedexpr.resnull;
+				if (!(*op->resnull))
+					*op->resvalue = *op->d.cachedexpr.resvalue;
+
+				EEO_JUMP(op->d.cachedexpr.jumpdone);
+			}
+
+			/*
+			 * Switch per-query memory context for subexpression evaluation.
+			 * It is necessary to save result between all tuples.
+			 */
+			oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+			EEO_NEXT();
+		}
+
+		EEO_CASE(EEOP_CACHEDEXPR_SUBEXPR_END)
+		{
+			/* save result and switch memory context back */
+			*op->d.cachedexpr.resnull = *op->resnull;
+			if (!(*op->resnull))
+				*op->d.cachedexpr.resvalue = *op->resvalue;
+			*op->d.cachedexpr.isExecuted = true;
+
+			MemoryContextSwitchTo(oldContext);
+
+			EEO_NEXT();
+		}
+
 		/*
 		 * If any of its clauses is FALSE, an AND's result is FALSE regardless
 		 * of the states of the rest of the clauses, so we can stop evaluating
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index b93b4fc..a322255 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -378,7 +378,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
 				set_subquery_pathlist(root, rel, rti, rte);
 				break;
 			case RTE_FUNCTION:
-				set_function_size_estimates(root, rel);
+				{
+					rel->baserestrictinfo = replace_qual_cached_expressions(
+						rel->baserestrictinfo);
+					set_function_size_estimates(root, rel);
+				}
 				break;
 			case RTE_TABLEFUNC:
 				set_tablefunc_size_estimates(root, rel);
@@ -517,6 +521,9 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	 */
 	check_index_predicates(root, rel);
 
+	rel->baserestrictinfo = replace_qual_cached_expressions(
+		rel->baserestrictinfo);
+
 	/* Mark rel with estimated output rows, width, etc */
 	set_baserel_size_estimates(root, rel);
 }
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 758ddea..fc799f1 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
@@ -825,6 +826,18 @@ clause_selectivity(PlannerInfo *root,
 								jointype,
 								sjinfo);
 	}
+	else if (IsA(clause, CachedExpr))
+	{
+		/*
+		 * Not sure this case is needed, but it can't hurt.
+		 * Calculate selectivity of subexpression.
+		 */
+		s1 = clause_selectivity(root,
+								get_subexpr((CachedExpr *) clause),
+								varRelid,
+								jointype,
+								sjinfo);
+	}
 	else
 	{
 		/*
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 5565736..7a28764 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -38,6 +38,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/subselect.h"
 #include "optimizer/tlist.h"
 #include "parser/parsetree.h"
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 552b73d..7c68d6d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6088,6 +6088,34 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
 	return result;
 }
 
+/*
+ * replace_pathtarget_cached_expressions
+ *		Replace cached expresisons in a PathTarget tlist.
+ *
+ * As a notational convenience, returns the same PathTarget pointer passed in.
+ */
+PathTarget *
+replace_pathtarget_cached_expressions(PathTarget *target)
+{
+	target->exprs = (List *) replace_cached_expressions_mutator(
+		(Node *) target->exprs);
+
+	return target;
+}
+
+/*
+ * replace_qual_cached_expressions
+ *		Replace cacehd expressions in a WHERE clause. The input can be either an
+ *		implicitly-ANDed list of boolean expressions, or a list of RestrictInfo
+ *		nodes.
+ */
+List *
+replace_qual_cached_expressions(List *quals)
+{
+	/* No setup needed for tree walk, so away we go */
+	return (List *) replace_cached_expressions_mutator((Node *) quals);
+}
+
 static Node *
 replace_cached_expressions_mutator(Node *node)
 {
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a1dafc8..0c0284a 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2758,6 +2758,61 @@ eval_const_expressions_mutator(Node *node,
 				newexpr->location = expr->location;
 				return (Node *) newexpr;
 			}
+		case T_CachedExpr:
+			{
+				CachedExpr *cachedexpr = (CachedExpr *) node;
+				Node	   *new_subexpr = eval_const_expressions_mutator(
+					get_subexpr(cachedexpr), context);
+				CachedExpr *new_cachedexpr;
+
+				/*
+				 * If unsafe transformations are used cached expression should
+				 * be always simplified.
+				 */
+				if (context->estimate)
+					Assert(IsA(new_subexpr, Const));
+
+				if (IsA(new_subexpr, Const))
+				{
+					/* successfully simplified it */
+					return new_subexpr;	
+				}
+				else
+				{
+					/*
+					 * The expression cannot be simplified any further, so build
+					 * and return a replacement CachedExpr node using the
+					 * possibly-simplified arguments of subexpression.
+					 */
+					new_cachedexpr = makeNode(CachedExpr);
+					new_cachedexpr->subexprtype = cachedexpr->subexprtype;
+					switch (new_cachedexpr->subexprtype)
+					{
+						case CACHED_FUNCEXPR:
+							new_cachedexpr->subexpr.funcexpr = (FuncExpr *)
+								new_subexpr;
+							break;
+						case CACHED_OPEXPR:
+							new_cachedexpr->subexpr.opexpr = (OpExpr *)
+								new_subexpr;
+							break;
+						case CACHED_DISTINCTEXPR:
+							new_cachedexpr->subexpr.distinctexpr =
+								(DistinctExpr *) new_subexpr;
+							break;
+						case CACHED_NULLIFEXPR:
+							new_cachedexpr->subexpr.nullifexpr = (NullIfExpr *)
+								new_subexpr;
+							break;
+						case CACHED_SCALARARRAYOPEXPR:
+							new_cachedexpr->subexpr.saopexpr =
+								(ScalarArrayOpExpr *) new_subexpr;
+							break;
+					}
+
+					return (Node *) new_cachedexpr;
+				}
+			}
 		case T_BoolExpr:
 			{
 				BoolExpr   *expr = (BoolExpr *) node;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 43b1475..838389d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7720,6 +7720,11 @@ get_rule_expr(Node *node, deparse_context *context,
 			}
 			break;
 
+		case T_CachedExpr:
+			get_rule_expr(get_subexpr((CachedExpr *) node), context,
+						  showimplicit);
+			break;
+
 		case T_ScalarArrayOpExpr:
 			{
 				ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 86fdb33..650c8af 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -86,6 +86,16 @@ typedef enum ExprEvalOp
 	EEOP_FUNCEXPR_STRICT_FUSAGE,
 
 	/*
+	 * Evaluate CachedExpr.  EEOP_CACHEDEXPR_IF_CACHED is used before
+	 * subexpression evaluation (if subexpression was evaluated use cached value
+	 * and jump to next state or get prepared to subexpression evaluation
+	 * otherwise).  EEOP_CACHEDEXPR_SUBEXPR_END is used after subexpression
+	 * evaluation for caching its result.
+	 */
+	EEOP_CACHEDEXPR_IF_CACHED,
+	EEOP_CACHEDEXPR_SUBEXPR_END,
+
+	/*
 	 * Evaluate boolean AND expression, one step per subexpression. FIRST/LAST
 	 * subexpressions are special-cased for performance.  Since AND always has
 	 * at least two subexpressions, FIRST and LAST never apply to the same
@@ -298,6 +308,15 @@ typedef struct ExprEvalStep
 			int			nargs;	/* number of arguments */
 		}			func;
 
+		/* for EEOP_CACHEDEXPR_* */
+		struct
+		{
+			bool	   *isExecuted;
+			bool	   *resnull;
+			Datum	   *resvalue;
+			int			jumpdone;	/* jump here if result determined */
+		}			cachedexpr;
+
 		/* for EEOP_BOOL_*_STEP */
 		struct
 		{
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index f3aaa23..bbadcdd 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -59,4 +59,7 @@ extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid);
 
 extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti);
 
+extern PathTarget *replace_pathtarget_cached_expressions(PathTarget *target);
+extern List *replace_qual_cached_expressions(List *quals);
+
 #endif   /* PLANNER_H */
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index ccb93d8..7488bd2 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -65,8 +65,12 @@ extern void split_pathtarget_at_srfs(PlannerInfo *root,
 						 PathTarget *target, PathTarget *input_target,
 						 List **targets, List **targets_contain_srfs);
 
-/* Convenience macro to get a PathTarget with valid cost/width fields */
+/*
+ * Convenience macro to get a PathTarget with valid cost/width fields and
+ * cached expressions.
+ */
 #define create_pathtarget(root, tlist) \
-	set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist))
+	set_pathtarget_cost_width(root, replace_pathtarget_cached_expressions( \
+		make_pathtarget_from_tlist(tlist)))
 
 #endif   /* TLIST_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 7a40c99..2e27052 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -6535,6 +6535,16 @@ exec_simple_check_node(Node *node)
 				return TRUE;
 			}
 
+		case T_CachedExpr:
+			{
+				/*
+				 * If CachedExpr will not be initialized by ExecInitCachedExpr
+				 * possibly it will use cached value when it shouldn't (for
+				 * example, snapshot has changed), so return false.
+				 */
+				return FALSE;
+			}
+
 		case T_ScalarArrayOpExpr:
 			{
 				ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
diff --git a/src/test/regress/expected/precalculate_stable_functions.out b/src/test/regress/expected/precalculate_stable_functions.out
new file mode 100644
index 0000000..093e6f8
--- /dev/null
+++ b/src/test/regress/expected/precalculate_stable_functions.out
@@ -0,0 +1,2625 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+-- Create types and tables for testing
+CREATE TYPE my_integer AS (value integer);
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+-- Create volatile functions for testing
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'v';
+  RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+  integer,
+  integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal integers volatile';
+  RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'v my_integer';
+  RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+  my_integer,
+  my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal my_integer volatile';
+  RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_vlt_array_int (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'v array_int';
+  RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create stable functions for testing
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's';
+  RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+     integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's2';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+     integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+  RAISE NOTICE 's2 strict';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+  integer,
+  integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal integers stable';
+  RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+  boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's2 boolean';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+  boolean,
+  boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal booleans stable strict';
+  RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's my_integer';
+  RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+  my_integer,
+  my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal my_integer stable';
+  RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_stl_array_int (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's array_int';
+  RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+  RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+  RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+-- Create immutable functions for testing
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+     integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'i2';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+     integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+  RAISE NOTICE 'i2 strict';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+  integer,
+  integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal integers immutable';
+  RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+  my_integer,
+  my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal my_integer immutable';
+  RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+-- Create operators for testing
+CREATE operator === (
+  PROCEDURE = equal_integers_vlt,
+  LEFTARG = integer,
+  RIGHTARG = integer
+);
+CREATE operator ==== (
+  PROCEDURE = equal_integers_stl,
+  LEFTARG = integer,
+  RIGHTARG = integer
+);
+CREATE operator ===== (
+  PROCEDURE = equal_integers_imm,
+  LEFTARG = integer,
+  RIGHTARG = integer
+);
+CREATE operator ====== (
+  PROCEDURE = equal_booleans_stl_strict,
+  LEFTARG = boolean,
+  RIGHTARG = boolean
+);
+CREATE operator ==== (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- Simple functions testing
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+ x_vlt 
+-------
+     1
+     1
+     1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE:  s
+ x_stl 
+-------
+     1
+     1
+     1
+(3 rows)
+
+-- WHERE clause testing
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+ x_vlt 
+-------
+     1
+     1
+     1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE:  s
+NOTICE:  s
+NOTICE:  s
+ x_stl 
+-------
+     1
+     1
+     1
+(3 rows)
+
+-- Functions with constant arguments and nested functions testing
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  s2
+NOTICE:  v
+NOTICE:  s2
+NOTICE:  v
+NOTICE:  s2
+NOTICE:  v
+NOTICE:  s2
+ x_stl2 
+--------
+      1
+      1
+      1
+      1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  i2
+NOTICE:  v
+NOTICE:  i2
+NOTICE:  v
+NOTICE:  i2
+NOTICE:  v
+NOTICE:  i2
+ x_imm2 
+--------
+      1
+      1
+      1
+      1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE:  s2
+NOTICE:  s2
+ x_stl2 
+--------
+      1
+      1
+      1
+      1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE:  s2
+NOTICE:  i2
+ x_imm2 
+--------
+      1
+      1
+      1
+      1
+(4 rows)
+
+-- Strict functions testing
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  s2 strict
+NOTICE:  v
+NOTICE:  s2 strict
+NOTICE:  v
+NOTICE:  s2 strict
+NOTICE:  v
+NOTICE:  s2 strict
+ x_stl2_strict 
+---------------
+             1
+             1
+             1
+             1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  i2 strict
+NOTICE:  v
+NOTICE:  i2 strict
+NOTICE:  v
+NOTICE:  i2 strict
+NOTICE:  v
+NOTICE:  i2 strict
+ x_imm2_strict 
+---------------
+             1
+             1
+             1
+             1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE:  s2 strict
+NOTICE:  s2 strict
+ x_stl2_strict 
+---------------
+             1
+             1
+             1
+             1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE:  s2 strict
+NOTICE:  i2 strict
+ x_imm2_strict 
+---------------
+             1
+             1
+             1
+             1
+(4 rows)
+
+-- Strict functions with null arguments testing
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2
+ x_stl2_strict 
+---------------
+              
+              
+              
+              
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2
+ x_imm2_strict 
+---------------
+              
+              
+              
+              
+(4 rows)
+
+-- Operators testing
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Nested and strict operators testing
+-- (also partly mixed functions and operators testing)
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal booleans stable strict
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal booleans stable strict
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal booleans stable strict
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  equal integers stable
+NOTICE:  s
+NOTICE:  equal integers immutable
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ ?column? 
+----------
+ 
+ 
+ 
+ 
+(4 rows)
+
+-- IS DISTINCT FROM expression testing
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- IS DISTINCT FROM expressions with null arguments testing
+SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+NOTICE:  s2
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+NOTICE:  s2
+NOTICE:  s2
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Nested IS DISTINCT FROM expression testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+  TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+  TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+ nullif 
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+ nullif 
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- NULLIF expressions with null arguments testing
+SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x;
+NOTICE:  s2
+NOTICE:  s2
+ x_stl2 
+--------
+      1
+      1
+      1
+      1
+(4 rows)
+
+SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x;
+NOTICE:  s2
+NOTICE:  s2
+NOTICE:  s2
+ x_stl2 
+--------
+       
+       
+       
+       
+(4 rows)
+
+-- Nested NULLIF expression testing
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE:  v my_integer
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+NOTICE:  v my_integer
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+NOTICE:  v my_integer
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+NOTICE:  v my_integer
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+ nullif 
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE:  s my_integer
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+ nullif 
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_imm,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE:  v my_integer
+NOTICE:  equal my_integer immutable
+NOTICE:  equal my_integer immutable
+NOTICE:  v my_integer
+NOTICE:  equal my_integer immutable
+NOTICE:  equal my_integer immutable
+NOTICE:  v my_integer
+NOTICE:  equal my_integer immutable
+NOTICE:  equal my_integer immutable
+NOTICE:  v my_integer
+NOTICE:  equal my_integer immutable
+NOTICE:  equal my_integer immutable
+ nullif 
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE:  s my_integer
+NOTICE:  equal my_integer immutable
+NOTICE:  equal my_integer immutable
+ nullif 
+--------
+ (1)
+ (1)
+ (1)
+ (1)
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x;
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing (also partly mixed functions and "scalar op ANY/ALL
+-- (array)" / "scalar IN (2 or more values)" expressions testing)
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  equal integers immutable
+NOTICE:  equal integers immutable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed functions and operators testing
+-- (most of it was earlier in Nested and strict operators testing)
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and IS DISTINCT FROM expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+  (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer),
+  (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  s my_integer
+NOTICE:  equal my_integer volatile
+NOTICE:  s my_integer
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+ equal_booleans_stl_strict 
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT equal_booleans_stl_strict(
+  (x_stl() IS DISTINCT FROM 1),
+  (x_stl() IS DISTINCT FROM 2)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  s
+NOTICE:  equal booleans stable strict
+ equal_booleans_stl_strict 
+---------------------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed functions and NULLIF expressions testing
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+  NULLIF(x_stl_my_integer(), '(1)'::my_integer),
+  NULLIF(x_stl_my_integer(), '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  s my_integer
+NOTICE:  equal my_integer volatile
+NOTICE:  s my_integer
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+ equal_my_integer_stl 
+----------------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2))
+FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  s
+NOTICE:  equal integers stable
+ equal_integers_stl 
+--------------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" /
+-- "scalar IN (2 or more values)" expressions testing)
+SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v array_int
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  v array_int
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  v array_int
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  v array_int
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v array_int
+NOTICE:  equal integers stable
+NOTICE:  v array_int
+NOTICE:  equal integers stable
+NOTICE:  v array_int
+NOTICE:  equal integers stable
+NOTICE:  v array_int
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x;
+NOTICE:  s array_int
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x;
+NOTICE:  s array_int
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed operators and IS DISTINCT FROM expressions testing
+-- should not be precalculated
+SELECT (
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+  ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT (
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+  ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed operators and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ 
+ 
+ 
+ 
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x;
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+-- should not be precalculated
+SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed IS DISTINCT FROM and NULLIF expressions testing
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+  NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT NULLIF(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+  ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+ nullif 
+--------
+ t
+ t
+ t
+ t
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+  NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT NULLIF(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+  ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+ nullif 
+--------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or
+-- more values)" expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  (1 === ANY('{2, 3}')) IS DISTINCT FROM
+  (1 === ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+  TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+  (1 ==== ANY('{2, 3}')) IS DISTINCT FROM
+  (1 ==== ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+  TRUE
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ t
+ t
+ t
+ t
+(4 rows)
+
+-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(
+  '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+  TRUE
+))
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+  ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+NOTICE:  equal my_integer volatile
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ 
+ 
+ 
+ 
+(4 rows)
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+SELECT x_stl2_boolean(NULLIF(
+  '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+  TRUE
+))
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+  ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+NOTICE:  equal my_integer stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+-- Tracking functions testing
+SET track_functions TO 'all';
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+ x_vlt 
+-------
+     1
+     1
+     1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 3) x;
+NOTICE:  s
+ x_stl 
+-------
+     1
+     1
+     1
+(3 rows)
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+NOTICE:  v
+ x_vlt 
+-------
+     1
+     1
+     1
+(3 rows)
+
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+NOTICE:  s
+NOTICE:  s
+NOTICE:  s
+ x_stl 
+-------
+     1
+     1
+     1
+(3 rows)
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  s2
+NOTICE:  v
+NOTICE:  s2
+NOTICE:  v
+NOTICE:  s2
+NOTICE:  v
+NOTICE:  s2
+ x_stl2 
+--------
+      1
+      1
+      1
+      1
+(4 rows)
+
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  i2
+NOTICE:  v
+NOTICE:  i2
+NOTICE:  v
+NOTICE:  i2
+NOTICE:  v
+NOTICE:  i2
+ x_imm2 
+--------
+      1
+      1
+      1
+      1
+(4 rows)
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE:  s2
+NOTICE:  s2
+ x_stl2 
+--------
+      1
+      1
+      1
+      1
+(4 rows)
+
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+NOTICE:  s2
+NOTICE:  i2
+ x_imm2 
+--------
+      1
+      1
+      1
+      1
+(4 rows)
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  s2 strict
+NOTICE:  v
+NOTICE:  s2 strict
+NOTICE:  v
+NOTICE:  s2 strict
+NOTICE:  v
+NOTICE:  s2 strict
+ x_stl2_strict 
+---------------
+             1
+             1
+             1
+             1
+(4 rows)
+
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  i2 strict
+NOTICE:  v
+NOTICE:  i2 strict
+NOTICE:  v
+NOTICE:  i2 strict
+NOTICE:  v
+NOTICE:  i2 strict
+ x_imm2_strict 
+---------------
+             1
+             1
+             1
+             1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE:  s2 strict
+NOTICE:  s2 strict
+ x_stl2_strict 
+---------------
+             1
+             1
+             1
+             1
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+NOTICE:  s2 strict
+NOTICE:  i2 strict
+ x_imm2_strict 
+---------------
+             1
+             1
+             1
+             1
+(4 rows)
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2
+ x_stl2_strict 
+---------------
+              
+              
+              
+              
+(4 rows)
+
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+NOTICE:  s2
+ x_imm2_strict 
+---------------
+              
+              
+              
+              
+(4 rows)
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+NOTICE:  equal integers volatile
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+NOTICE:  equal integers immutable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal booleans stable strict
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal booleans stable strict
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal booleans stable strict
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  equal integers stable
+NOTICE:  equal booleans stable strict
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+NOTICE:  s2 boolean
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ 
+ 
+ 
+ 
+(4 rows)
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers stable
+NOTICE:  v
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  v
+NOTICE:  equal integers immutable
+NOTICE:  v
+NOTICE:  equal integers immutable
+ ?column? 
+----------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+NOTICE:  s
+NOTICE:  s
+NOTICE:  equal integers stable
+ ?column? 
+----------
+ t
+ t
+ t
+ t
+(4 rows)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+NOTICE:  equal integers stable
+NOTICE:  s2 boolean
+ x_stl2_boolean 
+----------------
+ f
+ f
+ f
+ f
+(4 rows)
+
+SET track_functions TO DEFAULT;
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+BEGIN;
+SELECT simple();
+ simple 
+--------
+      2
+(1 row)
+
+INSERT INTO two VALUES (3);
+SELECT simple();
+ simple 
+--------
+      3
+(1 row)
+
+ROLLBACK;
+-- Drop tables for testing
+DROP TABLE two;
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 04206c3..f2710b9 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -179,3 +179,4 @@ test: with
 test: xml
 test: event_trigger
 test: stats
+test: precalculate_stable_functions
diff --git a/src/test/regress/sql/precalculate_stable_functions.sql b/src/test/regress/sql/precalculate_stable_functions.sql
new file mode 100644
index 0000000..a59791d
--- /dev/null
+++ b/src/test/regress/sql/precalculate_stable_functions.sql
@@ -0,0 +1,949 @@
+--
+-- PRECALCULATE STABLE FUNCTIONS
+--
+
+-- Create types and tables for testing
+
+CREATE TYPE my_integer AS (value integer);
+
+CREATE TABLE two (i integer);
+INSERT INTO two VALUES (1), (2);
+
+-- Create volatile functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_vlt (
+)
+RETURNS integer VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'v';
+  RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_vlt (
+  integer,
+  integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal integers volatile';
+  RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_my_integer (
+)
+RETURNS my_integer VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'v my_integer';
+  RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_vlt (
+  my_integer,
+  my_integer
+)
+RETURNS boolean VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal my_integer volatile';
+  RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_vlt_array_int (
+)
+RETURNS int[] VOLATILE AS
+$body$
+BEGIN
+  RAISE NOTICE 'v array_int';
+  RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create stable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_stl (
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's';
+  RETURN 1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2 (
+     integer
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's2';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_strict (
+     integer
+)
+RETURNS integer STABLE STRICT AS
+$body$
+BEGIN
+  RAISE NOTICE 's2 strict';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_stl (
+  integer,
+  integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal integers stable';
+  RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl2_boolean (
+  boolean
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's2 boolean';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_booleans_stl_strict (
+  boolean,
+  boolean
+)
+RETURNS boolean STABLE STRICT AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal booleans stable strict';
+  RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_my_integer (
+)
+RETURNS my_integer STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's my_integer';
+  RETURN '(1)'::my_integer;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_stl (
+  my_integer,
+  my_integer
+)
+RETURNS boolean STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal my_integer stable';
+  RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_stl_array_int (
+)
+RETURNS int[] STABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 's array_int';
+  RETURN '{2, 3}'::int[];
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.stable_max(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+  RETURN (SELECT max(i) from two);
+END
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.simple(
+)
+RETURNS integer STABLE AS
+$body$
+BEGIN
+  RETURN stable_max();
+END
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create immutable functions for testing
+
+CREATE OR REPLACE FUNCTION public.x_imm2 (
+     integer
+)
+RETURNS integer IMMUTABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'i2';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.x_imm2_strict (
+     integer
+)
+RETURNS integer IMMUTABLE STRICT AS
+$body$
+BEGIN
+  RAISE NOTICE 'i2 strict';
+  RETURN $1;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_integers_imm (
+  integer,
+  integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal integers immutable';
+  RETURN $1 = $2;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.equal_my_integer_imm (
+  my_integer,
+  my_integer
+)
+RETURNS boolean IMMUTABLE AS
+$body$
+BEGIN
+  RAISE NOTICE 'equal my_integer immutable';
+  RETURN $1.value = $2.value;
+END;
+$body$
+LANGUAGE 'plpgsql';
+
+-- Create operators for testing
+
+CREATE operator === (
+  PROCEDURE = equal_integers_vlt,
+  LEFTARG = integer,
+  RIGHTARG = integer
+);
+
+CREATE operator ==== (
+  PROCEDURE = equal_integers_stl,
+  LEFTARG = integer,
+  RIGHTARG = integer
+);
+
+CREATE operator ===== (
+  PROCEDURE = equal_integers_imm,
+  LEFTARG = integer,
+  RIGHTARG = integer
+);
+
+CREATE operator ====== (
+  PROCEDURE = equal_booleans_stl_strict,
+  LEFTARG = boolean,
+  RIGHTARG = boolean
+);
+
+CREATE operator ==== (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- Simple functions testing
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+-- WHERE clause testing
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+-- Functions with constant arguments and nested functions testing
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions testing
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+-- Strict functions with null arguments testing
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+-- Operators testing
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+
+-- Nested and strict operators testing
+-- (also partly mixed functions and operators testing)
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (x_stl() ==== 2) ====== (x_stl() ===== 3) FROM generate_series(1, 4) x;
+SELECT (1 ==== 2) ====== x_stl2_boolean(NULL) FROM generate_series(1, 4) x;
+
+-- IS DISTINCT FROM expression testing
+
+-- create operator here because we will drop and reuse it several times
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer
+FROM generate_series(1, 4) x;
+
+-- IS DISTINCT FROM expressions with null arguments testing
+
+SELECT x_stl2_boolean(1 IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(x_stl2(NULL) IS DISTINCT FROM x_stl2(NULL))
+FROM generate_series(1, 4) x;
+
+-- Nested IS DISTINCT FROM expression testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+  TRUE
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IS DISTINCT FROM
+  TRUE
+)
+FROM generate_series(1, 4) x;
+
+-- NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT NULLIF('(1)'::my_integer, '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- NULLIF expressions with null arguments testing
+
+SELECT x_stl2(NULLIF(1, x_stl2(NULL))) FROM generate_series(1, 4) x;
+
+SELECT x_stl2(NULLIF(x_stl2(NULL), x_stl2(NULL))) FROM generate_series(1, 4) x;
+
+-- Nested NULLIF expression testing
+
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_imm,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT NULLIF(NULLIF(x_vlt_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(NULLIF(x_stl_my_integer(), '(2)'::my_integer), '(2)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions
+-- testing
+
+SELECT 1 === ANY('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 === ALL('{2, 3}') FROM generate_series(1, 4) x; -- should not be precalculated
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+
+SELECT 1 ==== ANY('{2, 3}') FROM generate_series(1, 4) x;
+SELECT 1 ==== ALL('{2, 3}') FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)
+FROM generate_series(1, 4) x;
+
+-- "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)" expressions with
+-- null arguments testing
+
+SELECT 1 ==== ANY('{2, NULL}') FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== ANY(NULL)) FROM generate_series(1, 4) x;
+SELECT NULL ==== ANY('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+SELECT NULL ==== ANY('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL::int ==== ANY(NULL)) FROM generate_series(1, 4) x;
+
+SELECT 1 ==== ALL('{2, NULL}') FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== ALL(NULL)) FROM generate_series(1, 4) x;
+SELECT NULL ==== ALL('{2, 3}'::int[]) FROM generate_series(1, 4) x;
+SELECT NULL ==== ALL('{2, NULL}'::int[]) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL::int ==== ALL(NULL)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(1 IN (2, NULL)) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL IN (2, 3)) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL IN (2, NULL)) FROM generate_series(1, 4) x;
+
+-- Nesting "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing (also partly mixed functions and "scalar op ANY/ALL
+-- (array)" / "scalar IN (2 or more values)" expressions testing)
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ==== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((x_vlt() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((x_stl() ===== ANY('{2, 3}')) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and operators testing
+-- (most of it was earlier in Nested and strict operators testing)
+
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+-- Mixed functions and IS DISTINCT FROM expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT equal_booleans_stl_strict(
+  (x_stl_my_integer() IS DISTINCT FROM '(1)'::my_integer),
+  (x_stl_my_integer() IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT equal_booleans_stl_strict(
+  (x_stl() IS DISTINCT FROM 1),
+  (x_stl() IS DISTINCT FROM 2)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and NULLIF expressions testing
+
+-- should not be precalculated
+SELECT equal_my_integer_stl(
+  NULLIF(x_stl_my_integer(), '(1)'::my_integer),
+  NULLIF(x_stl_my_integer(), '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT equal_integers_stl(NULLIF(x_stl(), 1), NULLIF(x_stl(), 2))
+FROM generate_series(1, 4) x;
+
+-- Mixed functions and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing (partly in nesting "scalar op ANY/ALL (array)" /
+-- "scalar IN (2 or more values)" expressions testing)
+
+SELECT 1 ==== ANY(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== ALL(x_vlt_array_int()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT 1 ==== ANY(x_stl_array_int()) FROM generate_series(1, 4) x;
+SELECT 1 ==== ALL(x_stl_array_int()) FROM generate_series(1, 4) x;
+
+-- Mixed operators and IS DISTINCT FROM expressions testing
+
+-- should not be precalculated
+SELECT (
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+  ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT (
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) ======
+  ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) IS DISTINCT FROM TRUE)
+FROM generate_series(1, 4) x;
+
+-- Mixed operators and NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === 2, TRUE)) FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(NULLIF(1 ==== 2, TRUE)) FROM generate_series(1, 4) x;
+
+-- Mixed operators and "scalar op ANY/ALL (array)" / "scalar IN (2 or more
+-- values)" expressions testing
+
+-- should not be precalculated
+SELECT (1 === ANY('{2, 3}')) ====== (1 === ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean((1 === 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+SELECT (1 ==== ANY('{2, 3}')) ====== (1 ==== ALL('{2, 3}'))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) ====== TRUE
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) = ANY('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) = ALL('{TRUE}'))
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean((1 ==== 2) IN (TRUE, FALSE))
+FROM generate_series(1, 4) x;
+
+-- Mixed IS DISTINCT FROM and NULLIF expressions testing
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+  NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT NULLIF(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+  ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) IS DISTINCT FROM
+  NULLIF('(2)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT NULLIF(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer),
+  ('(2)'::my_integer IS DISTINCT FROM '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed IS DISTINCT FROM and "scalar op ANY/ALL (array)" / "scalar IN (2 or
+-- more values)" expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  (1 === ANY('{2, 3}')) IS DISTINCT FROM
+  (1 === ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+  TRUE
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+  (1 ==== ANY('{2, 3}')) IS DISTINCT FROM
+  (1 ==== ALL('{2, 3}'))
+)
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer)) IS DISTINCT FROM
+  TRUE
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ANY('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) = ALL('{TRUE}')
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(
+  ('(1)'::my_integer IS DISTINCT FROM '(2)'::my_integer) IN (TRUE, FALSE)
+)
+FROM generate_series(1, 4) x;
+
+-- Mixed NULLIF and "scalar op ANY/ALL (array)" / "scalar IN (2 or more values)"
+-- expressions testing
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(1 === ANY('{2, 3}'), 1 === ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_vlt,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+-- should not be precalculated
+SELECT x_stl2_boolean(NULLIF(
+  '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+  TRUE
+))
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+-- should not be precalculated
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+  ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+SELECT x_stl2_boolean(NULLIF(1 ==== ANY('{2, 3}'), 1 ==== ALL('{2, 3}')))
+FROM generate_series(1, 4) x;
+
+DROP OPERATOR = (my_integer, my_integer);
+CREATE OPERATOR = (
+  PROCEDURE = equal_my_integer_stl,
+  LEFTARG = my_integer,
+  RIGHTARG = my_integer
+);
+
+SELECT x_stl2_boolean(NULLIF(
+  '(1)'::my_integer IN ('(2)'::my_integer, '(3)'::my_integer),
+  TRUE
+))
+FROM generate_series(1, 4) x;
+
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  ANY('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) ====
+  ALL('{(3)}'::my_integer[])
+)
+FROM generate_series(1, 4) x;
+
+SELECT (
+  NULLIF('(1)'::my_integer, '(2)'::my_integer) IN
+  ('(3)'::my_integer, '(2)'::my_integer)
+)
+FROM generate_series(1, 4) x;
+
+-- Tracking functions testing
+
+SET track_functions TO 'all';
+
+SELECT x_vlt() FROM generate_series(1, 3) x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 3) x;
+
+SELECT x_vlt() FROM generate_series(1, 4) x WHERE x_vlt() < x; -- should not be precalculated
+SELECT x_stl() FROM generate_series(1, 4) x WHERE x_stl() < x;
+
+SELECT x_stl2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2(x_stl2(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2(x_stl2(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_imm2_strict(x_vlt()) FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2_strict(1)) FROM generate_series(1, 4) x;
+
+SELECT x_stl2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+SELECT x_imm2_strict(x_stl2(NULL)) FROM generate_series(1, 4) x;
+
+SELECT 1 === 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT 1 ==== 2 FROM generate_series(1, 4) x;
+SELECT 1 ===== 2 FROM generate_series(1, 4) x;
+
+SELECT (x_vlt() ==== 2) ====== (x_vlt() ===== 3) FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT (1 ==== 2) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(NULL) ====== (3 ==== 3) FROM generate_series(1, 4) x;
+
+SELECT x_vlt() ==== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+SELECT x_vlt() ===== 2 FROM generate_series(1, 4) x; -- should not be precalculated
+
+SELECT x_stl() ==== x_stl() FROM generate_series(1, 4) x;
+SELECT x_stl2_boolean(1 ==== 2) FROM generate_series(1, 4) x;
+
+SET track_functions TO DEFAULT;
+
+-- PL/pgSQL Simple expressions
+-- Make sure precalculated stable functions can't be simple expressions: these
+-- expressions are only initialized once per transaction and then executed
+-- multiple times.
+
+BEGIN;
+SELECT simple();
+INSERT INTO two VALUES (3);
+SELECT simple();
+ROLLBACK;
+
+-- Drop tables for testing
+
+DROP TABLE two;
-- 
1.9.1

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to