0001-Don-t-allow-composite-type-to-be-member-of-itself-vi.patch
text/x-patch
Filename: 0001-Don-t-allow-composite-type-to-be-member-of-itself-vi.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0001
Subject: Don't allow composite type to be member of itself via multirange
| File | + | − |
|---|---|---|
| src/backend/catalog/heap.c | 10 | 0 |
| src/test/regress/expected/alter_table.out | 9 | 0 |
| src/test/regress/expected/rangetypes.out | 3 | 1 |
| src/test/regress/sql/alter_table.sql | 3 | 0 |
| src/test/regress/sql/rangetypes.sql | 2 | 1 |
From 50eecdd6477279785616bb4945b31b243265904e Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Wed, 22 Apr 2026 22:10:06 +0300
Subject: [PATCH 1/2] Don't allow composite type to be member of itself via
multirange
CheckAttributeType() checks that a composite type is not made a member
of itself with ALTER TABLE ADD COLUMN or ALTER TYPE ADD ATTRIBUTE,
even indirectly via a domain, array, another composite type or a range
type. But it missed checking for multiranges. That was a simple
oversight when multiranges were added.
Discussion: xxx
Backpatch-through: 14
---
src/backend/catalog/heap.c | 10 ++++++++++
src/test/regress/expected/alter_table.out | 9 +++++++++
src/test/regress/expected/rangetypes.out | 4 +++-
src/test/regress/sql/alter_table.sql | 3 +++
src/test/regress/sql/rangetypes.sql | 3 ++-
5 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index ae6b7cda3dd..4a71f6a33b4 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -654,6 +654,16 @@ CheckAttributeType(const char *attname,
containing_rowtypes,
flags);
}
+ else if (att_typtype == TYPTYPE_MULTIRANGE)
+ {
+ /*
+ * If it's a multirange, recurse to check its plain range type.
+ */
+ CheckAttributeType(attname, get_multirange_range(atttypid),
+ get_range_collation(atttypid),
+ containing_rowtypes,
+ flags);
+ }
else if (OidIsValid((att_typelem = get_element_type(atttypid))))
{
/*
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index dad9d36937e..fc27e22023b 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2335,6 +2335,15 @@ ERROR: composite type recur1 cannot be made a member of itself
create domain array_of_recur1 as recur1[];
alter table recur1 add column f2 array_of_recur1; -- fails
ERROR: composite type recur1 cannot be made a member of itself
+create type range_of_recur1 as range (subtype=recur1, multirange_type_name='multirange_of_recur1');
+NOTICE: function "range_of_recur1" will be effectively temporary
+DETAIL: It depends on temporary type recur1.
+NOTICE: function "range_of_recur1" will be effectively temporary
+DETAIL: It depends on temporary type recur1.
+alter table recur1 add column f2 range_of_recur1; -- fails
+ERROR: composite type recur1 cannot be made a member of itself
+alter table recur1 add column f2 multirange_of_recur1; -- fails
+ERROR: composite type recur1 cannot be made a member of itself
create temp table recur2 (f1 int, f2 recur1);
alter table recur1 add column f2 recur2; -- fails
ERROR: composite type recur1 cannot be made a member of itself
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index e062a4e5c2c..b2a753fd179 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -1810,9 +1810,11 @@ select *, row_to_json(upper(t)) as u from
["(5,6)","(7,8)") | {"a":7,"b":8}
(2 rows)
--- this must be rejected to avoid self-inclusion issues:
+-- these must be rejected to avoid self-inclusion issues:
alter type two_ints add attribute c two_ints_range;
ERROR: composite type two_ints cannot be made a member of itself
+alter type two_ints add attribute c two_ints_multirange;
+ERROR: composite type two_ints cannot be made a member of itself
drop type two_ints cascade;
NOTICE: drop cascades to type two_ints_range
--
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index f5f13bbd3e7..6aaa33b6e34 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1535,6 +1535,9 @@ alter table recur1 add column f2 recur1; -- fails
alter table recur1 add column f2 recur1[]; -- fails
create domain array_of_recur1 as recur1[];
alter table recur1 add column f2 array_of_recur1; -- fails
+create type range_of_recur1 as range (subtype=recur1, multirange_type_name='multirange_of_recur1');
+alter table recur1 add column f2 range_of_recur1; -- fails
+alter table recur1 add column f2 multirange_of_recur1; -- fails
create temp table recur2 (f1 int, f2 recur1);
alter table recur1 add column f2 recur2; -- fails
alter table recur1 add column f2 int;
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 5c4b0337b7a..f0354736fe5 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -576,8 +576,9 @@ select *, row_to_json(upper(t)) as u from
(values (two_ints_range(row(1,2), row(3,4))),
(two_ints_range(row(5,6), row(7,8)))) v(t);
--- this must be rejected to avoid self-inclusion issues:
+-- these must be rejected to avoid self-inclusion issues:
alter type two_ints add attribute c two_ints_range;
+alter type two_ints add attribute c two_ints_multirange;
drop type two_ints cascade;
--
2.47.3