AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
 static void ATOneLevelRecursion(List **wqueue, Relation rel,
                    AlterTableCmd *cmd, LOCKMODE lockmode);
-static void find_typed_table_dependencies(Oid typeOid, const char *typeName);
-static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
+static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
+                                 LOCKMODE lockmode);
+static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
+                                          DropBehavior behavior);
+static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
                AlterTableCmd *cmd, LOCKMODE lockmode);
 static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
                ColumnDef *colDef, bool isOid, LOCKMODE lockmode);
                 Node *options, bool isReset, LOCKMODE lockmode);
 static void ATExecSetStorage(Relation rel, const char *colName,
                 Node *newValue, LOCKMODE lockmode);
-static void ATPrepDropColumn(Relation rel, bool recurse, AlterTableCmd *cmd);
+static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
+                            AlterTableCmd *cmd, LOCKMODE lockmode);
 static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                 DropBehavior behavior,
                 bool recurse, bool recursing,
 
 
 /*
- *     renameatt       - changes the name of a attribute in a relation
+ *     renameatt_internal      - workhorse for renameatt
  */
-void
-renameatt(Oid myrelid,
-         const char *oldattname,
-         const char *newattname,
-         bool recurse,
-         int expected_parents)
+static void
+renameatt_internal(Oid myrelid,
+                  const char *oldattname,
+                  const char *newattname,
+                  bool recurse,
+                  bool recursing,
+                  int expected_parents,
+                  DropBehavior behavior)
 {
    Relation    targetrelation;
    Relation    attrelation;
     */
    targetrelation = relation_open(myrelid, AccessExclusiveLock);
 
-   if (targetrelation->rd_rel->reloftype)
+   if (targetrelation->rd_rel->reloftype && !recursing)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("cannot rename column of typed table")));
 
-   if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
-       find_typed_table_dependencies(targetrelation->rd_rel->reltype,
-                                     RelationGetRelationName(targetrelation));
-
    /*
     * Renaming the columns of sequences or toast tables doesn't actually
     * break anything from the system's point of view, since internal
            if (childrelid == myrelid)
                continue;
            /* note we need not recurse again */
-           renameatt(childrelid, oldattname, newattname, false, numparents);
+           renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
        }
    }
    else
                            oldattname)));
    }
 
+   /* rename attributes in typed tables of composite type */
+   if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+   {
+       List       *child_oids;
+       ListCell   *lo;
+
+       child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
+                                                  RelationGetRelationName(targetrelation),
+                                                  behavior);
+
+       foreach(lo, child_oids)
+           renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
+   }
+
    attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
 
    atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
 }
 
 
+/*
+ *     renameatt       - changes the name of a attribute in a relation
+ */
+void
+renameatt(Oid myrelid, RenameStmt *stmt)
+{
+   renameatt_internal(myrelid,
+                      stmt->subname,       /* old att name */
+                      stmt->newname,       /* new att name */
+                      interpretInhOption(stmt->relation->inhOpt),  /* recursive? */
+                      false,  /* recursing? */
+                      0,   /* expected inhcount */
+                      stmt->behavior);
+}
+
+
 /*
  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW RENAME
  *
        case AT_AddColumn:      /* ADD COLUMN */
            ATSimplePermissions(rel, false, true);
            /* Performs own recursion */
-           ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
+           ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
            pass = AT_PASS_ADD_COL;
            break;
        case AT_AddColumnToView:        /* add column via CREATE OR REPLACE
                                         * VIEW */
            ATSimplePermissions(rel, true, false);
            /* Performs own recursion */
-           ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
+           ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
            pass = AT_PASS_ADD_COL;
            break;
        case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
            break;
        case AT_DropColumn:     /* DROP COLUMN */
            ATSimplePermissions(rel, false, true);
-           ATPrepDropColumn(rel, recurse, cmd);
+           ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
            /* Recursion occurs during execution phase */
            pass = AT_PASS_DROP;
            break;
    }
 }
 
+/*
+ * ATTypedTableRecursion
+ *
+ * Propagate ALTER TYPE operations to the typed tables of that type.
+ * Also check the RESTRICT/CASCADE behavior.
+ */
+static void
+ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
+                     LOCKMODE lockmode)
+{
+   ListCell   *child;
+   List       *children;
+
+   Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
+
+   children = find_typed_table_dependencies(rel->rd_rel->reltype,
+                                            RelationGetRelationName(rel),
+                                            cmd->behavior);
+
+   foreach(child, children)
+   {
+       Oid         childrelid = lfirst_oid(child);
+       Relation    childrel;
+
+       childrel = relation_open(childrelid, lockmode);
+       CheckTableNotInUse(childrel, "ALTER TABLE");
+       ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
+       relation_close(childrel, NoLock);
+   }
+}
+
 
 /*
  * find_composite_type_dependencies
  * find_typed_table_dependencies
  *
  * Check to see if a composite type is being used as the type of a
- * typed table.  Eventually, we'd like to propagate the alter
- * operation into such tables, but for now, just error out if we find
- * any.
+ * typed table.  Abort if any are found and behavior is RESTRICT.
+ * Else return the list of tables.
  */
