PostgreSQL行级安全中INTERSECT用法问题:查询结果不符预期
问题分析与解决方案
你的RLS政策之所以返回所有数据,核心问题出在递归引用目标表加上潜在的字段名笔误,导致逻辑完全偏离预期。我来一步步拆解并修正:
1. 核心问题:递归引用的致命错误
你的政策里写了:
select _section from user_table intersect (select _section::text[] from section_table where ...)
当你在user_table的RLS政策中再次查询user_table时,会触发递归RLS检查——因为表已经启用了行级安全,这个子查询会再次应用同一个政策。PostgreSQL处理这种递归时,会导致子查询返回所有行(相当于暂时绕过了RLS评估),最终intersect后的结果包含所有_section值,使得_section @> (...)对所有行都成立,自然返回了全部数据。
另外,你政策里写的section @>是笔误,user_table的字段是_section,这个错误要么会直接报错,要么会让PostgreSQL无法正确匹配条件,间接导致所有行通过检查。
2. 符合预期的正确政策写法
你的预期逻辑是:传入sub_section时,找到对应的_section,只要user_table行的_section与该集合有交集就返回。我们可以用两种方式实现这个逻辑,避免递归引用:
方式1:数组交集判断(简洁直观)
-- 先删除旧政策 DROP POLICY IF EXISTS user_p ON user_table; CREATE POLICY user_p ON user_table FOR SELECT USING ( -- 检查当前行的_section是否与匹配的_section集合有交集 _section && ( SELECT array_agg(_section) FROM section_table WHERE current_setting('my.sub_section', true)::text[] <@ sub_section ) );
方式2:EXISTS子句(性能更优,适合大数据量)
DROP POLICY IF EXISTS user_p ON user_table; CREATE POLICY user_p ON user_table FOR SELECT USING ( EXISTS ( SELECT 1 FROM section_table st -- 匹配当前sub_section对应的section WHERE current_setting('my.sub_section', true)::text[] <@ st.sub_section -- 检查当前user行的_section是否包含这个匹配的section AND st._section = ANY(user_table._section) ) );
3. 测试验证
执行你的测试语句:
set my.sub_section ='{"c"}'; select * from user_table;
此时section_table中只有_section='section2'满足{"c"} <@ {"c","d"},所以:
(1,'{"section1"}'):无交集,不返回(2,'{"section2"}'):有交集,返回(2,'{"section2","section1"}'):也包含section2,会被返回
如果你的预期是仅返回_section完全等于匹配值的行,可以把政策改成:
CREATE POLICY user_p ON user_table FOR SELECT USING ( _section = ( SELECT _section FROM section_table WHERE current_setting('my.sub_section', true)::text[] <@ sub_section LIMIT 1 -- 若存在多个匹配的_section,需额外处理唯一性 ) );
内容的提问来源于stack exchange,提问作者lin
相关产品推荐
相关产品推荐

