From 3f292e597470d4e1d48003680c4e49770a7cc715 Mon Sep 17 00:00:00 2001
From: "okbob@github.com" <okbob@github.com>
Date: Sat, 20 Jan 2024 20:35:38 +0100
Subject: [PATCH 17/19] expression with session variables can be inlined

There is not an reason why session variables should to block inlining.
(of SQL functions). I can imagine some use cases like wrapping, and
inlining significantly reduces an overhead of SQL functions.
---
 src/backend/optimizer/util/clauses.c          | 37 ++++++++++++++-----
 .../regress/expected/session_variables.out    |  7 ++--
 2 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 65f94cc084..ace4ce787e 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4732,8 +4732,7 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 		querytree->limitOffset ||
 		querytree->limitCount ||
 		querytree->setOperations ||
-		(list_length(querytree->targetList) != 1) ||
-		querytree->hasSessionVariables)
+		(list_length(querytree->targetList) != 1))
 		goto fail;
 
 	/* If the function result is composite, resolve it */
@@ -4937,21 +4936,39 @@ substitute_actual_parameters_mutator(Node *node,
 {
 	if (node == NULL)
 		return NULL;
+
+
+	/*
+	 * SQL functions can contain two different kind of params. The nodes with
+	 * paramkind PARAM_EXTERN are related to function's arguments (and should
+	 * be replaced in this step), because this is how we apply the function's
+	 * arguments for an expression.
+	 *
+	 * The nodes with paramkind PARAM_VARIABLE are related to usage of session
+	 * variables. The values of session variables are not passed to expression
+	 * by expression arguments, so it should not be replaced here by
+	 * function's arguments.
+	 */
 	if (IsA(node, Param))
 	{
 		Param	   *param = (Param *) node;
 
-		if (param->paramkind != PARAM_EXTERN)
+		if (param->paramkind != PARAM_EXTERN &&
+			param->paramkind != PARAM_VARIABLE)
 			elog(ERROR, "unexpected paramkind: %d", (int) param->paramkind);
-		if (param->paramid <= 0 || param->paramid > context->nargs)
-			elog(ERROR, "invalid paramid: %d", param->paramid);
 
-		/* Count usage of parameter */
-		context->usecounts[param->paramid - 1]++;
+		if (param->paramkind == PARAM_EXTERN)
+		{
+			if (param->paramid <= 0 || param->paramid > context->nargs)
+				elog(ERROR, "invalid paramid: %d", param->paramid);
 
-		/* Select the appropriate actual arg and replace the Param with it */
-		/* We don't need to copy at this time (it'll get done later) */
-		return list_nth(context->args, param->paramid - 1);
+			/* Count usage of parameter */
+			context->usecounts[param->paramid - 1]++;
+
+			/* Select the appropriate actual arg and replace the Param with it */
+			/* We don't need to copy at this time (it'll get done later) */
+			return list_nth(context->args, param->paramid - 1);
+		}
 	}
 	return expression_tree_mutator(node, substitute_actual_parameters_mutator,
 								   (void *) context);
diff --git a/src/test/regress/expected/session_variables.out b/src/test/regress/expected/session_variables.out
index d7a8ea633c..d58a78650c 100644
--- a/src/test/regress/expected/session_variables.out
+++ b/src/test/regress/expected/session_variables.out
@@ -104,7 +104,6 @@ SELECT var1;
 ERROR:  permission denied for session variable var1
 SELECT sqlfx(20);
 ERROR:  permission denied for session variable var1
-CONTEXT:  SQL function "sqlfx" statement 1
 SELECT plpgsqlfx(20);
 ERROR:  permission denied for session variable var1
 CONTEXT:  PL/pgSQL function plpgsqlfx(integer) line 1 at RETURN
@@ -211,10 +210,10 @@ SELECT sqlfx1(sqlfx2('Hello'));
 
 -- inlining is blocked
 EXPLAIN (COSTS OFF, VERBOSE) SELECT sqlfx1(sqlfx2('Hello'));
-                      QUERY PLAN                      
-------------------------------------------------------
+                                 QUERY PLAN                                  
+-----------------------------------------------------------------------------
  Result
-   Output: sqlfx1(sqlfx2('Hello'::character varying))
+   Output: ((var1 || ', '::text) || ((var2 || ', '::text) || 'Hello'::text))
 (2 rows)
 
 DROP FUNCTION sqlfx1(varchar);
-- 
2.44.0

