Обсуждение: CREATE TABLE LIKE INCLUDING POLICIES

Поиск
Список
Период
Сортировка

CREATE TABLE LIKE INCLUDING POLICIES

От
jian he
Дата:
hi.

demo:

 CREATE TABLE coll_t (c) AS VALUES ('bar'::text);
 CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C"));
+-- coll_t1 row security is not enabled, but we still copy it's policies
+CREATE TABLE coll_t1(LIKE coll_t INCLUDING POLICIES);
+\d coll_t1
+       Table "regress_rls_schema.coll_t1"
+ Column | Type | Collation | Nullable | Default
+--------+------+-----------+----------+---------
+ c      | text |           |          |
+Policies (row security disabled):
+    POLICY "coll_p"
+      USING ((c < ('foo'::text COLLATE "C")))

by default, new table row security will be disabled, policy object comments are
copied if INCLUDING COMMENTS is specified.

ALTER TABLE SET DATA TYPE, changing a column type typically pulls the object and
reconstructs the corresponding CREATE or ALTER command string.
however CREATE TABLE LIKE retrieves the object’s catalog data and adjusts Vars
to build a Create.*Stmt node.

Here, we generate a CreatePolicyStmt from the source relation’s pg_policy
metadata and then pass it to CreatePolicy.

CreatePolicy normally performs parse analysis on CreatePolicyStmt->qual and
CreatePolicyStmt->with_check.  However, since the pg_policy entries from the
source relation already have their qual and check_qual parse analyzed, we cannot
re-analyze them.  Similar to transformStatsStmt, we therefore need a
transformPolicyStmt.


v1-0001: refactor CreatePolicy, add function transformPolicyStmt
briefly explained in [1].
v1-0002: CREATE TABLE LIKE INCLUDING-POLICIES

[1] https://postgr.es/m/CACJufxGPcBzdL9T6Qh=OFecN8zqxuU0QXfYF8F3WYV=uzwYCdA@mail.gmail.com

Вложения

Re: CREATE TABLE LIKE INCLUDING POLICIES

От
jian he
Дата:
hi.

to avoid repeated name lookup issue, I added a RangeTblEntry field to
CreatePolicyStmt.
see v2-0001-add-RangeTblEntry-to-CreatePolicyStmt.patch commit message too.

v2-0001, v2-0002 will be used in thread [1] too.
Please also check polished v2-0003.

The main logic is for each source table POLICY, using pg_policy catalog
information, produces a CreatePolicyStmt node. The source relation is already
locked in AccessShareLock, but POLICY qual, with check qual may reference other
unrelated relations, we also need to lock them in AccessShareLock too.

[1]: https://postgr.es/m/CACJufxE42vysVEDEmaoBGmGYLZTCgUAwh_h-c9dcSLDtD5jE3g@mail.gmail.com



--
jian
https://www.enterprisedb.com

Вложения

Re: CREATE TABLE LIKE INCLUDING POLICIES

От
jian he
Дата:
hi.

inspired by this message [1], now, v3 logic is more like other CREATE
TABLE LIKE INCLUDING commands.

Since policy queries can include SubLinks, we first acquire
AccessShareLock lock on all the referenced relations.
Only then we copy the USING and WITH CHECK clauses from the source relation.

[1] https://postgr.es/m/CA+TgmoY8v44Y8j--b7e72kuvupZdtMEZf6FVjRfeqsG4gunpzw@mail.gmail.com


--
jian
https://www.enterprisedb.com

Вложения

Re: CREATE TABLE LIKE INCLUDING POLICIES

От
Zsolt Parragi
Дата:
Hello!

Generally looks good to me, the tests work, the new feature seem to
work, I only noticed a few minor things.

parse_utilcmd.c:2204
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert whole-row table reference"),
+ errdetail("Security policy \"%s\" contains a whole-row reference to
table \"%s\".",
+   NameStr(policy_form->polname),
+   RelationGetRelationName(parent_rel)));

The outer parentheses are missing after "ERROR, "

Same issue at line 2232.

parse_utilcmd:1634
+ policy_form = (Form_pg_policy) GETSTRUCT(tuple);
+
+ polrels = PolicyGetRelations(policy_form->oid);

Shouldn't the function free polrels after the foreach below?

policy.c:1357
+
+ relation_close(depRel, AccessShareLock);
+

Shouldn't that be table_close?

create_table.sgml:760
+          All row-level security policies are copied to the new table.
+          Note that by default row-level security is not enabled to
the new table,
+          using <command>ALTER TABLE ... ENABLE ROW LEVEL SECURITY</command>
+          in order for created policies to be applied to the new table.

