CREATE POLICY — 为一个表定义一条新的行级安全性策略
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 295。当 WITH CHECK表达式对某行返回真时,该行会被插入或 更新;如果返回假或 null,则会报错。
对于INSERT、UPDATE和 MERGE语句, WITH CHECK表达式会在BEFORE 触发器触发后、在进行任何实际数据修改之前强制执行。因此, BEFORE ROW触发器可以修改待插入的数据,从而影响 安全策略检查的结果。WITH CHECK表达式会在任何其他 约束之前执行。
策略名称按表区分。因此,同一个策略名可以用于许多不同的表,并且在 每个表上都可以有适合该表的定义。
策略可以针对特定命令或特定角色应用。除非另有指定,新建策略默认 适用于所有命令和角色。多个策略可以应用于同一命令;更多细节见下文。 Table 295总结了不同类型的策略如何应用于 特定命令。
对于既可以具有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 295)。
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 295总结了不同类型的策略如何应用到特定命令上。 在该表中,“check”表示要检查策略表达式,若其返回假或 null 则抛出错误; 而“filter”表示若策略表达式返回假或 null,则该行会被静默忽略。
Table 295. 按命令类型应用的策略
| 命令 | 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扩展。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。