Supabase RLS策略查询同表时陷入递归循环问题求助
问题背景
我通过community_members表关联community_id与user_id,实现用户加入社区的功能。需要给该表配置SELECT类型的RLS策略,要求:
用户仅能查看自身所属社区的所有成员数据(即当存在包含当前认证用户
user_id与对应community_id的行时,用户可查看该community_id下的所有行)
但当前编写的策略语句触发了无限递归循环,导致查询无返回结果,策略语句如下:
(community_id IN ( SELECT community_members.community_id FROM community_members WHERE ((auth.uid() = community_members.user_id) AND (community_id = community_members.community_id))))
对应的Dart查询代码:
return client .from('community_members') .stream(primaryKey: ['id']) .eq('user_id', userId);
问题
- 在同表上配置此类RLS策略是否合理?
- 有没有避免递归循环的优化方案?
解答
1. 同表配置这类策略的合理性
完全合理。业务逻辑需要基于同表中已有的用户-社区关联关系来判断权限,这类场景在RLS中很常见,问题出在策略的写法而非同表配置本身。当前写法的递归是因为PostgreSQL执行子查询时会再次触发同表的RLS检查,导致无限嵌套。
2. 避免递归循环的优化方案
方案一:简化子查询,移除冗余条件
将策略修改为更简洁的形式,移除冗余的community_id = community_members.community_id条件:
community_id IN ( SELECT community_id FROM community_members WHERE user_id = auth.uid() )
该写法减少了不必要的逻辑,PostgreSQL的查询优化器更容易识别权限判断的边界,避免递归触发RLS。
方案二:使用EXISTS替代IN(推荐)
EXISTS的逻辑更直接,查询性能更优,且更不容易触发递归:
EXISTS ( SELECT 1 FROM community_members cm WHERE cm.community_id = community_members.community_id AND cm.user_id = auth.uid() )
此策略的核心是:检查当前行的community_id是否存在一条属于当前认证用户的关联记录,逻辑清晰且不会引发递归。
方案三:临时绕过子查询的RLS(备选)
如果前两种方案仍有问题,可以在子查询中临时绕过RLS(需确保安全):
community_id IN ( SELECT community_id FROM community_members WHERE user_id = auth.uid() WITH NO RLS )
注:WITH NO RLS会绕过该表的RLS,但此处仅查询当前用户自身的社区关联记录,风险可控。
额外说明
你的Dart查询代码无需修改,只要RLS策略配置正确,eq('user_id', userId)的查询会自动被RLS过滤,仅返回用户有权限查看的行。
内容的提问来源于stack exchange,提问作者Koen

