v20240320-0013-allow-read-an-value-of-session-variable-directly-fro.patch
text/x-patch
Filename: v20240320-0013-allow-read-an-value-of-session-variable-directly-fro.patch
Type: text/x-patch
Part: 6
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v20240320-0013
Subject: allow read an value of session variable directly from expression executor
| File | + | − |
|---|---|---|
| src/backend/executor/execExpr.c | 63 | 25 |
| src/backend/executor/execExprInterp.c | 16 | 0 |
| src/backend/jit/llvm/llvmjit_expr.c | 6 | 0 |
| src/backend/parser/analyze.c | 0 | 8 |
| src/include/executor/execExpr.h | 11 | 1 |
| src/pl/plpgsql/src/expected/plpgsql_session_variable.out | 1 | 2 |
| src/pl/plpgsql/src/pl_exec.c | 1 | 2 |
| src/test/regress/expected/session_variables.out | 6 | 9 |
| src/test/regress/sql/session_variables.sql | 4 | 3 |
From 02596f696536d43e1e7d2226005b765d118533c6 Mon Sep 17 00:00:00 2001
From: "okbob@github.com" <okbob@github.com>
Date: Sat, 20 Jan 2024 06:31:48 +0100
Subject: [PATCH 13/19] allow read an value of session variable directly from
expression executor
The expression executor can be called directly (not inside query executor)
when expression is evaluated as simple expression in plpgsql or when expression
is an argument of CALL statement. For these cases we need to allow direct
access to content of session variables from executor. We can do it, because
we know so the expression is not evaluated under query and cannot be
evaluated inside parallel worker.
This patch enables session variables for simple expression evaluation.
This patch enables usage session variables in arguments of CALL stmt.
---
src/backend/executor/execExpr.c | 88 +++++++++++++------
src/backend/executor/execExprInterp.c | 16 ++++
src/backend/jit/llvm/llvmjit_expr.c | 6 ++
src/backend/parser/analyze.c | 8 --
src/include/executor/execExpr.h | 12 ++-
.../src/expected/plpgsql_session_variable.out | 3 +-
src/pl/plpgsql/src/pl_exec.c | 3 +-
.../regress/expected/session_variables.out | 15 ++--
src/test/regress/sql/session_variables.sql | 7 +-
9 files changed, 108 insertions(+), 50 deletions(-)
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index ebe636e37b..5d9f874658 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -994,31 +994,69 @@ ExecInitExprRec(Expr *node, ExprState *state,
es_num_session_variables = state->parent->state->es_num_session_variables;
}
- Assert(es_session_variables);
-
- /*
- * Use buffered session variables when the
- * buffer with copied values is avaiable
- * (standard query executor mode)
- */
-
- /* Parameter sanity checks. */
- if (param->paramid >= es_num_session_variables)
- elog(ERROR, "paramid of PARAM_VARIABLE param is out of range");
-
- var = &es_session_variables[param->paramid];
-
- if (var->typid != param->paramtype)
- elog(ERROR, "type of buffered value is different than PARAM_VARIABLE type");
-
- /*
- * In this case, pass the value like a
- * constant.
- */
- scratch.opcode = EEOP_CONST;
- scratch.d.constval.value = var->value;
- scratch.d.constval.isnull = var->isnull;
- ExprEvalPushStep(state, &scratch);
+ if (es_session_variables)
+ {
+ /*
+ * This path is used when expression is
+ * evaluated inside query evaluation. For
+ * ensuring of immutability of session variable
+ * inside query we use special buffer with copy
+ * of used session variables. This method helps
+ * with parallel execution too.
+ */
+
+ /* Parameter sanity checks. */
+ if (param->paramid >= es_num_session_variables)
+ elog(ERROR, "paramid of PARAM_VARIABLE param is out of range");
+
+ var = &es_session_variables[param->paramid];
+
+ if (var->typid != param->paramtype)
+ elog(ERROR, "type of buffered value is different than PARAM_VARIABLE type");
+
+ /*
+ * In this case, pass the value like a
+ * constant.
+ */
+ scratch.opcode = EEOP_CONST;
+ scratch.d.constval.value = var->value;
+ scratch.d.constval.isnull = var->isnull;
+ ExprEvalPushStep(state, &scratch);
+ }
+ else
+ {
+ AclResult aclresult;
+ Oid varid = param->paramvarid;
+ Oid vartype = param->paramtype;
+
+ Assert(!IsParallelWorker());
+
+ /*
+ * In some cases (plpgsql's simple expression
+ * evaluation or evaluation of CALL arguments),
+ * the expression executor is called directly.
+ * We can allow direct access to session
+ * variables (copy only), because we know, so
+ * outside is not any query (and expression
+ * cannot be executed parallel).
+ *
+ * In this case we should to do aclcheck,
+ * because usual aclcheck from
+ * standard_ExecutorStart is not executed in
+ * this case. Fortunately it is just once per
+ * transaction.
+ */
+ aclresult = object_aclcheck(VariableRelationId, varid,
+ GetUserId(), ACL_SELECT);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_VARIABLE,
+ get_session_variable_name(varid));
+
+ scratch.opcode = EEOP_PARAM_VARIABLE;
+ scratch.d.vparam.varid = varid;
+ scratch.d.vparam.vartype = vartype;
+ ExprEvalPushStep(state, &scratch);
+ }
}
break;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index a25ab7570f..90a9dc3db9 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -59,6 +59,7 @@
#include "access/heaptoast.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
+#include "commands/session_variable.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -449,6 +450,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
&&CASE_EEOP_PARAM_CALLBACK,
+ &&CASE_EEOP_PARAM_VARIABLE,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
@@ -1089,6 +1091,20 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_VARIABLE)
+ {
+ /*
+ * Direct access to session variable (without buffering). Because
+ * returned value can be used (without an assignement) after the
+ * referenced session variables is updated, we have to use an copy
+ * of stored value every time.
+ */
+ *op->resvalue = GetSessionVariableWithTypeCheck(op->d.vparam.varid,
+ op->resnull,
+ op->d.vparam.vartype);
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 2a7d84f046..4715c125ad 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -1145,6 +1145,12 @@ llvm_compile_expr(ExprState *state)
break;
}
+ case EEOP_PARAM_VARIABLE:
+ build_EvalXFunc(b, mod, "ExecEvalParamVariable",
+ v_state, op, v_econtext);
+ LLVMBuildBr(b, opblocks[opno + 1]);
+ break;
+
case EEOP_SBSREF_SUBSCRIPTS:
{
int jumpdone = op->d.sbsref_subscript.jumpdone;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 3b218bd8ae..23640c306b 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -3385,14 +3385,6 @@ transformCallStmt(ParseState *pstate, CallStmt *stmt)
true,
stmt->funccall->location);
- /*
- * The arguments of CALL statement are evaluated by direct
- * expression executor call. This path is unsupported yet,
- * so block it. It will be enabled by some other patch.
- */
- if (pstate->p_hasSessionVariables)
- elog(ERROR, "session variable cannot be used as an argument");
-
assign_expr_collations(pstate, node);
fexpr = castNode(FuncExpr, node);
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 8953d76738..1b8c8ca07b 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -156,10 +156,11 @@ typedef enum ExprEvalOp
EEOP_BOOLTEST_IS_FALSE,
EEOP_BOOLTEST_IS_NOT_FALSE,
- /* evaluate PARAM_EXEC/EXTERN parameters */
+ /* evaluate PARAM_EXEC/EXTERN/VARIABLE parameters */
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
+ EEOP_PARAM_VARIABLE,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
@@ -397,6 +398,13 @@ typedef struct ExprEvalStep
Oid paramtype; /* OID of parameter's datatype */
} cparam;
+ /* for EEOP_PARAM_VARIABLE */
+ struct
+ {
+ Oid varid; /* OID of assigned variable */
+ Oid vartype; /* OID of parameter's datatype */
+ } vparam;
+
/* for EEOP_CASE_TESTVAL/DOMAIN_TESTVAL */
struct
{
@@ -780,6 +788,8 @@ extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecEvalParamVariable(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext);
extern void ExecEvalCoerceViaIOSafe(ExprState *state, ExprEvalStep *op);
extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
diff --git a/src/pl/plpgsql/src/expected/plpgsql_session_variable.out b/src/pl/plpgsql/src/expected/plpgsql_session_variable.out
index 6e8b4134c0..1a2aeba24d 100644
--- a/src/pl/plpgsql/src/expected/plpgsql_session_variable.out
+++ b/src/pl/plpgsql/src/expected/plpgsql_session_variable.out
@@ -211,8 +211,7 @@ SET ROLE TO regress_var_exec_role;
-- should to fail
SELECT var_read_func();
ERROR: permission denied for session variable plpgsql_sv_var1
-CONTEXT: SQL expression "plpgsql_sv_var1"
-PL/pgSQL function var_read_func() line 3 at RAISE
+CONTEXT: PL/pgSQL function var_read_func() line 3 at RAISE
SET ROLE TO DEFAULT;
SET ROLE TO regress_var_owner_role;
GRANT SELECT ON VARIABLE plpgsql_sv_var1 TO regress_var_reader_role;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 11f9dedbb2..6947575b94 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8037,8 +8037,7 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
query->sortClause ||
query->limitOffset ||
query->limitCount ||
- query->setOperations ||
- query->hasSessionVariables)
+ query->setOperations)
return;
/*
diff --git a/src/test/regress/expected/session_variables.out b/src/test/regress/expected/session_variables.out
index 6beffd0f88..4d93761d5d 100644
--- a/src/test/regress/expected/session_variables.out
+++ b/src/test/regress/expected/session_variables.out
@@ -107,8 +107,7 @@ ERROR: permission denied for session variable var1
CONTEXT: SQL function "sqlfx" statement 1
SELECT plpgsqlfx(20);
ERROR: permission denied for session variable var1
-CONTEXT: SQL expression "$1 + var1"
-PL/pgSQL function plpgsqlfx(integer) line 1 at RETURN
+CONTEXT: PL/pgSQL function plpgsqlfx(integer) line 1 at RETURN
-- should be ok
SELECT sqlfx_sd(20);
sqlfx_sd
@@ -278,17 +277,15 @@ $$;
NOTICE: result array: {1,2,3,4,5,6,7,8,9,10}
DROP VARIABLE var1;
DROP VARIABLE var2;
--- CALL statement is not supported yet
--- requires direct access to session variable from expression executor
+-- CALL statement is supported
CREATE VARIABLE v int;
+LET v = 1;
CREATE PROCEDURE p(arg int) AS $$ BEGIN RAISE NOTICE '%', arg; END $$ LANGUAGE plpgsql;
--- should not crash (but is not supported yet)
+-- should to work
CALL p(v);
-ERROR: session variable cannot be used as an argument
+NOTICE: 1
DO $$ BEGIN CALL p(v); END $$;
-ERROR: session variable cannot be used as an argument
-CONTEXT: SQL statement "CALL p(v)"
-PL/pgSQL function inline_code_block line 1 at CALL
+NOTICE: 1
DROP PROCEDURE p(int);
DROP VARIABLE v;
-- test search path
diff --git a/src/test/regress/sql/session_variables.sql b/src/test/regress/sql/session_variables.sql
index abd048423a..dce4bd057f 100644
--- a/src/test/regress/sql/session_variables.sql
+++ b/src/test/regress/sql/session_variables.sql
@@ -207,13 +207,14 @@ $$;
DROP VARIABLE var1;
DROP VARIABLE var2;
--- CALL statement is not supported yet
--- requires direct access to session variable from expression executor
+-- CALL statement is supported
CREATE VARIABLE v int;
+LET v = 1;
+
CREATE PROCEDURE p(arg int) AS $$ BEGIN RAISE NOTICE '%', arg; END $$ LANGUAGE plpgsql;
--- should not crash (but is not supported yet)
+-- should to work
CALL p(v);
DO $$ BEGIN CALL p(v); END $$;
--
2.44.0