]> granicus.if.org Git - postgresql/commitdiff
Prevent a rowtype from being included in itself.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 28 Mar 2011 19:45:02 +0000 (15:45 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 28 Mar 2011 19:45:02 +0000 (15:45 -0400)
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.

src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/commands/tablecmds.c
src/include/catalog/heap.h
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index a090981e5c4161ff7dd72690e8f26e87135edbd0..d74700f716f81800149af4073c526e967bd242c9 100644 (file)
@@ -422,6 +422,7 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
        {
                CheckAttributeType(NameStr(tupdesc->attrs[i]->attname),
                                                   tupdesc->attrs[i]->atttypid,
+                                                  NIL, /* assume we're creating a new rowtype */
                                                   allow_system_table_mods);
        }
 }
@@ -430,15 +431,24 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
  *             CheckAttributeType
  *
  *             Verify that the proposed datatype of an attribute is legal.
- *             This is needed because there are types (and pseudo-types)
+ *             This is needed mainly because there are types (and pseudo-types)
  *             in the catalogs that we do not support as elements of real tuples.
+ *             We also check some other properties required of a table column.
+ *
+ * If the attribute is being proposed for addition to an existing table or
+ * composite type, pass a one-element list of the rowtype OID as
+ * containing_rowtypes.  When checking a to-be-created rowtype, it's
+ * sufficient to pass NIL, because there could not be any recursive reference
+ * to a not-yet-existing rowtype.
  * --------------------------------
  */
 void
 CheckAttributeType(const char *attname, Oid atttypid,
+                                  List *containing_rowtypes,
                                   bool allow_system_table_mods)
 {
        char            att_typtype = get_typtype(atttypid);
+       Oid                     att_typelem;
 
        if (atttypid == UNKNOWNOID)
        {
@@ -476,6 +486,20 @@ CheckAttributeType(const char *attname, Oid atttypid,
                TupleDesc       tupdesc;
                int                     i;
 
+               /*
+                * Check for self-containment.  Eventually we might be able to allow
+                * this (just return without complaint, if so) but it's not clear how
+                * many other places would require anti-recursion defenses before it
+                * would be safe to allow tables to contain their own rowtype.
+                */
+               if (list_member_oid(containing_rowtypes, atttypid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                        errmsg("composite type %s cannot be made a member of itself",
+                                                       format_type_be(atttypid))));
+
+               containing_rowtypes = lcons_oid(atttypid, containing_rowtypes);
+
                relation = relation_open(get_typ_typrelid(atttypid), AccessShareLock);
 
                tupdesc = RelationGetDescr(relation);
@@ -487,10 +511,22 @@ CheckAttributeType(const char *attname, Oid atttypid,
                        if (attr->attisdropped)
                                continue;
                        CheckAttributeType(NameStr(attr->attname), attr->atttypid,
+                                                          containing_rowtypes,
                                                           allow_system_table_mods);
                }
 
                relation_close(relation, AccessShareLock);
+
+               containing_rowtypes = list_delete_first(containing_rowtypes);
+       }
+       else if (OidIsValid((att_typelem = get_element_type(atttypid))))
+       {
+               /*
+                * Must recurse into array types, too, in case they are composite.
+                */
+               CheckAttributeType(attname, att_typelem,
+                                                  containing_rowtypes,
+                                                  allow_system_table_mods);
        }
 }
 
index 54cf280d57f4b00da58a247687f738e50fa23e66..caa985a791fa9b6a2d467935914015b960f5085b 100644 (file)
@@ -258,7 +258,8 @@ ConstructTupleDescriptor(Relation heapRelation,
                         * whether a table column is of a safe type (which is why we
                         * needn't check for the non-expression case).
                         */
-                       CheckAttributeType(NameStr(to->attname), to->atttypid, false);
+                       CheckAttributeType(NameStr(to->attname), to->atttypid,
+                                                          NIL, false);
                }
 
                /*
index 6a1804b6fec7173b722836be0d3092bc3679a6e5..64141c03408d6324da1e85337bc5b01c8c70fe74 100644 (file)
@@ -3729,7 +3729,9 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
        typeOid = HeapTupleGetOid(typeTuple);
 
        /* make sure datatype is legal for a column */
-       CheckAttributeType(colDef->colname, typeOid, false);
+       CheckAttributeType(colDef->colname, typeOid,
+                                          list_make1_oid(rel->rd_rel->reltype),
+                                          false);
 
        /* construct new attribute's pg_attribute entry */
        attribute.attrelid = myrelid;
@@ -5858,7 +5860,9 @@ ATPrepAlterColumnType(List **wqueue,
        targettype = typenameTypeId(NULL, typeName, &targettypmod);
 
        /* make sure datatype is legal for a column */
-       CheckAttributeType(colName, targettype, false);
+       CheckAttributeType(colName, targettype,
+                                          list_make1_oid(rel->rd_rel->reltype),
+                                          false);
 
        /*
         * Set up an expression to transform the old data value to the new type.
index 557c311bc22b4a25bbaddd666acbd25cc4a197c1..6a91ddb4fc3308500a87c2578e03a57c1c28c3cb 100644 (file)
@@ -115,6 +115,7 @@ extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
                                                 bool allow_system_table_mods);
 
 extern void CheckAttributeType(const char *attname, Oid atttypid,
+                                  List *containing_rowtypes,
                                   bool allow_system_table_mods);
 
 #endif   /* HEAP_H */
index 40a907d869e90225dc841257e4f7a2879afc2c9f..a460371a112412c70bdf578b27f40adc45b164c1 100644 (file)
@@ -1476,6 +1476,18 @@ select * from another;
 (3 rows)
 
 drop table another;
+-- disallow recursive containment of row types
+create temp table recur1 (f1 int);
+alter table recur1 add column f2 recur1; -- fails
+ERROR:  composite type recur1 cannot be made a member of itself
+alter table recur1 add column f2 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
+alter table recur1 add column f2 int;
+alter table recur1 alter column f2 type recur2; -- fails
+ERROR:  composite type recur1 cannot be made a member of itself
 --
 -- alter function
 --
index 2b01238e28264fa2829a553ddc1a2a2a754e68e7..8f472cff1e5e48026ca407a88f12669a205823e3 100644 (file)
@@ -1092,6 +1092,15 @@ select * from another;
 
 drop table another;
 
+-- disallow recursive containment of row types
+create temp table recur1 (f1 int);
+alter table recur1 add column f2 recur1; -- fails
+alter table recur1 add column f2 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;
+alter table recur1 alter column f2 type recur2; -- fails
+
 --
 -- alter function
 --