Maybe "use ALTER TABLE ... ENABLE ROW LEVEL SECURITY to copy existing
policies to the new table"?

I am also wondering if this shouldn't be at least an option for CREATE
TABLE AS. This seems like something that's very easy to overlook and
accidentally forget.



Re: CREATE TABLE LIKE INCLUDING POLICIES

От
jian he
Дата:
On Wed, Jan 21, 2026 at 11:23 PM Zsolt Parragi
<zsolt.parragi@percona.com> wrote:
>
> Hello!
>
> Generally looks good to me, the tests work, the new feature seem to
> work, I only noticed a few minor things.
>
> parse_utilcmd.c:2204
> + ereport(ERROR,
> + errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
> + errmsg("cannot convert whole-row table reference"),
> + errdetail("Security policy \"%s\" contains a whole-row reference to
> table \"%s\".",
> +   NameStr(policy_form->polname),
> +   RelationGetRelationName(parent_rel)));
>
> The outer parentheses are missing after "ERROR, "
>
> Same issue at line 2232.

Actually, this extra parentheses is optional.
see
https://postgr.es/m/202510100916.s2e6n3xiwvyc@alvherre.pgsql
and
https://postgr.es/m/CACJufxEheV10DpjFf+J1OabMgRe6CH+4c6d8ca3Wh1v8Twh3ZA@mail.gmail.com

>
> parse_utilcmd:1634
> + policy_form = (Form_pg_policy) GETSTRUCT(tuple);
> +
> + polrels = PolicyGetRelations(policy_form->oid);
>
> Shouldn't the function free polrels after the foreach below?
>

I found this post
https://stackoverflow.com/questions/79672060/when-should-i-use-pfree-in-a-postgres

I noticed that the above RelationGetIndexList does not list_free
parent_indexes as well.
It should be ok not not call list_free polrels too, because as the memory
allocation here is in portal context which should be reset by end of the current
statement execution.

> policy.c:1357
> +
> + relation_close(depRel, AccessShareLock);
> +
>
> Shouldn't that be table_close?
>
> create_table.sgml:760
> +          All row-level security policies are copied to the new table.
> +          Note that by default row-level security is not enabled to
> the new table,
> +          using <command>ALTER TABLE ... ENABLE ROW LEVEL SECURITY</command>
> +          in order for created policies to be applied to the new table.
>
> Maybe "use ALTER TABLE ... ENABLE ROW LEVEL SECURITY to copy existing
> policies to the new table"?
>
how about

+ All row-level security policies are copied to the new table.
+ Note, however, that row-level security policies are not enabled on the new
+ table by default,
+ using <command>ALTER TABLE ... ENABLE ROW LEVEL SECURITY</command>
+ for the copied policies to take effect.

> I am also wondering if this shouldn't be at least an option for CREATE
> TABLE AS. This seems like something that's very easy to overlook and
> accidentally forget.

do you mean
https://www.postgresql.org/docs/current/sql-createtableas.html
to also copy RLS policies.
if so, seems not doable, for example,

create table x as select from tenk1, tenk2 limit 2;

Should it copy all RLS policies from tenk1 and tenk2 to the new tables?
That does not seem to make sense to me.



--
jian
https://www.enterprisedb.com/



Re: CREATE TABLE LIKE INCLUDING POLICIES

От
Zsolt Parragi
Дата:
> Actually, this extra parentheses is optional.

Thanks for the links, I thought this is a mandatory convention in PG.

> I noticed that the above RelationGetIndexList does not list_free
> parent_indexes as well.
> It should be ok not not call list_free polrels too, because as the memory
> allocation here is in portal context which should be reset by end of the current
> statement execution.

I mainly suggested that because here PolicyGetRelations is inside a
loop, so it allocates multiple lists, not just one per statement. It
might be still a low amount of memory, so maybe its good as is.

> how about ...

that sounds good to me.


> do you mean
> https://www.postgresql.org/docs/current/sql-createtableas.html
> to also copy RLS policies.

No, sorry, I meant to write CREATE TABLE LIKE there. What I meant is
that it might be better to enable the policies if they are enabled on
the parent table, or at least to have an option to do this, as this
seems easy to overlook (to assume that if it INCLUDING POLICIES, that
includes enabling them).

Especially because in most RLS examples/introduction, you first run
ALTER TABLE ENABLE ROW LEVEL SECURITY, and then add policies with
CREATE POLICY after that.

> if so, seems not doable, for example,
>
> create table x as select from tenk1, tenk2 limit 2;
>
> Should it copy all RLS policies from tenk1 and tenk2 to the new tables?

Now that I think about it, it still would be a nice option for simple
cases where people just want to duplicate a table

create table including policies table_copy as select * from table;

But that's definitely a different topic.