SQL COMMAND · 角色与权限
CREATE POLICY
为一个表定义一条新的行级安全性策略
CREATE角色与权限引入 10(基线)现存至 20 devel1 次语法变更
- 动词
CREATE- 对象
POLICY- 分组
- 角色与权限
- 引入版本
- 10(基线)
- 状态
- 现存
- 语法变更次数
- 1
- 手册小节数
- 5
版本轨迹
相对 PostgreSQL 17 语法未变,正文有更新。
语法铁道图 PostgreSQL 18
沿轨道从左向右阅读,分岔表示选择,绕行表示可选,回环表示重复。方框为参数,点击带下划线的参数可展开子规则。
语法概要
CREATE POLICYnameONtable_name[ AS { PERMISSIVE | RESTRICTIVE } ] [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ] [ TO {role_name| PUBLIC | CURRENT_ROLE | CURRENT_USER | SESSION_USER } [, ...] ] [ USING (using_expression) ] [ WITH CHECK (check_expression) ]
描述
CREATE POLICY命令为一个表定义一条新的行级 安全性策略。请注意,必须先在该表上启用行级安全性(使用 ALTER TABLE ... ENABLE ROW LEVEL SECURITY), 已创建的策略才会被应用。
策略允许对符合相关策略表达式的行进行选择、插入、更新或删除。 现有表行会根据USING中指定的表达式进行检查, 而将通过INSERT或UPDATE创建的新行 则会根据WITH CHECK中指定的表达式进行检查。当 USING表达式对给定行返回真时,该行对用户可见; 如果返回假或 null,则该行不可见。通常,当某行不可见时不会报错, 但也有例外,见Table 299。当 WITH CHECK表达式对某行返回真时,该行会被插入或 更新;如果返回假或 null,则会报错。
对于INSERT、UPDATE和 MERGE语句, WITH CHECK表达式会在BEFORE 触发器触发后、在进行任何实际数据修改之前强制执行。因此, BEFORE ROW触发器可以修改待插入的数据,从而影响 安全策略检查的结果。WITH CHECK表达式会在任何其他 约束之前执行。
策略名称按表区分。因此,同一个策略名可以用于许多不同的表,并且在 每个表上都可以有适合该表的定义。
策略可以针对特定命令或特定角色应用。除非另有指定,新建策略默认 适用于所有命令和角色。多个策略可以应用于同一命令;更多细节见下文。 Table 299总结了不同类型的策略如何应用于 特定命令。
对于既可以具有USING又可以具有 WITH CHECK表达式的策略(ALL 和UPDATE),如果未定义 WITH CHECK表达式,那么USING 表达式将同时用于决定哪些行可见(普通USING情形) 以及允许写入哪些新行(WITH CHECK情形)。
如果某个表启用了行级安全性,但不存在适用的策略,则会假定存在一条 “默认拒绝”策略,因此没有任何行可见或可更新。
参数
name要创建的策略名称。它必须不同于该表上任何其他策略的名称。
table_name该策略适用的表的名称(可选模式限定)。
PERMISSIVE指定将该策略创建为宽松策略。适用于给定查询的所有宽松策略都会 使用布尔“OR”操作符组合在一起。通过创建宽松策略, 管理员可以扩大可访问行的集合。策略默认是宽松的。
RESTRICTIVE指定将该策略创建为限制性策略。适用于给定查询的所有限制性策略都会 使用布尔“AND”操作符组合在一起。通过创建限制性策略, 管理员可以缩小可访问行的集合,因为每一行都必须通过所有限制性 策略。
请注意,要让限制性策略能够有效缩小访问范围,必须先至少有一条 宽松策略授予对行的访问。如果只存在限制性策略,则没有任何行 可访问。当宽松策略和限制性策略混合存在时,只有在至少一条宽松 策略通过且所有限制性策略也都通过时,某一行才可访问。
command该策略适用的命令。有效选项是
ALL、SELECT、INSERT、UPDATE和DELETE。ALL是默认值。 有关这些策略如何应用的细节见下文。role_name该策略适用的角色(或多个角色)。默认是
PUBLIC, 即对所有角色应用该策略。using_expression任意返回
boolean的SQL条件表达式。 该条件表达式不能包含任何聚合函数或窗口函数。如果启用了行级安全性, 该表达式会被添加到引用该表的查询中。表达式返回真的行将对用户可见。 表达式返回假或 null 的行在SELECT中对用户不可见, 在UPDATE或DELETE中也不能 用于修改。通常,这类行会被静默忽略,不会报告错误(但例外情况见 Table 299)。check_expression任意返回
boolean的SQL条件表达式。 该条件表达式不能包含任何聚合函数或窗口函数。如果启用了行级安全性, 该表达式将用于针对该表的INSERT和UPDATE查询。只有使该表达式求值为真的行才会 被允许。对任何被插入的行,或更新后产生的任何行,如果该表达式 求值为假或 null,就会抛出错误。请注意,check_expression是针对该行拟写入的新内容而不是原始内容求值的。
针对每种命令的策略
ALL对策略使用
ALL意味着它适用于所有命令,而不论 命令类型如何。如果存在ALL策略且还存在更具体 的策略,则两者都会被应用。此外,ALL策略会 同时应用于查询的选行部分和修改部分;如果只定义了USING表达式,那么两种情况下都使用该USING表达式。例如,如果发出
UPDATE,那么ALL策略既适用于UPDATE能够选出作为更新目标的行 (应用USING表达式),也适用于更新后的结果 行,以检查它们是否允许被写入该表(若定义了WITH CHECK表达式则应用之,否则应用USING表达式)。如果INSERT或UPDATE命令试图向表中添加未通过ALL策略的WITH CHECK表达式(若未定义WITH CHECK表达式,则为其USING表达式)的行,整个命令将被中止。SELECT对策略使用
SELECT,意味着它适用于SELECT查询,以及在其定义所在关系上需要SELECT权限的任何场景。结果是,只有通过SELECT策略的那些行才会在SELECT查询中返回,而像UPDATE、DELETE和MERGE这类需要SELECT权限的查询,也只能看到SELECT策略允许的那些行。SELECT策略不能带有WITH CHECK表达式,因为它只适用于从关系中 取回行的情况,下述情形除外。如果某个修改数据的查询带有
RETURNING子句, 则该关系上需要SELECT权限,并且该关系中新插入或更新的任何行 都必须满足该关系的SELECT策略,才能提供给RETURNING子句。如果新插入或更新的行不满足该关系的SELECT策略,就会抛出错误 (需要返回的插入或更新行绝不会被静默忽略)。如果
INSERT带有ON CONFLICT DO UPDATE子句, 或带有指定仲裁索引或约束的ON CONFLICT DO NOTHING子句, 那么该关系上也需要SELECT权限,并且提议插入的行会按照该关系的SELECT策略进行检查。如果某个提议插入的行不满足该关系的SELECT策略,就会抛出错误 (INSERT绝不会被静默跳过)。 此外,如果走的是UPDATE路径,则待更新的行以及更新后的新行 都会按照该关系的SELECT策略进行检查;如果不满足,同样会报错 (辅助的UPDATE绝不会被静默跳过)。MERGE命令要求在源关系和目标关系上都具有SELECT权限,因此每个关系的SELECT策略 都会在连接它们之前被应用,而MERGE动作也只能看到这些策略允许的行。 另外,如果执行了UPDATE动作,则目标关系的SELECT策略会像独立的UPDATE那样应用到更新后的行; 不满足时会报错。INSERT对策略使用
INSERT,意味着它将适用于INSERT命令,以及包含INSERT动作的MERGE命令。插入的行如果未通过该策略, 将导致策略违规错误,并且整个INSERT命令将被 中止。INSERT策略不能带有USING表达式,因为它只适用于向关系添加行 的情况。注意,带有
ON CONFLICT DO NOTHING/UPDATE子句的INSERT,会对所有提议插入的行检查INSERT策略的WITH CHECK表达式, 无论这些行最终是否真的被插入。UPDATE对策略使用
UPDATE,意味着它将适用于UPDATE、SELECT FOR UPDATE、SELECT FOR SHARE命令,以及INSERT命令中 辅助的ON CONFLICT DO UPDATE子句,还适用于包含UPDATE动作的MERGE命令。 由于UPDATE命令需要取出现有行并用修改后的 新行替换它,因此UPDATE策略同时接受USING表达式和WITH CHECK表达式。USING表达式决定UPDATE命令能够看到哪些行来执行操作,而WITH CHECK表达式则定义哪些修改后的行允许 被写回该关系。任何更新后的值未通过
WITH CHECK表达式的行 都会导致错误,并且整个命令将被中止。如果只指定了一个USING子句,那么该子句将被用于USING和WITH CHECK两种情况。通常,
UPDATE命令还需要从待更新关系的列中读取 数据(例如在WHERE子句、RETURNING子句,或SET子句右侧的表达式中)。这种情况下,正在被更新的关系上也需要SELECT权限,并且除了UPDATE策略外,还会应用适当的SELECT或ALL策略。这样, 用户除了必须通过UPDATE或ALL策略获准更新这些行之外,还必须通过SELECT或ALL策略访问 正在被更新的行。当
INSERT命令带有辅助的ON CONFLICT DO UPDATE子句时,如果走UPDATE路径,则待更新的行会先根据任意UPDATE策略的USING表达式进行检查, 然后新的更新后行会再根据WITH CHECK表达式进行检查。 但要注意,与独立的UPDATE命令不同,如果现有行没有通过USING表达式检查,就会报错 (UPDATE路径绝不会被静默跳过)。 对MERGE命令中的UPDATE动作也是如此。DELETE对策略使用
DELETE,意味着它将适用于DELETE命令,以及包含DELETE动作的MERGE命令。对于DELETE命令,只有通过 该策略的行才会被DELETE命令看到。 可能存在一些通过SELECT策略可见,但由于未通过DELETE策略的USING表达式而不能删除的行。 但请注意,在MERGE命令中的DELETE动作会看到 通过SELECT策略可见的行;如果某一行未通过DELETE策略,就会报错。在多数情况下,
DELETE命令也需要从其删除所针对 的关系中的列读取数据(例如在WHERE子句或RETURNING子句中)。这种情况下,该关系上也 需要SELECT权限,并且除了DELETE策略外,还会应用适当的SELECT或ALL策略。这样, 用户除了必须通过DELETE或ALL策略获准删除这些行之外,还必须通过SELECT或ALL策略访问 正在被删除的行。DELETE策略不能具有WITH CHECK表达式,因为它只适用于正在从关系中删除行的情况, 所以没有新行需要检查。
Table 299总结了不同类型的策略如何应用到特定命令上。 在该表中,“check”表示要检查策略表达式,若其返回假或 null 则抛出错误; 而“filter”表示若策略表达式返回假或 null,则该行会被静默忽略。
Table 299. 按命令类型应用的策略
| 命令 | SELECT/ALL策略 | INSERT/ALL策略 | UPDATE/ALL策略 | DELETE/ALL策略 | |
|---|---|---|---|---|---|
USING 表达式 | WITH CHECK 表达式 | USING 表达式 | WITH CHECK 表达式 | USING 表达式 | |
SELECT / COPY ... TO | 筛选现有行 | — | — | — | — |
SELECT FOR UPDATE/SHARE | 筛选现有行 | — | 筛选现有行 | — | — |
INSERT | 检查新行 [a] | 检查新行 | — | — | — |
UPDATE | 筛选现有行 [a]并检查新行 [a] | — | 筛选现有行 | 检查新行 | — |
DELETE | 筛选现有行 [a] | — | — | — | 筛选现有行 |
INSERT ... ON CONFLICT | 检查新行 [b][c] | 检查新行 [c] | — | — | — |
ON CONFLICT DO UPDATE | 检查现有行和新行 [d] | — | 检查现有行 | 检查新行 [d] | — |
MERGE | 筛选源行和目标行 | — | — | — | — |
MERGE ... THEN INSERT | 检查新行 [a] | 检查新行 | — | — | — |
MERGE ... THEN UPDATE | 检查新行 | — | 检查现有行 | 检查新行 | — |
MERGE ... THEN DELETE | — | — | — | — | 检查现有行 |
[a] 如果需要读取现有行或新行,例如在 [b] 如果指定了仲裁索引或约束。 [c] 无论最终是否实际发生冲突,提议插入的行都会被检查。 [d] 指辅助 | |||||
多条策略的应用
当不同命令类型的多条策略应用于同一命令时(例如 SELECT和UPDATE策略应用于 UPDATE命令),用户必须同时具有这两种权限 (例如既有从该关系中选取行的权限,也有更新这些行的权限)。因此, 一种策略类型的表达式会与另一种策略类型的表达式使用 AND操作符组合。
当同一命令类型的多条策略应用于同一命令时,必须至少有一条 PERMISSIVE策略授予对该关系的访问权,并且所有 RESTRICTIVE策略都必须通过。因此,所有 PERMISSIVE策略表达式使用OR 组合,所有RESTRICTIVE策略表达式使用 AND组合,然后再将两者的结果使用 AND组合。如果没有 PERMISSIVE策略,则访问被拒绝。
请注意,就组合多条策略而言,ALL策略会被视为与 当前正在应用的其他策略同一类型。
例如,对于一个同时需要SELECT和 UPDATE权限的UPDATE命令, 如果这两类策略各自都有多个适用项,它们会按以下方式组合:
expressionfrom RESTRICTIVE SELECT/ALL policy 1 ANDexpressionfrom RESTRICTIVE SELECT/ALL policy 2 AND ... AND (expressionfrom PERMISSIVE SELECT/ALL policy 1 ORexpressionfrom PERMISSIVE SELECT/ALL policy 2 OR ... ) ANDexpressionfrom RESTRICTIVE UPDATE/ALL policy 1 ANDexpressionfrom RESTRICTIVE UPDATE/ALL policy 2 AND ... AND (expressionfrom PERMISSIVE UPDATE/ALL policy 1 ORexpressionfrom PERMISSIVE UPDATE/ALL policy 2 OR ... )
注解
要为一个表创建或修改策略,你必须是该表的拥有者。
虽然策略会应用于针对数据库中表的显式查询,但当系统执行内部引用 完整性检查或验证约束时,并不会应用这些策略。这意味着仍然存在间接判断 某个给定值是否存在的方法。一个例子是,尝试向某个主键列或带有唯一约束 的列插入重复值。如果插入失败,用户就能推断该值已经存在。(这个例子 假定策略允许该用户插入自己无权看见的行。)另一个例子是,用户被允许 向一个引用了另一张表的表中插入数据,而被引用的那张表本身对其是隐藏 的。用户可以通过向引用表插入值来判断其存在性;插入成功就表示该值 存在于被引用表中。要解决这些问题,可以仔细设计策略,防止用户插入、 删除或更新那些可能暗示其本来无权看见的值是否存在的行,或者改用生成 的值(例如代理键)来代替具有外部含义的键。
通常,为了防止受保护的数据无意间暴露给可能不可信的用户定义函数, 系统会在应用用户查询中出现的条件之前,先强制执行安全策略施加的 过滤条件。不过,被系统(或系统管理员)标记为 LEAKPROOF的函数和操作符由于被假定为可信,可以在 策略表达式之前求值。
由于策略表达式会被直接添加到用户查询中,因此它们将以执行整个查询的 用户权限运行。因此,使用某条策略的用户必须能够访问表达式中引用的 所有表或函数,否则在尝试查询启用了行级安全性的表时,只会收到权限被 拒绝的错误。不过,这并不改变视图的工作方式。与普通查询和视图一样, 被视图引用的表的权限检查和策略将使用视图所有者的权限,以及适用于 视图所有者的任何策略;但如果视图是用 security_invoker选项定义的,则例外(见 CREATE VIEW)。
对于MERGE,不存在单独的策略。相反,执行 MERGE时会根据实际执行的动作,应用为 SELECT、INSERT、 UPDATE和DELETE定义的策略。
更多讨论和实际示例见Section 5.9。
兼容性
CREATE POLICY是一种PostgreSQL扩展。
另见
语法演化
相邻大版本之间的差异,新的在前。版本号链接到对应快照。
PostgreSQL 20← 19正文更新
正文更新PostgreSQL 19← 18正文更新
正文更新PostgreSQL 18← 17正文更新
正文更新PostgreSQL 17← 16正文更新
正文更新PostgreSQL 15← 14正文更新
正文更新PostgreSQL 14← 13语法变化
+ [ TO { role_name | PUBLIC | CURRENT_ROLE | CURRENT_USER | SESSION_USER } [, ...] ]− [ TO { role_name | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ]正文更新PostgreSQL 13← 12正文更新
正文更新PostgreSQL 12← 11正文更新
正文更新PostgreSQL 11← 10正文更新
正文更新
同组命令
| 命令 | 动词 | 对象 | 版本变动 | 最近变更 |
|---|---|---|---|---|
GRANT | GRANT | — | 204 次 | |
| 定义访问权限 | 现存 | |||
REVOKE | REVOKE | — | 204 次 | |
| 撤销访问权限 | 现存 | |||
ALTER DEFAULT PRIVILEGES | ALTER | DEFAULT PRIVILEGES | 143 次 | |
| 定义默认访问权限 | 现存 | |||
CREATE GROUP | CREATE | GROUP | 192 次 | |
| 定义一个新的数据库角色 | 现存 | |||
ALTER GROUP | ALTER | GROUP | 141 次 | |
| 更改角色名称或成员资格 | 现存 | |||
DROP GROUP | DROP | GROUP | — | |
| 移除一个数据库角色 | 现存 | |||
SECURITY LABEL | SECURITY | LABEL | 122 次 | |
| 定义或更改应用于对象的安全标签 | 现存 | |||
DROP OWNED | DROP | OWNED | 141 次 | |
| 移除一个数据库角色所拥有的数据库对象 | 现存 | |||
REASSIGN OWNED | REASSIGN | OWNED | 141 次 | |
| 更改一个数据库角色所拥有的数据库对象的所有权 | 现存 | |||
CREATE POLICY | CREATE | POLICY | 141 次 | |
| 为一个表定义一条新的行级安全性策略 | 现存 | |||
ALTER POLICY | ALTER | POLICY | 141 次 | |
| 更改一条行级安全性策略的定义 | 现存 | |||
DROP POLICY | DROP | POLICY | — | |
| 从一个表中移除一条行级安全性策略 | 现存 | |||
CREATE ROLE | CREATE | ROLE | 142 次 | |
| 定义一个新的数据库角色 | 现存 | |||
ALTER ROLE | ALTER | ROLE | 142 次 | |
| 更改数据库角色 | 现存 | |||
DROP ROLE | DROP | ROLE | — | |
| 移除一个数据库角色 | 现存 | |||
SET ROLE | SET | ROLE | — | |
| 设置当前会话的当前用户标识符 | 现存 | |||
SET SESSION AUTHORIZATION | SET | SESSION AUTHORIZATION | — | |
| 设置当前会话的会话用户标识符和当前用户标识符 | 现存 | |||
CREATE USER | CREATE | USER | 192 次 | |
| 定义一个新的数据库角色 | 现存 | |||
ALTER USER | ALTER | USER | 142 次 | |
| 更改数据库角色 | 现存 | |||
DROP USER | DROP | USER | — | |
| 移除一个数据库角色 | 现存 | |||