diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 3ad99c8108..718c3b9091 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.268 2004/06/06 04:52:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.269 2004/06/06 20:30:07 tgl Exp $ * * * INTERFACE ROUTINES @@ -416,10 +416,7 @@ CheckAttributeType(const char *attname, Oid atttypid) * Warn user, but don't fail, if column to be created has UNKNOWN type * (usually as a result of a 'retrieve into' - jolly) * - * Refuse any attempt to create a pseudo-type column or one that uses a - * non-standalone composite type. (We could support using table rowtypes - * as attributes, if we were willing to make ALTER TABLE hugely more - * complex, but for now let's limit the damage ...) + * Refuse any attempt to create a pseudo-type column. */ if (atttypid == UNKNOWNOID) ereport(WARNING, @@ -435,16 +432,6 @@ CheckAttributeType(const char *attname, Oid atttypid) errmsg("column \"%s\" has pseudo-type %s", attname, format_type_be(atttypid)))); } - else if (att_typtype == 'c') - { - Oid typrelid = get_typ_typrelid(atttypid); - - if (get_rel_relkind(typrelid) != RELKIND_COMPOSITE_TYPE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("column \"%s\" has composite type %s", - attname, format_type_be(atttypid)))); - } } /* -------------------------------- diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 66c1b95135..3a95064442 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.111 2004/06/05 19:48:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.112 2004/06/06 20:30:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -194,6 +194,8 @@ static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse); static void ATOneLevelRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd); +static void find_composite_type_dependencies(Oid typeOid, + const char *origTblName); static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd); static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, @@ -2281,6 +2283,18 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) else newrel = NULL; + /* + * If we need to rewrite the table, the operation has to be propagated + * to tables that use this table's rowtype as a column type. + * + * (Eventually this will probably become true for scans as well, but + * at the moment a composite type does not enforce any constraints, + * so it's not necessary/appropriate to enforce them just during ALTER.) + */ + if (newrel) + find_composite_type_dependencies(oldrel->rd_rel->reltype, + RelationGetRelationName(oldrel)); + /* * Generate the constraint and default execution states */ @@ -2606,6 +2620,87 @@ ATOneLevelRecursion(List **wqueue, Relation rel, } } + +/* + * find_composite_type_dependencies + * + * Check to see if a table's rowtype is being used as a column in some + * other table (possibly nested several levels deep in composite types!). + * Eventually, we'd like to propagate the check or rewrite operation + * into other such tables, but for now, just error out if we find any. + * + * We assume that functions and views depending on the type are not reasons + * to reject the ALTER. (How safe is this really?) + */ +static void +find_composite_type_dependencies(Oid typeOid, const char *origTblName) +{ + Relation depRel; + ScanKeyData key[2]; + SysScanDesc depScan; + HeapTuple depTup; + + /* + * We scan pg_depend to find those things that depend on the rowtype. + * (We assume we can ignore refobjsubid for a rowtype.) + */ + depRel = relation_openr(DependRelationName, AccessShareLock); + + ScanKeyInit(&key[0], + Anum_pg_depend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelOid_pg_type)); + ScanKeyInit(&key[1], + Anum_pg_depend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(typeOid)); + + depScan = systable_beginscan(depRel, DependReferenceIndex, true, + SnapshotNow, 2, key); + + while (HeapTupleIsValid(depTup = systable_getnext(depScan))) + { + Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup); + Relation rel; + Form_pg_attribute att; + + /* Ignore dependees that aren't user columns of relations */ + /* (we assume system columns are never of rowtypes) */ + if (pg_depend->classid != RelOid_pg_class || + pg_depend->objsubid <= 0) + continue; + + rel = relation_open(pg_depend->objid, AccessShareLock); + att = rel->rd_att->attrs[pg_depend->objsubid - 1]; + + if (rel->rd_rel->relkind == RELKIND_RELATION) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter table \"%s\" because column \"%s\".\"%s\" uses its rowtype", + origTblName, + RelationGetRelationName(rel), + NameStr(att->attname)))); + } + else if (OidIsValid(rel->rd_rel->reltype)) + { + /* + * A view or composite type itself isn't a problem, but we must + * recursively check for indirect dependencies via its rowtype. + */ + find_composite_type_dependencies(rel->rd_rel->reltype, + origTblName); + } + + relation_close(rel, AccessShareLock); + } + + systable_endscan(depScan); + + relation_close(depRel, AccessShareLock); +} + + /* * ALTER TABLE ADD COLUMN *