| From: | jian he <jian(dot)universality(at)gmail(dot)com> |
|---|---|
| To: | dbryan(dot)green(at)gmail(dot)com |
| Cc: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | Re: COPY FROM with RLS |
| Date: | 2026-09-01 07:13:14 |
| Message-ID: | CACJufxEPnQ_wx=-6P6h=haxCO3CHeAijV7eeq3keUZ_Tve-1yw@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
On Thu, Aug 27, 2026 at 9:04 AM <dbryan(dot)green(at)gmail(dot)com> wrote:
>
> Separately, getting the WITH CHECK expressions from a full rewrite of a dummy
> INSERT runs the target's ON INSERT rules on the RLS path, so the same COPY
> behaves differently depending on whether the table has RLS enabled:
>
> COPY into non-RLS table COPY into RLS table
> DO ALSO loads target, rule ignored ERROR (unsupported)
> DO INSTEAD (uncond.) loads the named table loads the redirect target
> DO INSTEAD (cond.) loads target, rule ignored ERROR (unsupported)
>
> The length check rejects the rewrites that fan out past one query, but a
> single unconditional DO INSTEAD passes it, so COPY into an RLS table lands the
> rows in the rule's redirect target rather than the named table.
>
Portion of RLS node processing is inside the query rewriter (pg_rewrite_query),
pg_rewrite_query cannot be skipped.
So the only option is to disallow RLS on tables that have rules, IMHO.
I also made some other miscellaneous changes and addressed the other
issue you mentioned.
In the copy.sgml, I changed it as
<para>
<command>COPY FROM</command> will invoke any triggers and check
constraints on the destination table. However, it will not invoke rules.
+ If row-level security is enabled for the table, rules on the table are
+ not supported.
</para>
Below is the commit message:
Subject: [PATCH v15 1/1] COPY FROM with RLS
Previously, COPY FROM on a table with row-level security enabled failed with
"COPY FROM not supported with row-level security". Now it is fully supported:
the table's INSERT policies are enforced against each copied row, the same as
for INSERT.
To achieve this, CopyFrom() builds a dummy "INSERT INTO rel DEFAULT VALUES"
statement, runs it through parse analysis, the rewriter, and the planner, and
calls ExecutorStart() on the result. The plan is never executed; it only serves
to initialize the executor state, in particular the WITH CHECK OPTIONs derived
from the table's policies, which COPY then verifies against every row with
ExecWithCheckOptions().
Portion part of RLS node processing is inside the query rewriter, so this path
cannot skip query rewriting. Hence, unlike plain COPY FROM, which ignores rules
on the target table, COPY FROM with row-level security raises an error if the
table has any rule that would apply to an INSERT.
discussion: https://postgr.es/m/CACJufxFbmnoa5O-vL43DPTCGt6oagY4dXgKxy=rcD9-e9g0zEg@mail.gmail.com
commitfest: https://commitfest.postgresql.org/patch/6178
---------------------------
| Attachment | Content-Type | Size |
|---|---|---|
| v15-0001-COPY-FROM-with-RLS.patch | text/x-patch | 34.4 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Cagri Biroglu | 2026-09-01 07:17:02 | Re: Per-table resync for logical replication subscriptions |
| Previous Message | JoongHyuk Shin | 2026-09-01 07:04:49 | Re: Deadlock detector fails to activate on a hot standby replica |