On Tue, Oct 28, 2025 at 11:24 PM Kirill Reshke <[email protected]> wrote:
>
> I can see that 0 - FirstLowInvalidHeapAttributeNumber is an existing
> coding practise in sources, but so is
> InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber (sepgsql
> contrib extension). I'm voting for the second one, even though it is
> less used.
>

hi.

now it's:
+            /*
+             * If the partition expression contains a whole-row reference,
+             * then all columns are indirectly associated with that
+             * expression.
+             */
+            if (bms_is_member(InvalidAttrNumber -
FirstLowInvalidHeapAttributeNumber,
+                              expr_attrs))
+            {
+                if (used_in_expr)
+                    *used_in_expr = true;
+                return true;
+            }

also polished the commit message. below is the commit message:
--------------------------
For partition key expressions containing whole-row reference, we cannot rely on
pg_depend lookups to determine whether an individual column can be altered
(drop, set data type).
As noted in the comments for find_expr_references_walker, no dependency entries
are recorded for whole-row expressions. Therefore whole-row reference check is
needed in has_partition_attrs.

Partition key expressions contain whole-row reference, it is effectively
equivalent to all user columns being indirectly associated with that expression.
Since columns that in a partition key cannot be altered in any way, the same
restriction should be applied to whole-row reference expressions in the
partition key.
--------------------------


--
jian
https://www.enterprisedb.com/
From 8eee74af121b2a9cc8f72111f15d94c3086138a2 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Wed, 31 Dec 2025 10:54:54 +0800
Subject: [PATCH v3 1/1] disallow altering column if partition key contains
 wholerow reference

For partition key expressions containing whole-row reference, we cannot rely on
pg_depend lookups to determine whether an individual column can be altered
(drop, set data type).
As noted in the comments for find_expr_references_walker, no dependency entries
are recorded for whole-row expressions. Therefore whole-row reference check is
needed in has_partition_attrs.

Partition key expressions contain whole-row reference, it is effectively
equivalent to all user columns being indirectly associated with that expression.
Since columns that in a partition key cannot be altered in any way, the same
restriction should be applied to whole-row reference expressions in the
partition key.

Author: jian he <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Reviewed-by: Matt Dailis <[email protected]>
Reviewed-by: Kirill Reshke <[email protected]>
discussion: https://postgr.es/m/CACJufxG5wLiATocRTaC=z+kw4mUaasC-50+q9K=fOdAr3=o...@mail.gmail.com
---
 src/backend/catalog/partition.c           | 14 ++++++++++++++
 src/test/regress/expected/alter_table.out | 16 ++++++++++++++++
 src/test/regress/sql/alter_table.sql      |  9 +++++++++
 3 files changed, 39 insertions(+)

diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 93d72157a46..5d5fdf5dd77 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -290,6 +290,20 @@ has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
 
 			/* Find all attributes referenced */
 			pull_varattnos(expr, 1, &expr_attrs);
+
+			/*
+			 * If the partition expression contains a whole-row reference,
+			 * then all columns are indirectly associated with that
+			 * expression.
+			 */
+			if (bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,
+							  expr_attrs))
+			{
+				if (used_in_expr)
+					*used_in_expr = true;
+				return true;
+			}
+
 			partexprs_item = lnext(partexprs, partexprs_item);
 
 			if (bms_overlap(attnums, expr_attrs))
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 5e98bbf2425..e740f178c11 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3988,6 +3988,22 @@ LINE 1: ALTER TABLE partitioned ALTER COLUMN b TYPE char(5);
 ALTER TABLE partitioned SET (fillfactor=100);
 ERROR:  cannot specify storage parameters for a partitioned table
 HINT:  Specify storage parameters for its leaf partitions instead.
+-- If the partition expression contains a whole-row reference, altering a
+-- column’s data type or dropping the column is not allowed.
+CREATE TABLE partitioned1(a int, b int) PARTITION BY list((partitioned1));
+ALTER TABLE partitioned1 DROP COLUMN a; --error
+ERROR:  cannot drop column "a" because it is part of the partition key of relation "partitioned1"
+ALTER TABLE partitioned1 DROP COLUMN b; --error
+ERROR:  cannot drop column "b" because it is part of the partition key of relation "partitioned1"
+ALTER TABLE partitioned1 ALTER COLUMN a TYPE text; --error
+ERROR:  cannot alter column "a" because it is part of the partition key of relation "partitioned1"
+LINE 1: ALTER TABLE partitioned1 ALTER COLUMN a TYPE text;
+                                              ^
+ALTER TABLE partitioned1 ALTER COLUMN b TYPE text; --error
+ERROR:  cannot alter column "b" because it is part of the partition key of relation "partitioned1"
+LINE 1: ALTER TABLE partitioned1 ALTER COLUMN b TYPE text;
+                                              ^
+DROP TABLE partitioned1;
 -- partitioned table cannot participate in regular inheritance
 CREATE TABLE nonpartitioned (
 	a int,
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 417202430a5..cce2ac8ea57 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2392,6 +2392,15 @@ ALTER TABLE partitioned ALTER COLUMN b TYPE char(5);
 -- specifying storage parameters for partitioned tables is not supported
 ALTER TABLE partitioned SET (fillfactor=100);
 
+-- If the partition expression contains a whole-row reference, altering a
+-- column’s data type or dropping the column is not allowed.
+CREATE TABLE partitioned1(a int, b int) PARTITION BY list((partitioned1));
+ALTER TABLE partitioned1 DROP COLUMN a; --error
+ALTER TABLE partitioned1 DROP COLUMN b; --error
+ALTER TABLE partitioned1 ALTER COLUMN a TYPE text; --error
+ALTER TABLE partitioned1 ALTER COLUMN b TYPE text; --error
+DROP TABLE partitioned1;
+
 -- partitioned table cannot participate in regular inheritance
 CREATE TABLE nonpartitioned (
 	a int,
-- 
2.34.1

Reply via email to