-static void
-find_typed_table_dependencies(Oid typeOid, const char *typeName)
+static List *
+find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
 {
    Relation    classRel;
    ScanKeyData key[1];
    HeapScanDesc scan;
    HeapTuple   tuple;
+   List       *result = NIL;
 
    classRel = heap_open(RelationRelationId, AccessShareLock);
 
 
    if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
    {
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("cannot alter type \"%s\" because it is the type of a typed table",
-                       typeName)));
+       if (behavior == DROP_RESTRICT)
+           ereport(ERROR,
+                   (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+                    errmsg("cannot alter type \"%s\" because it is the type of a typed table",
+                           typeName),
+                    errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
+       else
+           result = lappend_oid(result, HeapTupleGetOid(tuple));
    }
 
    heap_endscan(scan);
    heap_close(classRel, AccessShareLock);
+
+   return result;
 }
 
 
  * AlterTableCmd's.
  */
 static void
-ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
+ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
                AlterTableCmd *cmd, LOCKMODE lockmode)
 {
-   if (rel->rd_rel->reloftype)
+   if (rel->rd_rel->reloftype && !recursing)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("cannot add column to typed table")));
    }
 
    if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
-       find_typed_table_dependencies(rel->rd_rel->reltype,
-                                     RelationGetRelationName(rel));
+       ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
 }
 
 static void
        cdef->storage = 0;
        cmd->def = (Node *) cdef;
    }
-   ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
+   ATPrepAddColumn(wqueue, rel, recurse, false, cmd, lockmode);
 }
 
 /*
  * correctly.)
  */
 static void
-ATPrepDropColumn(Relation rel, bool recurse, AlterTableCmd *cmd)
+ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
+                AlterTableCmd *cmd, LOCKMODE lockmode)
 {
-   if (rel->rd_rel->reloftype)
+   if (rel->rd_rel->reloftype && !recursing)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("cannot drop column from typed table")));
 
    if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
-       find_typed_table_dependencies(rel->rd_rel->reltype,
-                                     RelationGetRelationName(rel));
+       ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
 
-   /* No command-specific prep needed except saving recurse flag */
    if (recurse)
        cmd->subtype = AT_DropColumnRecurse;
 }
    NewColumnValue *newval;
    ParseState *pstate = make_parsestate(NULL);
 
-   if (rel->rd_rel->reloftype)
+   if (rel->rd_rel->reloftype && !recursing)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("cannot alter column type of typed table")));
        find_composite_type_dependencies(rel->rd_rel->reltype,
                                         NULL,
                                         RelationGetRelationName(rel));
-
-       find_typed_table_dependencies(rel->rd_rel->reltype,
-                                     RelationGetRelationName(rel));
    }
 
    ReleaseSysCache(tuple);
                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                 errmsg("type of inherited column \"%s\" must be changed in child tables too",
                        colName)));
+
+   if (tab->relkind == RELKIND_COMPOSITE_TYPE)
+       ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
 }
 
 static void
 
 ERROR:  cannot alter type "test_type1" because column "test_tbl1"."y" uses it
 CREATE TYPE test_type2 AS (a int, b text);
 CREATE TABLE test_tbl2 OF test_type2;
+\d test_type2
+Composite type "public.test_type2"
+ Column |  Type   
+--------+---------
+ a      | integer
+ b      | text
+
+\d test_tbl2
+   Table "public.test_tbl2"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ b      | text    | 
+Typed table of type: test_type2
+
 ALTER TYPE test_type2 ADD ATTRIBUTE c text; -- fails
 ERROR:  cannot alter type "test_type2" because it is the type of a typed table
+HINT:  Use ALTER ... CASCADE to alter the typed tables too.
+ALTER TYPE test_type2 ADD ATTRIBUTE c text CASCADE;
+\d test_type2
+Composite type "public.test_type2"
+ Column |  Type   
+--------+---------
+ a      | integer
+ b      | text
+ c      | text
+
+\d test_tbl2
+   Table "public.test_tbl2"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ b      | text    | 
+ c      | text    | 
+Typed table of type: test_type2
+
 ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar; -- fails
 ERROR:  cannot alter type "test_type2" because it is the type of a typed table
+HINT:  Use ALTER ... CASCADE to alter the typed tables too.
+ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar CASCADE;
+\d test_type2
+Composite type "public.test_type2"
+ Column |       Type        
+--------+-------------------
+ a      | integer
+ b      | character varying
+ c      | text
+
+\d test_tbl2
+        Table "public.test_tbl2"
+ Column |       Type        | Modifiers 
+--------+-------------------+-----------
+ a      | integer           | 
+ b      | character varying | 
+ c      | text              | 
+Typed table of type: test_type2
+
 ALTER TYPE test_type2 DROP ATTRIBUTE b; -- fails
 ERROR:  cannot alter type "test_type2" because it is the type of a typed table
-ALTER TYPE test_type2 RENAME ATTRIBUTE b TO bb; -- fails
+HINT:  Use ALTER ... CASCADE to alter the typed tables too.
+ALTER TYPE test_type2 DROP ATTRIBUTE b CASCADE;
+\d test_type2
+Composite type "public.test_type2"
+ Column |  Type   
+--------+---------
+ a      | integer
+ c      | text
+
+\d test_tbl2
+   Table "public.test_tbl2"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ c      | text    | 
+Typed table of type: test_type2
+
+ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa; -- fails
 ERROR:  cannot alter type "test_type2" because it is the type of a typed table
+HINT:  Use ALTER ... CASCADE to alter the typed tables too.
+ALTER TYPE test_type2 RENAME ATTRIBUTE a TO aa CASCADE;
+\d test_type2
+Composite type "public.test_type2"
+ Column |  Type   
+--------+---------
+ aa     | integer
+ c      | text
+
+\d test_tbl2
+   Table "public.test_tbl2"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ aa     | integer | 
+ c      | text    | 
+Typed table of type: test_type2
+
 CREATE TYPE test_type_empty AS ();
 DROP TYPE test_type_empty;