1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Fix RLS checks for FOR PORTION OF leftover rows

UPDATE/DELETE FOR PORTION OF may insert leftover rows to preserve the
parts of the old row that are outside the target range.  Those inserts
go through ExecInsert(), which checks RLS policies using
WCO_RLS_INSERT_CHECK.

However, the rewriter only added RLS WITH CHECK options for the
original statement command.  For UPDATE, that meant only
WCO_RLS_UPDATE_CHECK options were available, so ExecInsert() skipped
them.  For DELETE, no RLS WITH CHECK options were added at all.  As a
result, leftover rows could be inserted even when they violated INSERT
RLS policies.

Fix this by adding INSERT RLS WITH CHECK options for UPDATE/DELETE FOR
PORTION OF target relations.  Also add regression coverage for both
UPDATE and DELETE, including cases where allowed leftovers still
succeed and disallowed leftovers are rejected.

Author: Chao Li <lic@highgo.com>
Co-authored-by: Paul A Jungwirth <pj@illuminatedcomputing.com>
Reviewed-by: Paul A Jungwirth <pj@illuminatedcomputing.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/6C34A987-AC50-4477-BD71-2D4AFEE1A589%40gmail.com
Discussion: https://www.postgresql.org/message-id/flat/CAJTYsWWdeBkoH5g8D-k9LDw9ciqsMxb21EJSiFXAzP4J%3DXyxOQ%40mail.gmail.com
This commit is contained in:
Peter Eisentraut
2026-07-20 08:38:08 +02:00
parent a8c9d2be28
commit c58c83ce50
8 changed files with 264 additions and 4 deletions

View File

@@ -530,6 +530,26 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
<entry>Check new row</entry>
<entry>&mdash;</entry>
</row>
<row>
<entry><command>UPDATE ... FOR PORTION OF</command></entry>
<entry>
Filter existing row &amp; check new row
</entry>
<entry>
Check leftover rows&nbsp;<footnote id="rls-fpo-leftover">
<para>
<literal>FOR PORTION OF</literal> re-inserts the portions of the
affected row that fall outside the targeted range, to preserve them.
These leftover rows are checked against the <command>INSERT</command>
policy's <literal>WITH CHECK</literal> expression, even though no
<literal>INSERT</literal> privilege is required to store them.
</para>
</footnote>
</entry>
<entry>Filter existing row</entry>
<entry>Check new row</entry>
<entry>&mdash;</entry>
</row>
<row>
<entry><command>DELETE</command></entry>
<entry>
@@ -540,6 +560,18 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
<entry>&mdash;</entry>
<entry>Filter existing row</entry>
</row>
<row>
<entry><command>DELETE ... FOR PORTION OF</command></entry>
<entry>
Filter existing row
</entry>
<entry>
Check leftover rows&nbsp;<footnoteref linkend="rls-fpo-leftover"/>
</entry>
<entry>&mdash;</entry>
<entry>&mdash;</entry>
<entry>Filter existing row</entry>
</row>
<row>
<entry><command>INSERT ... ON CONFLICT</command></entry>
<entry>

View File

@@ -97,7 +97,8 @@ DELETE FROM [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ *
When <literal>FOR PORTION OF</literal> is used, the secondary inserts do
not require <literal>INSERT</literal> privilege on the table. (This is
because conceptually no new information is being added; the inserted rows
only preserve existing data about the untargeted time period.)
only preserve existing data about the untargeted time period.) Row-level
security INSERT policies are still checked for these leftover inserts.
</para>
</refsect1>

View File

@@ -101,7 +101,8 @@ UPDATE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
When <literal>FOR PORTION OF</literal> is used, the secondary inserts do
not require <literal>INSERT</literal> privilege on the table. (This is
because conceptually no new information is being added; the inserted rows
only preserve existing data about the untargeted time period.)
only preserve existing data about the untargeted time period.) Row-level
security INSERT policies are still checked for these leftover inserts.
</para>
</refsect1>