CREATE POLICY — テーブルに新しい行単位のセキュリティポリシーを定義する
CREATE POLICYname
ONtable_name
[ AS { PERMISSIVE | RESTRICTIVE } ] [ 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
を使って)テーブルの行単位セキュリティを有効にしなければならないことに注意して下さい。
ポリシーは、それを定義する式にマッチした行をselect/insert/update/deleteする権限を与えます。
テーブルの既存の行はUSING
で指定した式によって検査されます。
INSERT
またはUPDATE
によって作成される新しい行はWITH CHECK
で指定した式によって検査されます。
ある行についてUSING
式がtrueを返した場合、その行はユーザに可視となりますが、falseまたはnullを返した場合は不可視となります。
行に対してWITH CHECK
式がtrueを返した場合、その行は挿入または更新されますが、falseまたはnullを返した場合はエラーが発生します。
INSERT
文およびUPDATE
文では、BEFORE
トリガーが実行された後で、かつ、実際のデータ更新が行われるより前にWITH CHECK
式が実行されます。
従って、BEFORE ROW
トリガーは挿入されるデータを変更する場合があり、これはセキュリティポリシーの検査の結果に影響を与えます。
WITH CHECK
式は他のいかなる制約よりも前に実行されます。
ポリシー名はテーブル毎につけられます。 従って、1つのポリシー名を多くの異なるテーブルに使うことができます。 また、その定義は各テーブル毎に異なった、適切な内容にできます。
ポリシーは特定のコマンドまたは特定のロールに対して適用することができます。 新しく作成するポリシーのデフォルトは、特に指定しなければ、すべてのコマンドとロールに適用、となっています。 複数のポリシーを単一のコマンドに適用できます。 更なる詳細は以下を参照ください。 表 272に、どのようにして、特定のコマンドに異なるタイプのポリシーが適用されるかがまとめられています。
USING
式とWITH CHECK
式の両方を持つことができるポリシー(ALL
とUPDATE
)についてWITH CHECK
式が定義されていない場合、どの行が可視であるかの決定(通常のUSING
の対象)と新しい行のどれが追加可能であるかの決定(WITH CHECK
の対象)の両方でUSING
式が使用されます。
テーブルの行単位セキュリティが有効で、適用可能なポリシーが存在しない場合、「デフォルト拒否」のポリシーが適用され、すべての行が不可視で更新不能になります。
name
作成するポリシーの名前です。 同じテーブルの他のポリシーとは異なる名前でなければなりません。
table_name
ポリシーが適用されるテーブルの名前(スキーマ修飾可)です。
PERMISSIVE
作成するポリシーが許容(permissive)ポリシーであることを指定します。 問い合わせに適用可能なすべての許容ポリシーは論理演算子「OR」を使って結合されます。 許容ポリシーを作成することで、管理者はアクセス可能なレコード集合を追加することができます。 デフォルトではポリシーは許容ポリシーです。
RESTRICTIVE
作成するポリシーが制限(restrictive)ポリシーであることを指定します。 問い合わせに適用可能なすべての制限ポリシーは論理演算子「AND」と使って結合されます。 各行についてすべての制限ポリシーを満たさなければならなくなるため、制限ポリシーを作成することで、管理者はアクセス可能なレコード集合を減らすことができます。
制限ポリシーを有効に使ってアクセスを制限できるようにする前に、レコードへのアクセスを許可する許容ポリシーが少なくとも1つ必要であることに注意してください。 制限ポリシーだけしか存在しない場合、レコードにアクセスすることはできません。 許容ポリシーと制限ポリシーが混在している場合、少なくとも1つの許容ポリシーを満たし、さらに、すべての制限ポリシーを満たしている場合のみレコードにアクセスできます。
command
ポリシーが適用されるコマンドです。
有効なオプションはALL
、SELECT
、INSERT
、UPDATE
、DELETE
です。
デフォルトはALL
です。
これらがどのように適用されるかの詳細は以下を参照して下さい。
role_name
ポリシーが適用されるロールです。
デフォルトはPUBLIC
で、すべてのロールに対してポリシーが適用されます。
using_expression
任意のSQL条件式(戻り値はboolean
)です。
条件式に集約関数やウィンドウ関数を含めることはできません。
行単位セキュリティが有効なときは、テーブルへの問い合わせにこの式が追加されます。
この式がtrueを返す行が可視となります。
この式がfalseまたはnullを返す行は、ユーザには(SELECT
において)不可視となり、また(UPDATE
あるいはDELETE
では)更新の対象ではなくなります。
そのような行は静かに無視され、エラーは報告されません。
check_expression
任意のSQL条件式(戻り値はboolean
)です。
条件式に集約関数やウィンドウ関数を含めることはできません。
この式は、そのテーブルに対するINSERT
およびUPDATE
の問い合わせで使用され、この式の評価がtrueになる行のみが許されます。
挿入されるレコード、あるいは更新の結果のレコードでこの式の評価がfalseまたはnullになるものについては、エラーが発生します。
check_expression
は元の内容ではなく、予定される更新の後の新しい内容に対して評価されることに注意してください。
ALL
ポリシーにALL
を使うのは、そのポリシーはコマンドの種類に関係なく、すべてのコマンドに適用されるという意味になります。
ALL
のポリシーと特定のコマンドに対するポリシーの両方が存在する場合、ALL
のポリシーと特定のコマンドに対するポリシーの両方が適用されます。
さらにALL
のポリシーは、問い合わせの選択側と更新側の両方で適用されます。
このとき、USING
式だけが定義されていたら、両方の場合についてUSING
式を使用します。
例えばUPDATE
が実行されるとき、ALL
のポリシーは、UPDATE
が更新対象の行として選択できる行(USING
式が適用されます)と、UPDATE
文の結果としてできる行がテーブルに追加できるかどうかの検証(WITH CHECK
が定義されていれば、それが適用され、なければUSING
式が適用されます)の両方で適用可能です。
INSERT
またはUPDATE
コマンドがALL
のWITH CHECK
式に反する行をテーブルに追加しようとした場合、コマンド全体が中止されます。
SELECT
ポリシーにSELECT
を使うのは、そのポリシーはSELECT
の問い合わせの他に、そのポリシーが定義されているリレーションに対してSELECT
権限が必要な時は常に適用されるという意味になります。
その結果、SELECT
問い合わせでは、SELECT
ポリシーに適うレコードだけが返されます。
また、UPDATE
などSELECT
権限が必要な問い合わせでも、SELECT
ポリシーによって許可されるレコードだけが見えます。
SELECT
ポリシーはリレーションからレコードを取り出す場合にしか適用されないので、WITH CHECK
式を持つことはできません。
INSERT
ポリシーにINSERT
を使うのは、そのポリシーはINSERT
コマンドに適用されるという意味になります。
このポリシーに反する行を挿入しようとすると、ポリシー違反エラーを起こし、INSERT
コマンド全体が中止されます。
INSERT
ポリシーはリレーションにレコードを追加する場合にしか適用されないため、USING
式を持つことはできません。
ON CONFLICT DO UPDATE
のあるINSERT
では、INSERT
ポリシーのWITH CHECK
式について、INSERT
の部分でリレーションに追加されるすべての行についてのみ確認することに注意してください。
UPDATE
ポリシーにUPDATE
を使うのは、そのポリシーはUPDATE
コマンド、SELECT FOR UPDATE
コマンド、SELECT FOR SHARE
コマンド、および補助的にINSERT
コマンドのON CONFLICT DO UPDATE
句で適用されるという意味になります。
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
の部分が警告なしに回避されることは決してありません)。
DELETE
ポリシーにDELETE
を使うのは、そのポリシーはDELETE
コマンドに適用されるという意味になります。
ポリシーを満たす行だけがDELETE
コマンドから見えます。
SELECT
では見えるけれど、削除の対象ではない、という行もあり得ます。
それらの行がDELETE
ポリシーのUSING
式を満たさない場合です。
ほとんどの場合、DELETE
コマンドは削除対象のリレーションの列からデータを読む必要もあります(例えば、WHERE
句の中やRETURNING
句)。
この場合、リレーション上のSELECT
権限も必要となり、DELETE
ポリシーに加えて、適切なSELECT
ポリシーまたはALL
ポリシーも適用されます。
従って、ユーザはDELETE
またはALL
ポリシーによって、行を削除する権限を付与されているのに加えて、SELECT
またはALL
ポリシーによって、削除対象の行にアクセスできなければなりません。
DELETE
ポリシーはリレーションからレコードが削除される場合にしか適用されず、確認すべき新しい行はないので、WITH CHECK
式を持つことはできません。
表272 コマンドタイプにより適用されるポリシー
コマンド | SELECT/ALLポリシー | INSERT/ALLポリシー | UPDATE/ALLポリシー | DELETE/ALLポリシー | |
---|---|---|---|---|---|
USING式 | WITH CHECK式 | USING式 | WITH CHECK式 | USING式 | |
SELECT | 既存の行 | — | — | — | — |
SELECT FOR UPDATE/SHARE | 既存の行 | — | 既存の行 | — | — |
INSERT | — | 新しい行 | — | — | — |
INSERT ... RETURNING | 新しい行 [a] | 新しい行 | — | — | — |
UPDATE | 既存の行と新しい行 [a] | — | 既存の行 | 新しい行 | — |
DELETE | 既存の行 [a] | — | — | — | 既存の行 |
ON CONFLICT DO UPDATE | 既存の行と新しい行 | — | 既存の行 | 新しい行 | — |
[a]
読み出しアクセスが既存の、あるいは新しい行(たとえば、リレーションのカラムを参照する |
同じコマンドに対して、異なるコマンド種別の複数のポリシーを適用する場合(例えば、UPDATE
コマンドに対してはSELECT
とUPDATE
のポリシーが適用されます)、ユーザは両方の種別の権限(例えば、リレーションから行を検索する権限と、それを更新する権限)を持っている必要があります。
従って、ある種別のポリシーの式は、別の種別のポリシーの式とAND
演算子を使って結合されます。
同じコマンドに対して同じコマンド種別の複数のポリシーが適用される場合、リレーションのアクセスを許可する少なくとも1つのPERMISSIVE
ポリシーがなければならず、さらにすべてのRESTRICTIVE
ポリシーを満たす必要があります。
従って、すべてのPERMISSIVE
ポリシー式がOR
を使って結合され、すべてのRESTRICTIVE
ポリシー式がAND
を使って結合され、その結果がAND
を使って結合されます。
PERMISSIVE
ポリシーがなければアクセスは拒絶されます。
複数のポリシーを結合するという目的において、ALL
のポリシーは適用対象となっている他のすべてのポリシーと同じ種別であるとして扱われることに注意してください。
例えば、SELECT
とUPDATE
の両方の権限を必要とするUPDATE
コマンドにおいて、それぞれの種別の適用可能な複数のポリシーがある場合、以下のように結合されます。
expression
from RESTRICTIVE SELECT/ALL policy 1 ANDexpression
from RESTRICTIVE SELECT/ALL policy 2 AND ... AND (expression
from PERMISSIVE SELECT/ALL policy 1 ORexpression
from PERMISSIVE SELECT/ALL policy 2 OR ... ) ANDexpression
from RESTRICTIVE UPDATE/ALL policy 1 ANDexpression
from RESTRICTIVE UPDATE/ALL policy 2 AND ... AND (expression
from PERMISSIVE UPDATE/ALL policy 1 ORexpression
from PERMISSIVE UPDATE/ALL policy 2 OR ... )
ポリシーを作成あるいは変更するには、テーブルの所有者でなければなりません。
ポリシーは、データベース内のテーブルに対する明示的な問い合わせには適用されますが、システムが内部的な参照整合性のチェックや制約の検証をしている時には適用されません。 この意味するところは、ある値が存在するかどうかを判定する間接的な方法がある、ということです。 その例の1つは、主キーあるいは一意制約のある列に重複する値を挿入しようとすることです。 挿入に失敗すれば、その値が既に存在すると推定することができます。 (この例では、ユーザが見ることができないレコードを挿入することがポリシーにより許されていると仮定しています。) 別の例は、あるテーブルへの挿入は許されているが、そのテーブルが別の隠されているテーブルを参照している、という場合です。 参照元のテーブルに値を挿入することで、値の存在が判断できます。 この場合、挿入の成功はその値が参照先のテーブルに存在することを示唆します。 これらの問題は、見ることができない値を示唆するかもしれないようなレコードの挿入、削除、更新が全くできないように注意深くポリシーを設計するか、あるいは外部的な意味を持つキーの代わりに生成された値(例:代理キー)を使うことで解決できます。
一般に、システムは問い合わせに記述される制限より前に、セキュリティポリシーを使ったフィルター条件を実行します。
これは守られるべきデータが信頼できないかもしれないユーザ定義関数に偶然に意図せずに渡されることを防ぐためです。
しかし、システム(またはシステム管理者)によってLEAKPROOF
であるとされた関数や演算子については、信頼できるとみなして良いので、ポリシー式より先に評価される場合があります。
ポリシーの式はユーザの問い合わせに直接追加されるため、式は問い合わせ全体を実行しているユーザの権限によって実行されます。 そのため、あるポリシーを使用するユーザは、その式が参照しているすべてのテーブルおよび関数にアクセスできる必要があります。 そうでなければ、行単位セキュリティが有効になっているテーブルに問い合わせをしようとしたときに、単に権限なしのエラーを受け取ります。 しかし、これによってビューの動作が変わることはありません。 通常の問い合わせおよびビューと同じく、ビューによって参照されるテーブルに対する権限の確認とポリシーは、ビューの所有者の権限およびビューの所有者に適用されるポリシーを利用します。
更なる詳細と実践的な例については5.8に記述されています。
CREATE POLICY
はPostgreSQLの拡張です。