@ -49,6 +49,8 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
in <literal>WITH CHECK</literal>. When a <literal>USING</literal>
in <literal>WITH CHECK</literal>. When a <literal>USING</literal>
expression returns true for a given row then that row is visible to the
expression returns true for a given row then that row is visible to the
user, while if false or null is returned then the row is not visible.
user, while if false or null is returned then the row is not visible.
Typically, no error occurs when a row is not visible, but see
<xref linkend="sql-createpolicy-summary"/> for exceptions.
When a <literal>WITH CHECK</literal> expression returns true for a row
When a <literal>WITH CHECK</literal> expression returns true for a row
then that row is inserted or updated, while if false or null is returned
then that row is inserted or updated, while if false or null is returned
then an error occurs.
then an error occurs.
@ -193,8 +195,9 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
rows for which the expression returns false or null will not be
rows for which the expression returns false or null will not be
visible to the user (in a <command>SELECT</command>), and will not be
visible to the user (in a <command>SELECT</command>), and will not be
available for modification (in an <command>UPDATE</command>
available for modification (in an <command>UPDATE</command>
or <command>DELETE</command>). Such rows are silently suppressed; no error
or <command>DELETE</command>). Typically, such rows are silently
is reported.
suppressed; no error is reported (but see
<xref linkend="sql-createpolicy-summary"/> for exceptions).
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
@ -250,8 +253,10 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
otherwise). If an <command>INSERT</command>
otherwise). If an <command>INSERT</command>
or <command>UPDATE</command> command attempts to add rows to the
or <command>UPDATE</command> command attempts to add rows to the
table that do not pass the <literal>ALL</literal>
table that do not pass the <literal>ALL</literal>
policy's <literal>WITH CHECK</literal> expression, the entire
policy's <literal>WITH CHECK</literal> expression (or its
command will be aborted.
<literal>USING</literal> expression, if it does not have a
<literal>WITH CHECK</literal> expression), the entire command will
be aborted.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
@ -267,11 +272,39 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
relation that pass the <literal>SELECT</literal> policy will be
relation that pass the <literal>SELECT</literal> policy will be
returned during a <literal>SELECT</literal> query, and that queries
returned during a <literal>SELECT</literal> query, and that queries
that require <literal>SELECT</literal> permissions, such as
that require <literal>SELECT</literal> permissions, such as
<literal>UPDATE</literal>, will also only see those records
<literal>UPDATE</literal> and <literal>DELETE</literal>,
will also only see those records
that are allowed by the <literal>SELECT</literal> policy.
that are allowed by the <literal>SELECT</literal> policy.
A <literal>SELECT</literal> policy cannot have a <literal>WITH
A <literal>SELECT</literal> policy cannot have a <literal>WITH
CHECK</literal> expression, as it only applies in cases where
CHECK</literal> expression, as it only applies in cases where
records are being retrieved from the relation.
records are being retrieved from the relation, except as described
below.
</para>
<para>
If a data-modifying query has a <literal>RETURNING</literal> clause,
<literal>SELECT</literal> permissions are required on the relation,
and any newly inserted or updated rows from the relation must satisfy
the relation's <literal>SELECT</literal> policies in order to be
available to the <literal>RETURNING</literal> clause. If a newly
inserted or updated row does not satisfy the relation's
<literal>SELECT</literal> policies, an error will be thrown (inserted
or updated rows to be returned are <emphasis>never</emphasis>
silently ignored).
</para>
<para>
If an <literal>INSERT</literal> has an <literal>ON CONFLICT DO
NOTHING/UPDATE</literal> clause, <literal>SELECT</literal>
permissions are required on the relation, and the rows proposed for
insertion are checked using the relation's <literal>SELECT</literal>
policies. If a row proposed for insertion does not satisfy the
relation's <literal>SELECT</literal> policies, an error is thrown
(the <literal>INSERT</literal> is <emphasis>never</emphasis> silently
avoided). In addition, if the <literal>UPDATE</literal> path is
taken, the row to be updated and the new updated row are checked
against the relation's <literal>SELECT</literal> policies, and an
error is thrown if they are not satisfied (an auxiliary
<literal>UPDATE</literal> is <emphasis>never</emphasis> silently
avoided).
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
@ -289,10 +322,11 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
where records are being added to the relation.
where records are being added to the relation.
</para>
</para>
<para>
<para>
Note that <literal>INSERT</literal> with <literal>ON CONFLICT DO
Note that an <literal>INSERT</literal> with an <literal>ON CONFLICT
UPDATE</literal> checks <literal>INSERT</literal> policies'
DO NOTHING/UPDATE</literal> clause will check the
<literal>WITH CHECK</literal> expressions only for rows appended
<literal>INSERT</literal> policies' <literal>WITH CHECK</literal>
to the relation by the <literal>INSERT</literal> path.
expressions for all rows proposed for insertion, regardless of
whether or not they end up being inserted.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
@ -363,10 +397,10 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
Using <literal>DELETE</literal> for a policy means that it will apply
Using <literal>DELETE</literal> for a policy means that it will apply
to <literal>DELETE</literal> commands. Only rows that pass this
to <literal>DELETE</literal> commands. Only rows that pass this
policy will be seen by a <literal>DELETE</literal> command. There can
policy will be seen by a <literal>DELETE</literal> command. There can
be rows that are visible through a <literal>SELECT</literal> that are
be rows that are visible through a <literal>SELECT</literal> policy
not available for deletion, if they do not pass the
that are not available for deletion, if they do not pass the
<literal>USING</literal> expression for
<literal>USING</literal> expression for the <literal>DELETE</literal>
the <literal>DELETE</literal> policy.
policy.
</para>
</para>
<para>
<para>
@ -395,6 +429,15 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
</variablelist>
</variablelist>
<para>
<xref linkend="sql-createpolicy-summary"/> summarizes how the different
types of policy apply to specific commands. In the table,
<quote>check</quote> means that the policy expression is checked and an
error is thrown if it returns false or null, whereas <quote>filter</quote>
means that the row is silently ignored if the policy expression returns
false or null.
</para>
<table id="sql-createpolicy-summary">
<table id="sql-createpolicy-summary">
<title>Policies Applied by Command Type</title>
<title>Policies Applied by Command Type</title>
<tgroup cols="6">
<tgroup cols="6">
@ -419,8 +462,8 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
</thead>
</thead>
<tbody>
<tbody>
<row>
<row>
<entry><command>SELECT</command></entry>
<entry><command>SELECT</command> / <command>COPY ... TO</command> </entry>
<entry>E xisting row</entry>
<entry>Filter e xisting row</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
@ -428,32 +471,24 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
</row>
</row>
<row>
<row>
<entry><command>SELECT FOR UPDATE/SHARE</command></entry>
<entry><command>SELECT FOR UPDATE/SHARE</command></entry>
<entry>E xisting row</entry>
<entry>Filter e xisting row</entry>
<entry>—</entry>
<entry>—</entry>
<entry>E xisting row</entry>
<entry>Filter e xisting row</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
</row>
</row>
<row>
<row>
<entry><command>INSERT</command></entry>
<entry><command>INSERT</command></entry>
<entry>—</entry>
<entry>New row</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
</row>
<row>
<entry><command>INSERT ... RETURNING</command></entry>
<entry>
<entry>
New row <footnote id="rls-select-priv">
Check new row <footnote id="rls-select-priv">
<para>
<para>
If read access is required to the existing or new row (for example,
If read access is required to either the existing or new row (for
a <literal>WHERE</literal> or <literal>RETURNING</literal> clause
example, a <literal>WHERE</literal> or <literal>RETURNING</literal>
that refers to columns from the relation).
clause that refers to columns from the relation).
</para>
</para>
</footnote>
</footnote>
</entry>
</entry>
<entry>N ew row</entry>
<entry>Check n ew row</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
@ -461,29 +496,57 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
<row>
<row>
<entry><command>UPDATE</command></entry>
<entry><command>UPDATE</command></entry>
<entry>
<entry>
Existing & new rows <footnoteref linkend="rls-select-priv"/>
Filter existing row <footnoteref linkend="rls-select-priv"/> &
check new row <footnoteref linkend="rls-select-priv"/>
</entry>
</entry>
<entry>—</entry>
<entry>—</entry>
<entry>E xisting row</entry>
<entry>Filter e xisting row</entry>
<entry>N ew row</entry>
<entry>Check n ew row</entry>
<entry>—</entry>
<entry>—</entry>
</row>
</row>
<row>
<row>
<entry><command>DELETE</command></entry>
<entry><command>DELETE</command></entry>
<entry>
<entry>
Existing row <footnoteref linkend="rls-select-priv"/>
Filter existing row <footnoteref linkend="rls-select-priv"/>
</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>Filter existing row</entry>
</row>
<row>
<entry><command>INSERT ... ON CONFLICT</command></entry>
<entry>
Check new row <footnote id="rls-on-conflict-priv">
<para>
Row proposed for insertion is checked regardless of whether or not a
conflict occurs.
</para>
</footnote>
</entry>
<entry>
Check new row <footnoteref linkend="rls-on-conflict-priv"/>
</entry>
</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
<entry>Existing row</entry>
</row>
</row>
<row>
<row>
<entry><command>ON CONFLICT DO UPDATE</command></entry>
<entry><command>ON CONFLICT DO UPDATE</command></entry>
<entry>Existing & new rows</entry>
<entry>
Check existing & new rows <footnote id="rls-on-conflict-update-priv">
<para>
New row of the auxiliary <command>UPDATE</command> command, which
might be different from the new row of the original
<command>INSERT</command> command.
</para>
</footnote>
</entry>
<entry>—</entry>
<entry>—</entry>
<entry>Existing row</entry>
<entry>Check existing row</entry>
<entry>New row</entry>
<entry>
Check new row <footnoteref linkend="rls-on-conflict-update-priv"/>
</entry>
<entry>—</entry>
<entry>—</entry>
</row>
</row>
</tbody>
</tbody>