Skip to content

Commit ce3b0ab

Browse files
committed
Prevent a rowtype from being included in itself.
Eventually we might be able to allow that, but it's not clear how many places need to be fixed to prevent infinite recursion when there's a direct or indirect inclusion of a rowtype in itself. One such place is CheckAttributeType(), which will recurse to stack overflow in cases such as those exhibited in bug #5950 from Alex Perepelica. If we were sure it was the only such place, we could easily modify the code added by this patch to stop the recursion without a complaint ... but it probably isn't the only such place. Hence, throw error until such time as someone is excited enough about this type of usage to put work into making it safe. Back-patch as far as 8.3. 8.2 doesn't have the recursive call in CheckAttributeType in the first place, so I see no need to add code there in the absence of clear evidence of a problem elsewhere.
1 parent 3a88af0 commit ce3b0ab

File tree

6 files changed

+67
-4
lines changed

6 files changed

+67
-4
lines changed

src/backend/catalog/heap.c

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
422422
{
423423
CheckAttributeType(NameStr(tupdesc->attrs[i]->attname),
424424
tupdesc->attrs[i]->atttypid,
425+
NIL, /* assume we're creating a new rowtype */
425426
allow_system_table_mods);
426427
}
427428
}
@@ -430,15 +431,24 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
430431
* CheckAttributeType
431432
*
432433
* Verify that the proposed datatype of an attribute is legal.
433-
* This is needed because there are types (and pseudo-types)
434+
* This is needed mainly because there are types (and pseudo-types)
434435
* in the catalogs that we do not support as elements of real tuples.
436+
* We also check some other properties required of a table column.
437+
*
438+
* If the attribute is being proposed for addition to an existing table or
439+
* composite type, pass a one-element list of the rowtype OID as
440+
* containing_rowtypes. When checking a to-be-created rowtype, it's
441+
* sufficient to pass NIL, because there could not be any recursive reference
442+
* to a not-yet-existing rowtype.
435443
* --------------------------------
436444
*/
437445
void
438446
CheckAttributeType(const char *attname, Oid atttypid,
447+
List *containing_rowtypes,
439448
bool allow_system_table_mods)
440449
{
441450
char att_typtype = get_typtype(atttypid);
451+
Oid att_typelem;
442452

443453
if (atttypid == UNKNOWNOID)
444454
{
@@ -476,6 +486,20 @@ CheckAttributeType(const char *attname, Oid atttypid,
476486
TupleDesc tupdesc;
477487
int i;
478488

489+
/*
490+
* Check for self-containment. Eventually we might be able to allow
491+
* this (just return without complaint, if so) but it's not clear how
492+
* many other places would require anti-recursion defenses before it
493+
* would be safe to allow tables to contain their own rowtype.
494+
*/
495+
if (list_member_oid(containing_rowtypes, atttypid))
496+
ereport(ERROR,
497+
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
498+
errmsg("composite type %s cannot be made a member of itself",
499+
format_type_be(atttypid))));
500+
501+
containing_rowtypes = lcons_oid(atttypid, containing_rowtypes);
502+
479503
relation = relation_open(get_typ_typrelid(atttypid), AccessShareLock);
480504

481505
tupdesc = RelationGetDescr(relation);
@@ -487,10 +511,22 @@ CheckAttributeType(const char *attname, Oid atttypid,
487511
if (attr->attisdropped)
488512
continue;
489513
CheckAttributeType(NameStr(attr->attname), attr->atttypid,
514+
containing_rowtypes,
490515
allow_system_table_mods);
491516
}
492517

493518
relation_close(relation, AccessShareLock);
519+
520+
containing_rowtypes = list_delete_first(containing_rowtypes);
521+
}
522+
else if (OidIsValid((att_typelem = get_element_type(atttypid))))
523+
{
524+
/*
525+
* Must recurse into array types, too, in case they are composite.
526+
*/
527+
CheckAttributeType(attname, att_typelem,
528+
containing_rowtypes,
529+
allow_system_table_mods);
494530
}
495531
}
496532

src/backend/catalog/index.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ ConstructTupleDescriptor(Relation heapRelation,
258258
* whether a table column is of a safe type (which is why we
259259
* needn't check for the non-expression case).
260260
*/
261-
CheckAttributeType(NameStr(to->attname), to->atttypid, false);
261+
CheckAttributeType(NameStr(to->attname), to->atttypid,
262+
NIL, false);
262263
}
263264

264265
/*

src/backend/commands/tablecmds.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3729,7 +3729,9 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
37293729
typeOid = HeapTupleGetOid(typeTuple);
37303730

37313731
/* make sure datatype is legal for a column */
3732-
CheckAttributeType(colDef->colname, typeOid, false);
3732+
CheckAttributeType(colDef->colname, typeOid,
3733+
list_make1_oid(rel->rd_rel->reltype),
3734+
false);
37333735

37343736
/* construct new attribute's pg_attribute entry */
37353737
attribute.attrelid = myrelid;
@@ -5858,7 +5860,9 @@ ATPrepAlterColumnType(List **wqueue,
58585860
targettype = typenameTypeId(NULL, typeName, &targettypmod);
58595861

58605862
/* make sure datatype is legal for a column */
5861-
CheckAttributeType(colName, targettype, false);
5863+
CheckAttributeType(colName, targettype,
5864+
list_make1_oid(rel->rd_rel->reltype),
5865+
false);
58625866

58635867
/*
58645868
* Set up an expression to transform the old data value to the new type.

src/include/catalog/heap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
115115
bool allow_system_table_mods);
116116

117117
extern void CheckAttributeType(const char *attname, Oid atttypid,
118+
List *containing_rowtypes,
118119
bool allow_system_table_mods);
119120

120121
#endif /* HEAP_H */

src/test/regress/expected/alter_table.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,18 @@ select * from another;
14761476
(3 rows)
14771477

14781478
drop table another;
1479+
-- disallow recursive containment of row types
1480+
create temp table recur1 (f1 int);
1481+
alter table recur1 add column f2 recur1; -- fails
1482+
ERROR: composite type recur1 cannot be made a member of itself
1483+
alter table recur1 add column f2 recur1[]; -- fails
1484+
ERROR: composite type recur1 cannot be made a member of itself
1485+
create temp table recur2 (f1 int, f2 recur1);
1486+
alter table recur1 add column f2 recur2; -- fails
1487+
ERROR: composite type recur1 cannot be made a member of itself
1488+
alter table recur1 add column f2 int;
1489+
alter table recur1 alter column f2 type recur2; -- fails
1490+
ERROR: composite type recur1 cannot be made a member of itself
14791491
--
14801492
-- alter function
14811493
--

src/test/regress/sql/alter_table.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,15 @@ select * from another;
10921092

10931093
drop table another;
10941094

1095+
-- disallow recursive containment of row types
1096+
create temp table recur1 (f1 int);
1097+
alter table recur1 add column f2 recur1; -- fails
1098+
alter table recur1 add column f2 recur1[]; -- fails
1099+
create temp table recur2 (f1 int, f2 recur1);
1100+
alter table recur1 add column f2 recur2; -- fails
1101+
alter table recur1 add column f2 int;
1102+
alter table recur1 alter column f2 type recur2; -- fails
1103+
10951104
--
10961105
-- alter function
10971106
--

0 commit comments

Comments
 (0)