@ -49,6 +49,8 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
in <literal>WITH CHECK</literal>. When a <literal>USING</literal>
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.
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
then that row is inserted or updated, while if false or null is returned
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
visible to the user (in a <command>SELECT</command>), and will not be
available for modification (in an <command>UPDATE</command>
or <command>DELETE</command>). Such rows are silently suppressed; no error
is reported.
or <command>DELETE</command>). Typically, such rows are silently
suppressed; no error is reported (but see
<xref linkend="sql-createpolicy-summary"/> for exceptions).
</para>
</listitem>
</varlistentry>
@ -250,8 +253,10 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
otherwise). If an <command>INSERT</command>
or <command>UPDATE</command> command attempts to add rows to the
table that do not pass the <literal>ALL</literal>
policy's <literal>WITH CHECK</literal> expression, the entire
command will be aborted.
policy's <literal>WITH CHECK</literal> expression (or its
<literal>USING</literal> expression, if it does not have a
<literal>WITH CHECK</literal> expression), the entire command will
be aborted.
</para>
</listitem>
</varlistentry>
@ -267,11 +272,39 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
relation that pass the <literal>SELECT</literal> policy will be
returned during a <literal>SELECT</literal> query, and that queries
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.
A <literal>SELECT</literal> policy cannot have a <literal>WITH
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>
</listitem>
</varlistentry>
@ -289,10 +322,11 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
where records are being added to the relation.
</para>
<para>
Note that <literal>INSERT</literal> with <literal>ON CONFLICT DO
UPDATE</literal> checks <literal>INSERT</literal> policies'
<literal>WITH CHECK</literal> expressions only for rows appended
to the relation by the <literal>INSERT</literal> path.
Note that an <literal>INSERT</literal> with an <literal>ON CONFLICT
DO NOTHING/UPDATE</literal> clause will check the
<literal>INSERT</literal> policies' <literal>WITH CHECK</literal>
expressions for all rows proposed for insertion, regardless of
whether or not they end up being inserted.
</para>
</listitem>
</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
to <literal>DELETE</literal> commands. Only rows that pass this
policy will be seen by a <literal>DELETE</literal> command. There can
be rows that are visible through a <literal>SELECT</literal> that are
not available for deletion, if they do not pass the
<literal>USING</literal> expression for
the <literal>DELETE</literal> policy.
be rows that are visible through a <literal>SELECT</literal> policy
that are not available for deletion, if they do not pass the
<literal>USING</literal> expression for the <literal>DELETE</literal>
policy.
</para>
<para>
@ -395,6 +429,15 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
</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">
<title>Policies Applied by Command Type</title>
<tgroup cols="6">
@ -419,8 +462,8 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
</thead>
<tbody>
<row>
<entry><command>SELECT</command></entry>
<entry>E xisting row</entry>
<entry><command>SELECT</command> / <command>COPY ... TO</command> </entry>
<entry>Filter e xisting row</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
@ -428,32 +471,24 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
</row>
<row>
<entry><command>SELECT FOR UPDATE/SHARE</command></entry>
<entry>E xisting row</entry>
<entry>Filter e xisting row</entry>
<entry>—</entry>
<entry>E xisting row</entry>
<entry>Filter e xisting row</entry>
<entry>—</entry>
<entry>—</entry>
</row>
<row>
<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>
New row <footnote id="rls-select-priv">
Check new row <footnote id="rls-select-priv">
<para>
If read access is required to the existing or new row (for example,
a <literal>WHERE</literal> or <literal>RETURNING</literal> clause
that refers to columns from the relation).
If read access is required to either the existing or new row (for
example, a <literal>WHERE</literal> or <literal>RETURNING</literal>
clause that refers to columns from the relation).
</para>
</footnote>
</entry>
<entry>N ew row</entry>
<entry>Check n ew row</entry>
<entry>—</entry>
<entry>—</entry>
<entry>—</entry>
@ -461,29 +496,57 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
<row>
<entry><command>UPDATE</command></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>E xisting row</entry>
<entry>N ew row</entry>
<entry>Filter e xisting row</entry>
<entry>Check n ew row</entry>
<entry>—</entry>
</row>
<row>
<entry><command>DELETE</command></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>Existing row</entry>
</row>
<row>
<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>Existing row</entry>
<entry>New row</entry>
<entry>Check existing row</entry>
<entry>
Check new row <footnoteref linkend="rls-on-conflict-update-priv"/>
</entry>
<entry>—</entry>
</row>
</tbody>