From dcf45b66a9db3033c775cc8aa70b3e840aeb89fa Mon Sep 17 00:00:00 2001 From: "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