On Wed, Aug 19, 2026 at 7:58 PM Alexander Korotkov <[email protected]> wrote:
>
> Agree on your corrections expect for deleteSplitPartitionContext(): it
> still have resources to free. The revised patchset is attached.
>
Hi.
-- SPLIT PARTITION rejects a partition with row-level security of its own, for
-- the same reason as MERGE.
CREATE TABLE t (i int, secret bool) PARTITION BY RANGE (i);
CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
ALTER TABLE tp_0_2 ENABLE ROW LEVEL SECURITY;
CREATE POLICY hide_secret ON tp_0_2 FOR SELECT USING (secret IS NOT TRUE);
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); -- fails
ERROR: cannot merge or split partition "tp_0_2" that has row-level
security enabled
DETAIL: Row-level security is not carried over to the new partition,
which would expose rows that the partition currently hides.
HINT: Disable row-level security on the partition before the
operation, and re-establish it on the new partition afterwards.
ALTER TABLE tp_0_2 DISABLE ROW LEVEL SECURITY;
ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
(PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
PARTITION tp_1_2 FOR VALUES FROM (1) TO (2)); -- still fails
ERROR: cannot merge or split partition "tp_0_2" that has row-level
security policies
DETAIL: The policies are not carried over to the new partition and
would be silently lost.
HINT: Drop the policies from the partition before the operation, and
define them on the new partition afterwards.
DROP POLICY hide_secret ON tp_0_2;
----------------------------------
Since we have the MERGE SQL command, it would be better to replace
"the same reason as MERGE."
with "the same reason as MERGE PARTITIONS".
I think the HINT in the first error message is not very helpful, it
suggests disabling row-level security on table tp_0_2.
However, even if with RLS disabled on table tp_0_2, we still need to
drop the policies and redefine them.I am OK with the second HINT.
maybe we can change errhint("Disable row-level security on the
partition before the operation, and re-establish it on the new
partition afterwards."));to errhint("Disable row-level security on the
partition and drop the existing policies before the operation, then
re-establish them on the new partition afterwards."));
"because the row-movement path cannot safely recompute the value while
re-verifying all of the table's constraints against it."
I am not sure the word "path" is necessary.
Other than that, v5 looks good to me. (i didn't review 0001 and 0002).
--------------------
CREATE ACCESS METHOD partitions_merge_heap TYPE TABLE HANDLER
heap_tableam_handler;
begin;
DROP TABLE if exists t;
CREATE TABLE t (i int) PARTITION BY RANGE (i);
CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
set local default_table_access_method to partitions_merge_heap;
ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
SELECT a.amname FROM pg_class c, pg_am a WHERE c.relname = 'tp_0_2'
AND a.oid = c.relam;
rollback;
The last SELECT query should return "partitions_merge_heap", IIMHO.
The attached patch based on v5, fixes this issue.
--
jian
https://www.enterprisedb.com/
From 40b78f838af24970bb51674de1783a1ebb9cbd17 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 20 Aug 2026 15:15:00 +0800
Subject: [PATCH v6 1/1] Fix access method for new partition tables
If the partitioned table has a valid table access method, newly created
partitions for MERGE/SPLIT PARTITIONS use the parent table's access method.
Otherwise, fall back to default_table_access_method.
---
src/backend/commands/tablecmds.c | 5 ++++-
src/test/regress/expected/partition_merge.out | 15 +++++++++++++++
src/test/regress/expected/partition_split.out | 18 ++++++++++++++++++
src/test/regress/sql/partition_merge.sql | 11 +++++++++++
src/test/regress/sql/partition_split.sql | 12 ++++++++++++
5 files changed, 60 insertions(+), 1 deletion(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1840738f3f4..4371694ed8d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -23334,7 +23334,10 @@ createPartitionTable(RangeVar *newPartName,
descriptor = BuildDescForRelation(colList);
/* Look up the access method for the new relation. */
- relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
+ if (OidIsValid(parent_relform->relam))
+ relamId = parent_relform->relam;
+ else
+ relamId = get_table_am_oid(default_table_access_method, false);
/* Look up the namespace in which we are supposed to create the relation. */
namespaceId =
diff --git a/src/test/regress/expected/partition_merge.out b/src/test/regress/expected/partition_merge.out
index 7e1aac3b44d..d16a898e6c0 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -791,6 +791,21 @@ ORDER BY c.relname COLLATE "C";
tp_0_2 | partitions_merge_heap
(2 rows)
+DROP TABLE t;
+CREATE TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+BEGIN;
+SET LOCAL default_table_access_method to partitions_merge_heap;
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
+SELECT c.relname, a.amname FROM pg_class c, pg_am a
+WHERE c.relname = 'tp_0_2' AND a.oid = c.relam;
+ relname | amname
+---------+-----------------------
+ tp_0_2 | partitions_merge_heap
+(1 row)
+
+COMMIT;
DROP TABLE t;
DROP ACCESS METHOD partitions_merge_heap;
-- Test permission checks. The user needs to own the parent table and all
diff --git a/src/test/regress/expected/partition_split.out b/src/test/regress/expected/partition_split.out
index 98575e00119..8780d19f90a 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1355,6 +1355,24 @@ ORDER BY c.relname COLLATE "C";
tp_1_2 | partition_split_heap
(3 rows)
+DROP TABLE t;
+CREATE TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
+BEGIN;
+SET LOCAL default_table_access_method to partition_split_heap;
+ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
+ (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
+ PARTITION tp_1_2 FOR VALUES FROM (1) TO (2));
+SELECT c.relname, a.amname FROM pg_class c, pg_am a
+WHERE c.relname IN ('tp_0_1', 'tp_1_2')
+AND a.oid = c.relam;
+ relname | amname
+---------+----------------------
+ tp_0_1 | partition_split_heap
+ tp_1_2 | partition_split_heap
+(2 rows)
+
+COMMIT;
DROP TABLE t;
DROP ACCESS METHOD partition_split_heap;
-- Split partition of a temporary table when one of the partitions after
diff --git a/src/test/regress/sql/partition_merge.sql b/src/test/regress/sql/partition_merge.sql
index 0fcda645147..11550a00be8 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -550,6 +550,17 @@ FROM pg_class c JOIN pg_am a ON c.relam = a.oid
WHERE c.oid IN ('t'::regclass, 'tp_0_2'::regclass)
ORDER BY c.relname COLLATE "C";
DROP TABLE t;
+
+CREATE TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TABLE tp_0_1 PARTITION OF t FOR VALUES FROM (0) TO (1);
+CREATE TABLE tp_1_2 PARTITION OF t FOR VALUES FROM (1) TO (2);
+BEGIN;
+SET LOCAL default_table_access_method to partitions_merge_heap;
+ALTER TABLE t MERGE PARTITIONS (tp_0_1, tp_1_2) INTO tp_0_2;
+SELECT c.relname, a.amname FROM pg_class c, pg_am a
+WHERE c.relname = 'tp_0_2' AND a.oid = c.relam;
+COMMIT;
+DROP TABLE t;
DROP ACCESS METHOD partitions_merge_heap;
-- Test permission checks. The user needs to own the parent table and all
diff --git a/src/test/regress/sql/partition_split.sql b/src/test/regress/sql/partition_split.sql
index e97f13f749c..b734d69ba68 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -970,6 +970,18 @@ FROM pg_class c JOIN pg_am a ON c.relam = a.oid
WHERE c.oid IN ('t'::regclass, 'tp_0_1'::regclass, 'tp_1_2'::regclass)
ORDER BY c.relname COLLATE "C";
DROP TABLE t;
+CREATE TABLE t (i int) PARTITION BY RANGE (i);
+CREATE TABLE tp_0_2 PARTITION OF t FOR VALUES FROM (0) TO (2);
+BEGIN;
+SET LOCAL default_table_access_method to partition_split_heap;
+ALTER TABLE t SPLIT PARTITION tp_0_2 INTO
+ (PARTITION tp_0_1 FOR VALUES FROM (0) TO (1),
+ PARTITION tp_1_2 FOR VALUES FROM (1) TO (2));
+SELECT c.relname, a.amname FROM pg_class c, pg_am a
+WHERE c.relname IN ('tp_0_1', 'tp_1_2')
+AND a.oid = c.relam;
+COMMIT;
+DROP TABLE t;
DROP ACCESS METHOD partition_split_heap;
-- Split partition of a temporary table when one of the partitions after
--
2.34.1