diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2011-03-28 15:45:08 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2011-03-28 15:45:08 -0400 |
commit | fca20bcec9f4cae358044bdc66ebcb88c07c5ac2 (patch) | |
tree | 8282eddcc0f46e24da66e03087317661279a5df5 /src/backend | |
parent | c2391b0551729250923cfbb4d8de5c289d07b1ba (diff) | |
download | postgresql-fca20bcec9f4cae358044bdc66ebcb88c07c5ac2.tar.gz postgresql-fca20bcec9f4cae358044bdc66ebcb88c07c5ac2.zip |
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.
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/heap.c | 43 | ||||
-rw-r--r-- | src/backend/catalog/index.c | 2 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 6 |
3 files changed, 44 insertions, 7 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 5dc90660dee..071266536df 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -403,7 +403,8 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind) for (i = 0; i < natts; i++) { CheckAttributeType(NameStr(tupdesc->attrs[i]->attname), - tupdesc->attrs[i]->atttypid); + tupdesc->attrs[i]->atttypid, + NIL /* assume we're creating a new rowtype */); } } @@ -411,14 +412,23 @@ 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) +CheckAttributeType(const char *attname, Oid atttypid, + List *containing_rowtypes) { char att_typtype = get_typtype(atttypid); + Oid att_typelem; if (atttypid == UNKNOWNOID) { @@ -454,6 +464,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); @@ -464,10 +488,21 @@ CheckAttributeType(const char *attname, Oid atttypid) if (attr->attisdropped) continue; - CheckAttributeType(NameStr(attr->attname), attr->atttypid); + CheckAttributeType(NameStr(attr->attname), attr->atttypid, + containing_rowtypes); } 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); } } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 73b5e6b91c4..487dbbf24d2 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -247,7 +247,7 @@ 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); + CheckAttributeType(NameStr(to->attname), to->atttypid, NIL); } /* diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index da491e14ef2..5d30b0fb20a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -3613,7 +3613,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, typeOid = HeapTupleGetOid(typeTuple); /* make sure datatype is legal for a column */ - CheckAttributeType(colDef->colname, typeOid); + CheckAttributeType(colDef->colname, typeOid, + list_make1_oid(rel->rd_rel->reltype)); /* construct new attribute's pg_attribute entry */ attribute.attrelid = myrelid; @@ -5606,7 +5607,8 @@ ATPrepAlterColumnType(List **wqueue, targettype = typenameTypeId(NULL, typename, &targettypmod); /* make sure datatype is legal for a column */ - CheckAttributeType(colName, targettype); + CheckAttributeType(colName, targettype, + list_make1_oid(rel->rd_rel->reltype)); /* * Set up an expression to transform the old data value to the new type. |