PostgreSQL中是可以针对不同用户,按行过滤数据的,这其实跟管理信息系统里经常提到的数据权限是干一个事情。但是由数据库自身提供这个功能,听起来还是很强大的。真要动手操作起来也并不复杂,主要有这么几个要点:

1. 开启行级权限

ALTER TABLE 目标表名 ENABLE ROW LEVEL SECURITY;

开启行级权限后,除了表的拥有者,其他用户都是默认否定权限,也就是不可能从表中查询到数据,当然其他DML操作也不行。

2. 创建策略

CREATE POLICY 起个策略名 ON 目标表名 TO 目标角色名
USING (一个能返回boolean的表达式);

当表达式返回true时,该行就是可见的,反之是隐藏的。如果是update或delete一个不符合策略的数据,不会报错,只会略过。

3. 精确到命令的策略

策略是可以精确到SELECTINSERTUPDATE以及DELETE的,写在TO之前,用FOR打头,像这样

CREATE POLICY 起个策略名 ON 目标表名 FOR 命令名(比如select) TO 目标角色名
USING (一个能返回boolean的表达式);

如果不写命令的话,默认是ALL

4. 由于支持命令级别的策略,实际的复杂度大大上升了

听我细细讲来:

  • selectdelete是面对现存数据的命令
  • insert是面对新产生数据的命令
  • update跨界,它既要面对现存数据,又要产生新数据

这么看,上面只有一个USING表达式的思路就说不通了,在只有一个USING的情况下,它到底是描述数据的可见性问题还是描述数据的合规性问题呢?这就引出了WITH CHECK表达式。USING负责判断数据的可见性,WITH CHECK符合判断数据的合规性。这里拿update举例子比较合适

create policy my_policy on users
for update
to joe
using (user_name = current_user)
with check (age > 18)

这里USING的表达式限制了只有字段user_name等于当前用户名的数据才可以被编辑(current_user是系统内置变量);而编辑后的结果,只有age大于18才可以成功存储。用过数据库约束的,现在肯定明白了,with check就是数据库约束嘛,只是这个约束不是面向表的,而是面向角色(用户)的。
这也就解释了当用户尝试update它没权限update的数据,并不会报错,而update的数据不满足check时,会收到一个错:

ERROR:  new row violates row-level security policy for table "users"

5. 如果策略不止一个,那它们之间的关系是OR?还是AND

答案是默认情况是OR,这个叫宽松策略。如果引入RESTRICTIVE关键字就成为限制性策略。其用法如下

CREATE POLICY  users_managers ON users as RESTRICTIVE  for select  TO joe  using(first_name='a');

所有限制性策略都是AND链接的,在跟宽松策略组合起来使用时,会这么一个情况

(宽松策略A or 宽松策略B) and (限制性策略C and 限制性策略D)

不过还有一点要注意,如果只有限制性策略时不行的,必须先有一个宽松策略才行,这个可以理解成限制性策略必须得跟别人and,如果不提供宽松策略的话,就是and (空),所以结果只能是空

6. 官方文档

CREATE POLICY的详细语法解析在此
行级权限(行安全性策略)的整体解释和示例代码在此