PostgreSQL函数中设会话变量为参数值后RLS无法识别的问题
问题分析与解决办法
你遇到的核心问题是:在PL/pgSQL函数里把会话变量"my.user_id"设为固定值时RLS正常工作,但设为函数参数v_user_id就失效。这是因为PostgreSQL在解析RETURN QUERY对应的查询时,会提前绑定会话变量的值,普通SET命令的会话级变量更新不会立即作用于紧接着的查询。
下面给你两个可行的解决方案:
方案1:用SET LOCAL替代SET
SET LOCAL会把变量的作用域限制在当前事务内,而PL/pgSQL函数默认在事务中执行,这样后续的RETURN QUERY查询能获取到刚设置的参数值:
CREATE OR REPLACE FUNCTION sbx.get_expenses_by_ppp_table_2(v_user_id character varying, day character varying) RETURNS TABLE(report_id integer, report_row_id character varying, report_row_name character varying, row_level_num integer, fact_amount numeric, plan_amount numeric, execution_percentage numeric, daily_fact_amount numeric) LANGUAGE plpgsql AS $function$ #variable_conflict use_column declare report_day date; report_year int4; begin report_day := day::date; report_year := date_part('year', report_day); SET LOCAL "my.user_id" = v_user_id; -- 替换为SET LOCAL return query select 1 as report_id, ex.ppp_code as report_row_id, -- 补充你的剩余查询逻辑 ...; end; $function$;
方案2:确认RLS策略的变量引用正确
确保你的RLS策略里是用current_setting('my.user_id')来读取变量的,比如:
CREATE POLICY expenses_user_policy ON sbx.expenses FOR SELECT TO public USING (user_id = current_setting('my.user_id')::character varying);
如果之前的策略写法有问题,哪怕变量设置对了也不会生效。
补充说明
固定值能生效是因为SET "my.user_id" = '1'是会话级生效,测试时变量值会保留到查询执行阶段。但用参数设置时,普通SET的更新在查询解析后才生效,导致查询用的是旧值。SET LOCAL强制在当前事务内生效,能让后续查询读到最新的参数值。
内容的提问来源于stack exchange,提问作者Иван Размахнин
相关产品推荐
相关产品推荐

