CREATE POLICY name ON table_name [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ] [ TO { role_name | PUBLIC | 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表达式对于一个给定行返回真时,该行对用户 可见,而返回假或空时该行不可见。当一个WITH CHECK 表达式对一行返回真时,该行会被插入或更新,而如果返回假或空时会发生 一个错误。
对于INSERT和 UPDATE语句,在BEFORE 触发器被引发后并且在任何数据修改真正发生之前, WITH CHECK表达式会被强制。因此, 一个BEFORE ROW触发器可以修改要被插入的数据, 从而影响安全性策略检查的结果。WITH CHECK表达式 在任何其他约束之前被强制。
策略名称是针对每个表的。因此,一个策略名称可以被用于很多个不同的表 并且对于不同的表呈现适合于该表的定义。
策略可以被应用于特定的命令或者特定的角色。除非特别指定,新创建的策略 的默认行为是适用于所有命令和角色。如果多条策略适用于一个给定的语句, 将用 OR 把它们组合起来(不过ON CONFLICT DO UPDATE 和 INSERT策略不用这种方式组合,而是在 ON CONFLICT执行的每一个阶段进行强制)。
对同时具有USING和WITH CHECK 策略(ALL和UPDATE)的命令, 如果没有定义WITH CHECK策略,那么 USING策略将被用于决定哪些行可见(普通 USING情况)以及允许哪些行被增加( WITH CHECK情况)。
如果为一个表启用了行级安全性但是没有适用的策略存在,将假定为一种 "默认否定"策略,这样任何行都不可见也不可更新。
要创建的策略的名称。这必须和该表上已有的任何其他策略名称相区分。
该策略适用的表的名称(可以被模式限定)。
该策略适用的命令。合法的选项是 ALL、SELECT、 INSERT、UPDATE 以及DELETE。 ALL为默认。有关这些策略如何被应用的 细节见下文。
该策略适用的角色。默认是PUBLIC,它将把策略应用 到所有的角色。
任意的SQL条件表达式(返回 boolean)。该条件表达式不能包含任何聚集或者窗口 函数。如果行级安全性被启用,这个表达式将被增加到引用该表的查询。 让这个表达式返回真的行将可见。让这个表达式返回假或者空的任何行 将对用户不可见(在SELECT中)并且将对修改不可用( 在UPDATE或DELETE中)。这类行 会被悄悄地禁止而不会报告错误。
任意的SQL条件表达式(返回 boolean)。该条件表达式不能包含任何聚集或者窗口 函数。如果行级安全性被启用,这个表达式将被用在该表上的 INSERT以及 UPDATE查询中。只有让该表达式计算为真 的行才被允许。如果任何被插入的记录或者跟新后的记录导致该表达式计 算为假或者空,则会抛出一个错误。注意 check_expression 是根据行的新内容而不是原始内容计算的。
为一条策略使用ALL表示它将适用于所有命令, 不管命令的类型如何。如果存在一条ALL策略 以及更多特定的策略,则ALL策略和那些策略 会被 OR 组合在一起,就像重叠策略一样。此外, ALL策略将同时适用于一个查询的选择端和修 改端,如果只定义了一个USING表达式则将 该USING表达式用于两种情况。
例如,如果发出一个UPDATE,那么 ALL策略将同时影响UPDATE 能更新哪些行(应用USING表达式)以及更新后 的行是否被允许加入到表中(如果定义了WITH CHECK 表达式,则应用之;否则使用USING表达式)。 如果一条INSERT 或者UPDATE命令尝试增加行到表中, 但行没有通过ALL策略的 WITH CHECK表达式,则整个语句将会中断。
对一条策略使用SELECT表示它将适用于 SELECT查询,并且无论何时都要求该约束所在的关系上 的SELECT权限。其结果是在一次 SELECT查询期间,只有该关系中那些通过了 SELECT策略的记录才将被返回,并且查询要求 SELECT权限,例如 UPDATE也将只能看到那些 SELECT策略允许的行。一条 SELECT策略不能具有WITH CHECK表达式,因为它只适用于正在从关系中检索记录的情况。
为一条策略使用INSERT表示它适用于 INSERT命令。没有通过这种策略的正在被插入的行 会导致策略违背错误,并且整个INSERT命令将会中止。 一条INSERT策略不能具有USING 表达式,因为它只适用于正在向关系增加记录的情况。
注意在带有ON CONFLICT DO UPDATE的INSERT中,只有对通过 INSERT路径追加到关系的行才会检查 INSERT策略的WITH CHECK 表达式。
为一条策略使用UPDATE表示它适用于 UPDATE命令(或者INSERT 命令的ON CONFLICT DO UPDATE子句)。因为 UPDATE涉及到取出一个现有的记录然后对该记录 的某个部分(可能不是全部)作出更改,UPDATE 策略同时接受USING表达式和 WITH CHECK表达式。USING 表达式决定UPDATE命令将能看到哪些要对其操作 的记录,而WITH CHECK表达式定义哪些被修改的 行被允许存回到关系中。
当一个UPDATE命令使用了 WHERE子句或者RETURNING 子句时,也要求在被更新的关系上的SELECT权限, 并且合适的SELECT以及ALL策略 将用 AND 和 UPDATE策略的 USING子句组合在一起(找到的任何重叠的 SELECT相关策略之间用 OR 组合)。因此,为了一个 用户能UPDATE特定的行,该用户必须能通过一条 SELECT或者ALL策略 访问那些行并且那些行必须能通过该UPDATE策略的 USING表达式。
任何更新后的值无法通过WITH CHECK表达式的行 将会导致错误,并且整个命令将被中止。如果只指定了一个 USING子句,那么该子句将被用于 USING和WITH CHECK两种情况。
不过要注意的是,带有ON CONFLICT DO UPDATE 的INSERT要求一个UPDATE策略的 USING表达式总是被作为一个 WITH CHECK表达式来强制。当采用 UPDATE路径时,UPDATE策略必须 总是能通过。任何迫使采用UPDATE路径的现有行必须 能通过(UPDATE或ALL的) USING条件(用 OR 组合),在这种环境中这些条件总是 被作为WITH CHECK选项来强制( UPDATE路径将永不可能被避免,而是 会抛出一个错误)。最后,被追加到关系中的最终行必须通过任何 常见的UPDATE被要求通过的 WITH CHECK选项。
为一条策略使用DELETE表示它适用于 DELETE命令。只有通过这条策略的行才将能被 DELETE命令所看到。如果有的行不能通过该 DELETE策略的USING表达式,则 它们可以通过SELECT看到但不能被删除。
当一个DELETE命令使用了 WHERE子句或者RETURNING子句时, 也要求在被更新的关系上的SELECT权限, 并且合适的SELECT以及ALL策略 将用 AND 和 DELETE策略的 USING子句组合在一起(找到的任何重叠的 SELECT相关策略之间用 OR 组合)。 因此,为了一个 用户能DELETE特定的行,该用户必须能通过一条 SELECT或者ALL策略 访问那些行并且那些行必须能通过该DELETE策略的 USING表达式。
DELETE策略不能具有WITH CHECK表达式,因为它只适用于正在从关系中删除记录的情况, 所以没有新行需要检查。
要为一个表创建或者修改策略,你必须是该表的拥有者。
虽然策略将被应用于针对数据库中表的显式查询上,但当系统正在执行 内部引用完整性检查或者验证约束时不会应用它们。这意味着有间接的 方法来决定一个给定的值是否存在。一个例子是向一个作为主键或者拥 有唯一约束的列中尝试插入重复值。如果插入失败则用户可以推导出该 值已经存在(这个例子假设用户被策略允许插入他们看不到的记录)。 另一个例子是一个用户被允许向一个引用了其他表的表中插入,然而另 一个表是隐藏表。通过用户向引用表中插入值可以判断存在性, 成功表示该值存在于被引用表中。为了解决这些问题,应该仔细地制作 策略以完全阻止用户插入、删除或者更新那些可能指示他们不能看到的 值的记录,或者使用生成的值(例如代理键)来代替具有外部含义的键。
通常,系统将在应用用户查询中出现的条件之前先强制由安全性策略施 加的过滤条件,这是为了防止无意中把受保护的数据暴露给可能不可信 的用户定义函数。不过,被系统(或者系统管理员)标记为 LEAKPROOF的函数和操作符可以在策略表达式之前 被计算,因为它们已经被假定为可信。
因为策略表达式会被直接加到用户查询上,它们将使用运行整个查询的用户的 权限运行。因此,使用一条给定策略的用户必须能够访问表达式中引用的任何 表或函数,否则在尝试查询启用了行级安全性的表时,他们将简单地收到一条 没有权限的错误。不过,这不会改变视图的工作方式。就普通查询和视图来说, 权限检查和视图所引用的表的策略将使用视图拥有者的权限以及任何适用于视 图拥有者的策略。
在Section 5.7中可以找到额外的讨论和实际的例子。