Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a3c9d35

Browse files
committed
Reject attempts to alter composite types used in indexes.
find_composite_type_dependencies() ignored indexes, which is a poor decision because an expression index could have a stored column of a composite (or other container) type even when the underlying table does not. Teach it to detect such cases and error out. We have to work a bit harder than for other relations because the pg_depend entry won't identify the specific index column of concern, but it's not much new code. This does not address bug #17872's original complaint that dropping a column in such a type might lead to violations of the uniqueness property that a unique index is supposed to ensure. That seems of much less concern to me because it won't lead to crashes. Per bug #17872 from Alexander Lakhin. Back-patch to all supported branches. Discussion: https://postgr.es/m/17872-d0fbb799dc3fd85d@postgresql.org
1 parent c87aff0 commit a3c9d35

File tree

3 files changed

+67
-10
lines changed

3 files changed

+67
-10
lines changed

src/backend/commands/tablecmds.c

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6508,6 +6508,7 @@ find_composite_type_dependencies(Oid typeOid, Relation origRelation,
65086508
{
65096509
Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
65106510
Relation rel;
6511+
TupleDesc tupleDesc;
65116512
Form_pg_attribute att;
65126513

65136514
/* Check for directly dependent types */
@@ -6524,18 +6525,57 @@ find_composite_type_dependencies(Oid typeOid, Relation origRelation,
65246525
continue;
65256526
}
65266527

6527-
/* Else, ignore dependees that aren't user columns of relations */
6528-
/* (we assume system columns are never of interesting types) */
6529-
if (pg_depend->classid != RelationRelationId ||
6530-
pg_depend->objsubid <= 0)
6528+
/* Else, ignore dependees that aren't relations */
6529+
if (pg_depend->classid != RelationRelationId)
65316530
continue;
65326531

65336532
rel = relation_open(pg_depend->objid, AccessShareLock);
6534-
att = TupleDescAttr(rel->rd_att, pg_depend->objsubid - 1);
6533+
tupleDesc = RelationGetDescr(rel);
65356534

6536-
if (rel->rd_rel->relkind == RELKIND_RELATION ||
6537-
rel->rd_rel->relkind == RELKIND_MATVIEW ||
6538-
rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6535+
/*
6536+
* If objsubid identifies a specific column, refer to that in error
6537+
* messages. Otherwise, search to see if there's a user column of the
6538+
* type. (We assume system columns are never of interesting types.)
6539+
* The search is needed because an index containing an expression
6540+
* column of the target type will just be recorded as a whole-relation
6541+
* dependency. If we do not find a column of the type, the dependency
6542+
* must indicate that the type is transiently referenced in an index
6543+
* expression but not stored on disk, which we assume is OK, just as
6544+
* we do for references in views. (It could also be that the target
6545+
* type is embedded in some container type that is stored in an index
6546+
* column, but the previous recursion should catch such cases.)
6547+
*/
6548+
if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6549+
att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6550+
else
6551+
{
6552+
att = NULL;
6553+
for (int attno = 1; attno <= tupleDesc->natts; attno++)
6554+
{
6555+
att = TupleDescAttr(tupleDesc, attno - 1);
6556+
if (att->atttypid == typeOid && !att->attisdropped)
6557+
break;
6558+
att = NULL;
6559+
}
6560+
if (att == NULL)
6561+
{
6562+
/* No such column, so assume OK */
6563+
relation_close(rel, AccessShareLock);
6564+
continue;
6565+
}
6566+
}
6567+
6568+
/*
6569+
* We definitely should reject if the relation has storage. If it's
6570+
* partitioned, then perhaps we don't have to reject: if there are
6571+
* partitions then we'll fail when we find one, else there is no
6572+
* stored data to worry about. However, it's possible that the type
6573+
* change would affect conclusions about whether the type is sortable
6574+
* or hashable and thus (if it's a partitioning column) break the
6575+
* partitioning rule. For now, reject for partitioned rels too.
6576+
*/
6577+
if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6578+
RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
65396579
{
65406580
if (origTypeName)
65416581
ereport(ERROR,

src/test/regress/expected/alter_table.out

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3097,6 +3097,13 @@ CREATE TYPE test_type1 AS (a int, b text);
30973097
CREATE TABLE test_tbl1 (x int, y test_type1);
30983098
ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
30993099
ERROR: cannot alter type "test_type1" because column "test_tbl1.y" uses it
3100+
DROP TABLE test_tbl1;
3101+
CREATE TABLE test_tbl1 (x int, y text);
3102+
CREATE INDEX test_tbl1_idx ON test_tbl1((row(x,y)::test_type1));
3103+
ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
3104+
ERROR: cannot alter type "test_type1" because column "test_tbl1_idx.row" uses it
3105+
DROP TABLE test_tbl1;
3106+
DROP TYPE test_type1;
31003107
CREATE TYPE test_type2 AS (a int, b text);
31013108
CREATE TABLE test_tbl2 OF test_type2;
31023109
CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
@@ -3208,7 +3215,8 @@ Typed table of type: test_type2
32083215
c | text | | |
32093216
Inherits: test_tbl2
32103217

3211-
DROP TABLE test_tbl2_subclass;
3218+
DROP TABLE test_tbl2_subclass, test_tbl2;
3219+
DROP TYPE test_type2;
32123220
CREATE TYPE test_typex AS (a int, b text);
32133221
CREATE TABLE test_tblx (x int, y test_typex check ((y).a > 0));
32143222
ALTER TYPE test_typex DROP ATTRIBUTE a; -- fails

src/test/regress/sql/alter_table.sql

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,14 @@ CREATE TYPE test_type1 AS (a int, b text);
19831983
CREATE TABLE test_tbl1 (x int, y test_type1);
19841984
ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
19851985

1986+
DROP TABLE test_tbl1;
1987+
CREATE TABLE test_tbl1 (x int, y text);
1988+
CREATE INDEX test_tbl1_idx ON test_tbl1((row(x,y)::test_type1));
1989+
ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
1990+
1991+
DROP TABLE test_tbl1;
1992+
DROP TYPE test_type1;
1993+
19861994
CREATE TYPE test_type2 AS (a int, b text);
19871995
CREATE TABLE test_tbl2 OF test_type2;
19881996
CREATE TABLE test_tbl2_subclass () INHERITS (test_tbl2);
@@ -2010,7 +2018,8 @@ ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa CASCADE;
20102018
\d test_tbl2
20112019
\d test_tbl2_subclass
20122020

2013-
DROP TABLE test_tbl2_subclass;
2021+
DROP TABLE test_tbl2_subclass, test_tbl2;
2022+
DROP TYPE test_type2;
20142023

20152024
CREATE TYPE test_typex AS (a int, b text);
20162025
CREATE TABLE test_tblx (x int, y test_typex check ((y).a > 0));

0 commit comments

Comments
 (0)