On tis, 2010-09-21 at 17:53 +0900, KaiGai Kohei wrote:
> Sorry, I missed a bug when we create a typed table using composite
> type which has been altered.

> Perhaps, we also need to patch at transformOfType() to
> skip attributes with attisdropped.

Fixed.

> An additional question. It seems me we can remove all the attributes
> from the composite type, although CREATE TYPE prohibits to create
> a composite type without any attribute.
> What does it mean a composite type with no attribute?
> Or, do we need a restriction to prevent the last one attribute?

We need to allow the creation of zero-attribute types then; same as with
CREATE TABLE.  I have fixed that now.

diff --git a/doc/src/sgml/ref/alter_type.sgml b/doc/src/sgml/ref/alter_type.sgml
index e2fec32..e984e48 100644
--- a/doc/src/sgml/ref/alter_type.sgml
+++ b/doc/src/sgml/ref/alter_type.sgml
@@ -23,9 +23,17 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
+ALTER TYPE <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ]
 ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable> 
+ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
+ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
 ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+
+    ADD ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable>
+    DROP ATTRIBUTE [ IF EXISTS ] <replaceable class="PARAMETER">attribute_name</replaceable>
+    ALTER ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">data_type</replaceable>
 </synopsis>
  </refsynopsisdiv>
 
@@ -34,6 +42,76 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
 
   <para>
    <command>ALTER TYPE</command> changes the definition of an existing type.
+   There are several subforms:
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>ADD ATTRIBUTE</literal></term>
+    <listitem>
+     <para>
+      This form adds a new attribute to a composite type, using the same syntax as
+      <xref linkend="SQL-CREATETYPE">.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>DROP ATTRIBUTE [ IF EXISTS ]</literal></term>
+    <listitem>
+     <para>
+      This form drops an attribute from a composite type.
+      If <literal>IF EXISTS</literal> is specified and the attribute
+      does not exist, no error is thrown. In this case a notice
+      is issued instead.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET DATA TYPE</literal></term>
+    <listitem>
+     <para>
+      This form changes the type of an attribute of a composite type.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OWNER</literal></term>
+    <listitem>
+     <para>
+      This form changes the owner of the type.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RENAME</literal></term>
+    <listitem>
+     <para>
+      This form changes the name of the type or the name of an
+      individual attribute of a composite type.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET SCHEMA</literal></term>
+    <listitem>
+     <para>
+      This form moves the type into another schema.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+  </para>
+
+  <para>
+   The <literal>ADD ATTRIBUTE</literal>, <literal>DROP
+   ATTRIBUTE</literal>, and <literal>ALTER ATTRIBUTE</literal> actions
+   can be combined into a list of multiple alterations to apply in
+   parallel.  For example, it is possible to add several attributes
+   and/or alter the type of several attributes in a single command.
   </para>
 
   <para>
@@ -90,6 +168,34 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replace
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><replaceable class="PARAMETER">attribute_name</replaceable></term>
+      <listitem>
+       <para>
+        The name of the attribute to add, alter, or drop.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">new_attribute_name</replaceable></term>
+      <listitem>
+       <para>
+        The new name of the attribute begin renamed.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">data_type</replaceable></term>
+      <listitem>
+       <para>
+        The data type of the attribute to add, or the new type of the
+        attribute to alter.
+       </para>
+      </listitem>
+     </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
@@ -119,14 +225,30 @@ ALTER TYPE email OWNER TO joe;
 ALTER TYPE email SET SCHEMA customers;
 </programlisting>
   </para>
+
+  <para>
+   To add a new attribute to a type:
+<programlisting>
+ALTER TYPE compfoo ADD ATTRIBUTE f3 int;
+</programlisting>
+  </para>
  </refsect1>
 
  <refsect1>
   <title>Compatibility</title>
-    
+
   <para>
-   There is no <command>ALTER TYPE</command> statement in the SQL
-   standard.
+   The variants to add and drop attributes are part of the SQL
+   standard; the other variants are PostgreSQL extensions.
   </para>
  </refsect1>
+
+ <refsect1 id="SQL-ALTERTYPE-see-also">
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createtype"></member>
+   <member><xref linkend="sql-droptype"></member>
+  </simplelist>
+ </refsect1>
 </refentry>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index b481073..6c497ca 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE TYPE <replaceable class="parameter">name</replaceable> AS
-    ( <replaceable class="PARAMETER">attribute_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] )
+    ( [ <replaceable class="PARAMETER">attribute_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] ] )
 
 CREATE TYPE <replaceable class="parameter">name</replaceable> AS ENUM
     ( [ '<replaceable class="parameter">label</replaceable>' [, ... ] ] )
@@ -768,10 +768,18 @@ CREATE TABLE big_objs (
   <title>Compatibility</title>
 
   <para>
-   This <command>CREATE TYPE</command> command is a
-   <productname>PostgreSQL</productname> extension.  There is a
-   <command>CREATE TYPE</command> statement in the <acronym>SQL</> standard
-   that is rather different in detail.
+   The first form of the <command>CREATE TYPE</command> command, which
+   creates a composite type, conforms to the <acronym>SQL</> standard.
+   The other forms are <productname>PostgreSQL</productname>
+   extensions.  The <command>CREATE TYPE</command> statement in
+   the <acronym>SQL</> standard also defines other forms that are not
+   implemented in <productname>PostgreSQL</>.
+  </para>
+
+  <para>
+   The ability to create a composite type with zero attributes is
+   a <productname>PostgreSQL</productname>-specific deviation from the
+   standard (analogous to <command>CREATE TABLE</command>).
   </para>
  </refsect1>
 
@@ -779,10 +787,10 @@ CREATE TABLE big_objs (
   <title>See Also</title>
 
   <simplelist type="inline">
-   <member><xref linkend="sql-createfunction"></member>
-   <member><xref linkend="sql-droptype"></member>
    <member><xref linkend="sql-altertype"></member>
    <member><xref linkend="sql-createdomain"></member>
+   <member><xref linkend="sql-createfunction"></member>
+   <member><xref linkend="sql-droptype"></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/drop_type.sgml b/doc/src/sgml/ref/drop_type.sgml
index 753ef2b..9096e66 100644
--- a/doc/src/sgml/ref/drop_type.sgml
+++ b/doc/src/sgml/ref/drop_type.sgml
@@ -97,7 +97,7 @@ DROP TYPE box;
    This command is similar to the corresponding command in the SQL
    standard, apart from the <literal>IF EXISTS</>
    option, which is a <productname>PostgreSQL</> extension.
-   But note that the <command>CREATE TYPE</command> command
+   But note that much of the <command>CREATE TYPE</command> command
    and the data type extension mechanisms in
    <productname>PostgreSQL</productname> differ from the SQL standard.
   </para>
@@ -107,8 +107,8 @@ DROP TYPE box;
   <title>See Also</title>
 
   <simplelist type="inline">
-   <member><xref linkend="sql-createtype"></member>
    <member><xref linkend="sql-altertype"></member>
+   <member><xref linkend="sql-createtype"></member>
   </simplelist>
  </refsect1>
 
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 788a4db..9da2690 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -89,6 +89,7 @@ ExecRenameStmt(RenameStmt *stmt)
 		case OBJECT_VIEW:
 		case OBJECT_INDEX:
 		case OBJECT_COLUMN:
+		case OBJECT_ATTRIBUTE:
 		case OBJECT_TRIGGER:
 			{
 				Oid			relid;
@@ -123,6 +124,7 @@ ExecRenameStmt(RenameStmt *stmt)
 							break;
 						}
 					case OBJECT_COLUMN:
+					case OBJECT_ATTRIBUTE:
 						renameatt(relid,
 								  stmt->subname,		/* old att name */
 								  stmt->newname,		/* new att name */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1e5ac13..3f1581e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -263,12 +263,13 @@ static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 static void ATRewriteTables(List **wqueue, LOCKMODE lockmode);
 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
-static void ATSimplePermissions(Relation rel, bool allowView);
+static void ATSimplePermissions(Relation rel, bool allowView, bool allowType);
 static void ATSimplePermissionsRelationOrIndex(Relation rel);
 static void ATSimpleRecursion(List **wqueue, Relation rel,
 				  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,
 				AlterTableCmd *cmd, LOCKMODE lockmode);
 static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
@@ -1978,6 +1979,10 @@ renameatt(Oid myrelid,
 				(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
@@ -2368,8 +2373,13 @@ AlterTable(AlterTableStmt *stmt)
 
 			/*
 			 * For mostly-historical reasons, we allow ALTER TABLE to apply to
-			 * all relation types.
+			 * almost all relation types.
 			 */
+			if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("\"%s\" is not a table",
+								RelationGetRelationName(rel))));
 			break;
 
 		case OBJECT_INDEX:
@@ -2388,6 +2398,14 @@ AlterTable(AlterTableStmt *stmt)
 								RelationGetRelationName(rel))));
 			break;
 
+		case OBJECT_TYPE:
+			if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("\"%s\" is not a composite type",
+								RelationGetRelationName(rel))));
+			break;
+
 		case OBJECT_VIEW:
 			if (rel->rd_rel->relkind != RELKIND_VIEW)
 				ereport(ERROR,
@@ -2639,14 +2657,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 	switch (cmd->subtype)
 	{
 		case AT_AddColumn:		/* ADD COLUMN */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, true);
 			/* Performs own recursion */
 			ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
 			pass = AT_PASS_ADD_COL;
 			break;
 		case AT_AddColumnToView:		/* add column via CREATE OR REPLACE
 										 * VIEW */
-			ATSimplePermissions(rel, true);
+			ATSimplePermissions(rel, true, false);
 			/* Performs own recursion */
 			ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
 			pass = AT_PASS_ADD_COL;
@@ -2659,19 +2677,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			 * substitutes default values into INSERTs before it expands
 			 * rules.
 			 */
-			ATSimplePermissions(rel, true);
+			ATSimplePermissions(rel, true, false);
 			ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
 			/* No command-specific prep needed */
 			pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
 			break;
 		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
 			/* No command-specific prep needed */
 			pass = AT_PASS_DROP;
 			break;
 		case AT_SetNotNull:		/* ALTER COLUMN SET NOT NULL */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
 			/* No command-specific prep needed */
 			pass = AT_PASS_ADD_CONSTR;
@@ -2689,25 +2707,25 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			pass = AT_PASS_MISC;
 			break;
 		case AT_SetStorage:		/* ALTER COLUMN SET STORAGE */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
 			break;
 		case AT_DropColumn:		/* DROP COLUMN */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, true);
 			ATPrepDropColumn(rel, recurse, cmd);
 			/* Recursion occurs during execution phase */
 			pass = AT_PASS_DROP;
 			break;
 		case AT_AddIndex:		/* ADD INDEX */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			/* This command never recurses */
 			/* No command-specific prep needed */
 			pass = AT_PASS_ADD_INDEX;
 			break;
 		case AT_AddConstraint:	/* ADD CONSTRAINT */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			/* Recursion occurs during execution phase */
 			/* No command-specific prep needed except saving recurse flag */
 			if (recurse)
@@ -2715,7 +2733,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			pass = AT_PASS_ADD_CONSTR;
 			break;
 		case AT_DropConstraint:	/* DROP CONSTRAINT */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			/* Recursion occurs during execution phase */
 			/* No command-specific prep needed except saving recurse flag */
 			if (recurse)
@@ -2723,7 +2741,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			pass = AT_PASS_DROP;
 			break;
 		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, true);
 			/* Performs own recursion */
 			ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
 			pass = AT_PASS_ALTER_TYPE;
@@ -2735,20 +2753,20 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_ClusterOn:		/* CLUSTER ON */
 		case AT_DropCluster:	/* SET WITHOUT CLUSTER */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			/* These commands never recurse */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
 			break;
 		case AT_AddOids:		/* SET WITH OIDS */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			/* Performs own recursion */
 			if (!rel->rd_rel->relhasoids || recursing)
 				ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
 			pass = AT_PASS_ADD_COL;
 			break;
 		case AT_DropOids:		/* SET WITHOUT OIDS */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			/* Performs own recursion */
 			if (rel->rd_rel->relhasoids)
 			{
@@ -2775,7 +2793,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			pass = AT_PASS_MISC;
 			break;
 		case AT_AddInherit:		/* INHERIT */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			/* This command never recurses */
 			ATPrepAddInherit(rel);
 			pass = AT_PASS_MISC;
@@ -2793,7 +2811,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_EnableReplicaRule:
 		case AT_DisableRule:
 		case AT_DropInherit:	/* NO INHERIT */
-			ATSimplePermissions(rel, false);
+			ATSimplePermissions(rel, false, false);
 			/* These commands never recurse */
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
@@ -3519,7 +3537,7 @@ ATGetQueueEntry(List **wqueue, Relation rel)
  * - Ensure that it is not a system table
  */
 static void
-ATSimplePermissions(Relation rel, bool allowView)
+ATSimplePermissions(Relation rel, bool allowView, bool allowType)
 {
 	if (rel->rd_rel->relkind != RELKIND_RELATION)
 	{
@@ -3531,6 +3549,14 @@ ATSimplePermissions(Relation rel, bool allowView)
 						 errmsg("\"%s\" is not a table or view",
 								RelationGetRelationName(rel))));
 		}
+		else if (allowType)
+		{
+			if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("\"%s\" is not a table or composite type",
+								RelationGetRelationName(rel))));
+		}
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -3759,6 +3785,44 @@ find_composite_type_dependencies(Oid typeOid,
 
 
 /*
+ * 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.
+ */
+static void
+find_typed_table_dependencies(Oid typeOid, const char *typeName)
+{
+	Relation	classRel;
+	ScanKeyData key[1];
+	HeapScanDesc scan;
+	HeapTuple	tuple;
+
+	classRel = heap_open(RelationRelationId, AccessShareLock);
+
+	ScanKeyInit(&key[0],
+				Anum_pg_class_reloftype,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(typeOid));
+
+	scan = heap_beginscan(classRel, SnapshotNow, 1, key);
+
+	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)));
+	}
+
+	heap_endscan(scan);
+	heap_close(classRel, AccessShareLock);
+}
+
+
+/*
  * ALTER TABLE ADD COLUMN
  *
  * Adds an additional attribute to a relation making the assumption that
@@ -3804,6 +3868,10 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 					 errmsg("column must be added to child tables too")));
 	}
+
+	if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+		find_typed_table_dependencies(rel->rd_rel->reltype,
+									  RelationGetRelationName(rel));
 }
 
 static void
@@ -4007,7 +4075,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	 * defaults, not even for domain-typed columns.  And in any case we
 	 * mustn't invoke Phase 3 on a view, since it has no storage.
 	 */
-	if (relkind != RELKIND_VIEW && attribute.attnum > 0)
+	if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE && attribute.attnum > 0)
 	{
 		defval = (Expr *) build_column_default(rel, attribute.attnum);
 
@@ -4535,6 +4603,10 @@ ATPrepDropColumn(Relation rel, bool recurse, AlterTableCmd *cmd)
 				(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));
+
 	/* No command-specific prep needed except saving recurse flag */
 	if (recurse)
 		cmd->subtype = AT_DropColumnRecurse;
@@ -4554,7 +4626,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
 
 	/* At top level, permission check was done in ATPrepCmd, else do it */
 	if (recursing)
-		ATSimplePermissions(rel, false);
+		ATSimplePermissions(rel, false, true);
 
 	/*
 	 * get the number of the attribute
@@ -4858,7 +4930,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 	/* At top level, permission check was done in ATPrepCmd, else do it */
 	if (recursing)
-		ATSimplePermissions(rel, false);
+		ATSimplePermissions(rel, false, false);
 
 	/*
 	 * Call AddRelationNewConstraints to do the work, making sure it works on
@@ -5801,7 +5873,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
 
 	/* At top level, permission check was done in ATPrepCmd, else do it */
 	if (recursing)
-		ATSimplePermissions(rel, false);
+		ATSimplePermissions(rel, false, false);
 
 	conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
 
@@ -6033,6 +6105,8 @@ ATPrepAlterColumnType(List **wqueue,
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype, false);
 
+	if (tab->relkind == RELKIND_RELATION)
+	{
 	/*
 	 * Set up an expression to transform the old data value to the new type.
 	 * If a USING option was given, transform and use that expression, else
@@ -6103,6 +6177,21 @@ ATPrepAlterColumnType(List **wqueue,
 	newval->expr = (Expr *) transform;
 
 	tab->newvals = lappend(tab->newvals, newval);
+	}
+
+	if (tab->relkind == RELKIND_COMPOSITE_TYPE)
+	{
+		/*
+		 * For composite types, do this check now.  Tables will check
+		 * it later when the table is being rewritten.
+		 */
+		find_composite_type_dependencies(rel->rd_rel->reltype,
+										 NULL,
+										 RelationGetRelationName(rel));
+
+		find_typed_table_dependencies(rel->rd_rel->reltype,
+									  RelationGetRelationName(rel));
+	}
 
 	ReleaseSysCache(tuple);
 
@@ -7367,7 +7456,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 	 * Must be owner of both parent and child -- child was checked by
 	 * ATSimplePermissions call in ATPrepCmd
 	 */
-	ATSimplePermissions(parent_rel, false);
+	ATSimplePermissions(parent_rel, false, false);
 
 	/* Permanent rels cannot inherit from temporary ones */
 	if (parent_rel->rd_istemp && !child_rel->rd_istemp)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7bad4b3..98b3fbc 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1508,11 +1508,6 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
 	Oid			typeNamespace;
 	Oid			relid;
 
-	if (coldeflist == NIL)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-				 errmsg("composite type must have at least one attribute")));
-
 	/*
 	 * now set the parameters for keys/inheritance etc. All of these are
 	 * uninteresting for composite types...
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b38b2b1..0423553 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -131,6 +131,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
 						 List *args, int location);
 static List *mergeTableFuncParameters(List *func_args, List *columns);
 static TypeName *TableFuncTypeName(List *columns);
+static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner);
 
 %}
 
@@ -184,7 +185,7 @@ static TypeName *TableFuncTypeName(List *columns);
 		AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
 		AlterForeignServerStmt AlterGroupStmt
 		AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
-		AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
+		AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
 		AlterRoleStmt AlterRoleSetStmt
 		AlterDefaultPrivilegesStmt DefACLAction
 		AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
@@ -218,8 +219,8 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <node>	alter_column_default opclass_item opclass_drop alter_using
 %type <ival>	add_drop opt_asc_desc opt_nulls_order
 
-%type <node>	alter_table_cmd
-%type <list>	alter_table_cmds
+%type <node>	alter_table_cmd alter_type_cmd
+%type <list>	alter_table_cmds alter_type_cmds
 
 %type <dbehavior>	opt_drop_behavior
 
@@ -295,7 +296,7 @@ static TypeName *TableFuncTypeName(List *columns);
 				reloption_list group_clause TriggerFuncArgs select_limit
 				opt_select_limit opclass_item_list opclass_drop_list
 				opt_opfamily transaction_mode_list_or_empty
-				TableFuncElementList opt_type_modifiers
+				OptTableFuncElementList TableFuncElementList opt_type_modifiers
 				prep_type_clause
 				execute_param_clause using_clause returning_clause
 				opt_enum_val_list enum_val_list table_func_column_list
@@ -462,7 +463,7 @@ static TypeName *TableFuncTypeName(List *columns);
 /* ordinary key words in alphabetical order */
 %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
 	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
-	ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION
+	ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
 
 	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
 	BOOLEAN_P BOTH BY
@@ -657,6 +658,7 @@ stmt :
 			| AlterOwnerStmt
 			| AlterSeqStmt
 			| AlterTableStmt
+			| AlterCompositeTypeStmt
 			| AlterRoleSetStmt
 			| AlterRoleStmt
 			| AlterTSConfigurationStmt
@@ -1970,6 +1972,72 @@ reloption_elem:
 
 /*****************************************************************************
  *
+ *	ALTER TYPE
+ *
+ * really variants of the ALTER TABLE subcommands with different spellings
+ *****************************************************************************/
+
+AlterCompositeTypeStmt:
+			ALTER TYPE_P any_name alter_type_cmds
+				{
+					AlterTableStmt *n = makeNode(AlterTableStmt);
+
+					/* can't use qualified_name, sigh */
+					n->relation = makeRangeVarFromAnyName($3, @3, yyscanner);
+					n->cmds = $4;
+					n->relkind = OBJECT_TYPE;
+					$$ = (Node *)n;
+				}
+			;
+
+alter_type_cmds:
+			alter_type_cmd							{ $$ = list_make1($1); }
+			| alter_type_cmds ',' alter_type_cmd	{ $$ = lappend($1, $3); }
+		;
+
+alter_type_cmd:
+			/* ALTER TYPE <name> ADD ATTRIBUTE <coldef> */
+			ADD_P ATTRIBUTE TableFuncElement
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_AddColumn;
+					n->def = $3;
+					$$ = (Node *)n;
+				}
+			/* ALTER TYPE <name> DROP ATTRIBUTE IF EXISTS <attname> */
+			| DROP ATTRIBUTE IF_P EXISTS ColId
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_DropColumn;
+					n->name = $5;
+					n->behavior = DROP_RESTRICT; /* currently no effect */
+					n->missing_ok = TRUE;
+					$$ = (Node *)n;
+				}
+			/* ALTER TYPE <name> DROP ATTRIBUTE <attname> */
+			| DROP ATTRIBUTE ColId opt_drop_behavior
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_DropColumn;
+					n->name = $3;
+					n->behavior = DROP_RESTRICT; /* currently no effect */
+					n->missing_ok = FALSE;
+					$$ = (Node *)n;
+				}
+			/* ALTER TYPE <name> ALTER ATTRIBUTE <attname> [SET DATA] TYPE <typename> */
+			| ALTER ATTRIBUTE ColId opt_set_data TYPE_P Typename
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_AlterColumnType;
+					n->name = $3;
+					n->def = (Node *) $6;
+					$$ = (Node *)n;
+				}
+		;
+
+
+/*****************************************************************************
+ *
  *		QUERY :
  *				close <portalname>
  *
@@ -3678,39 +3746,12 @@ DefineStmt:
 					n->definition = NIL;
 					$$ = (Node *)n;
 				}
-			| CREATE TYPE_P any_name AS '(' TableFuncElementList ')'
+			| CREATE TYPE_P any_name AS '(' OptTableFuncElementList ')'
 				{
 					CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
-					RangeVar *r = makeNode(RangeVar);
 
 					/* can't use qualified_name, sigh */
-					switch (list_length($3))
-					{
-						case 1:
-							r->catalogname = NULL;
-							r->schemaname = NULL;
-							r->relname = strVal(linitial($3));
-							break;
-						case 2:
-							r->catalogname = NULL;
-							r->schemaname = strVal(linitial($3));
-							r->relname = strVal(lsecond($3));
-							break;
-						case 3:
-							r->catalogname = strVal(linitial($3));
-							r->schemaname = strVal(lsecond($3));
-							r->relname = strVal(lthird($3));
-							break;
-						default:
-							ereport(ERROR,
-									(errcode(ERRCODE_SYNTAX_ERROR),
-									 errmsg("improper qualified name (too many dotted names): %s",
-											NameListToString($3)),
-											parser_errposition(@3)));
-							break;
-					}
-					r->location = @3;
-					n->typevar = r;
+					n->typevar = makeRangeVarFromAnyName($3, @3, yyscanner);
 					n->coldeflist = $6;
 					$$ = (Node *)n;
 				}
@@ -5836,6 +5877,15 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 					n->newname = $6;
 					$$ = (Node *)n;
 				}
+			| ALTER TYPE_P any_name RENAME ATTRIBUTE name TO name
+				{
+					RenameStmt *n = makeNode(RenameStmt);
+					n->renameType = OBJECT_ATTRIBUTE;
+					n->relation = makeRangeVarFromAnyName($3, @3, yyscanner);
+					n->subname = $6;
+					n->newname = $8;
+					$$ = (Node *)n;
+				}
 		;
 
 opt_column: COLUMN									{ $$ = COLUMN; }
@@ -8216,6 +8266,11 @@ where_or_current_clause:
 		;
 
 
+OptTableFuncElementList:
+			TableFuncElementList				{ $$ = $1; }
+			| /*EMPTY*/							{ $$ = NIL; }
+		;
+
 TableFuncElementList:
 			TableFuncElement
 				{
@@ -10897,6 +10952,7 @@ unreserved_keyword:
 			| ASSERTION
 			| ASSIGNMENT
 			| AT
+			| ATTRIBUTE
 			| BACKWARD
 			| BEFORE
 			| BEGIN_P
@@ -11858,6 +11914,47 @@ TableFuncTypeName(List *columns)
 }
 
 /*
+ * Convert a list of (dotted) names to a RangeVar (like
+ * makeRangeVarFromNameList, but with position support).  The
+ * "AnyName" refers to the any_name production in the grammar.
+ */
+static RangeVar *
+makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner)
+{
+	RangeVar *r = makeNode(RangeVar);
+
+	switch (list_length(names))
+	{
+		case 1:
+			r->catalogname = NULL;
+			r->schemaname = NULL;
+			r->relname = strVal(linitial(names));
+			break;
+		case 2:
+			r->catalogname = NULL;
+			r->schemaname = strVal(linitial(names));
+			r->relname = strVal(lsecond(names));
+			break;
+		case 3:
+			r->catalogname = strVal(linitial(names));;
+			r->schemaname = strVal(lsecond(names));
+			r->relname = strVal(lthird(names));
+			break;
+		default:
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("improper qualified name (too many dotted names): %s",
+							NameListToString(names)),
+					 parser_errposition(position)));
+			break;
+	}
+
+	r->location = position;
+
+	return r;
+}
+
+/*
  * Must undefine this stuff before including scan.c, since it has different
  * definitions for these macros.
  */
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index af25c21..0d0a0ac 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -839,6 +839,9 @@ transformOfType(ParseState *pstate, CreateStmtContext *cxt, TypeName *ofTypename
 		Form_pg_attribute attr = tupdesc->attrs[i];
 		ColumnDef  *n = makeNode(ColumnDef);
 
+		if (attr->attisdropped)
+			continue;
+
 		n->colname = pstrdup(NameStr(attr->attname));
 		n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
 		n->constraints = NULL;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 44cc401..0b46247 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1657,6 +1657,7 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_TSCONFIGURATION:
 					tag = "ALTER TEXT SEARCH CONFIGURATION";
 					break;
+				case OBJECT_ATTRIBUTE:
 				case OBJECT_TYPE:
 					tag = "ALTER TYPE";
 					break;
@@ -1780,6 +1781,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_SEQUENCE:
 					tag = "ALTER SEQUENCE";
 					break;
+				case OBJECT_TYPE:
+					tag = "ALTER TYPE";
+					break;
 				case OBJECT_VIEW:
 					tag = "ALTER VIEW";
 					break;
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index e5ea553..62de203 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1253,15 +1253,45 @@ psql_completion(char *text, int start, int end)
 		COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3);
 	}
 
-	/* complete ALTER TYPE <foo> with OWNER TO, SET SCHEMA */
+	/* complete ALTER TYPE <foo> with actions */
 	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 			 pg_strcasecmp(prev2_wd, "TYPE") == 0)
 	{
 		static const char *const list_ALTERTYPE[] =
-		{"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
+		{"ADD ATTRIBUTE", "ALTER ATTRIBUTE", "DROP ATTRIBUTE",
+		 "OWNER TO", "RENAME", "SET SCHEMA", NULL};
 
 		COMPLETE_WITH_LIST(list_ALTERTYPE);
 	}
+	/* ALTER TYPE <foo> RENAME  */
+	else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+			 pg_strcasecmp(prev3_wd, "TYPE") == 0 &&
+			 pg_strcasecmp(prev_wd, "RENAME") == 0)
+	{
+		static const char *const list_ALTERTYPE[] =
+		{"ATTRIBUTE", "TO", NULL};
+
+		COMPLETE_WITH_LIST(list_ALTERTYPE);
+	}
+	/* ALTER TYPE xxx RENAME ATTRIBUTE yyy */
+	else if (pg_strcasecmp(prev5_wd, "TYPE") == 0 &&
+			 pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
+			 pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0)
+		COMPLETE_WITH_CONST("TO");
+
+	/* If we have TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list of attributes */
+	else if (pg_strcasecmp(prev4_wd, "TYPE") == 0 &&
+			 (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
+			  pg_strcasecmp(prev2_wd, "DROP") == 0 ||
+			  pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
+			 pg_strcasecmp(prev_wd, "ATTRIBUTE") == 0)
+		COMPLETE_WITH_ATTR(prev3_wd, "");
+	/* ALTER TYPE ALTER ATTRIBUTE <foo> */
+	else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+			  pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0))
+	{
+		COMPLETE_WITH_CONST("TYPE");
+	}
 	/* complete ALTER GROUP <foo> */
 	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 			 pg_strcasecmp(prev2_wd, "GROUP") == 0)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e896dc7..0543576 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1046,6 +1046,7 @@ typedef struct SetOperationStmt
 typedef enum ObjectType
 {
 	OBJECT_AGGREGATE,
+	OBJECT_ATTRIBUTE,			/* type's attribute, when distinct from column */
 	OBJECT_CAST,
 	OBJECT_COLUMN,
 	OBJECT_CONSTRAINT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 974bb7a..51c3b1f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -49,6 +49,7 @@ PG_KEYWORD("assertion", ASSERTION, UNRESERVED_KEYWORD)
 PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD)
 PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD)
 PG_KEYWORD("at", AT, UNRESERVED_KEYWORD)
+PG_KEYWORD("attribute", ATTRIBUTE, UNRESERVED_KEYWORD)
 PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD)
 PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD)
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 83e24fd..ab19a8e 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -854,7 +854,7 @@ select * from myview;
 (0 rows)
 
 alter table myview drop d;
-ERROR:  "myview" is not a table
+ERROR:  "myview" is not a table or composite type
 drop view myview;
 -- test some commands to make sure they fail on the dropped column
 analyze atacc1(a);
@@ -1472,6 +1472,11 @@ select * from another;
 (3 rows)
 
 drop table another;
+-- table's row type
+create table tab1 (a int, b text);
+create table tab2 (x int, y tab1);
+alter table tab1 alter column b type varchar; -- fails
+ERROR:  cannot alter table "tab1" because column "tab2"."y" uses its rowtype
 --
 -- lock levels
 --
@@ -1683,3 +1688,85 @@ drop cascades to view alter2.v1
 drop cascades to function alter2.plus1(integer)
 drop cascades to type alter2.posint
 drop cascades to type alter2.ctype
+--
+-- composite types
+--
+CREATE TYPE test_type AS (a int);
+\d test_type
+Composite type "public.test_type"
+ Column |  Type   
+--------+---------
+ a      | integer
+
+ALTER TYPE nosuchtype ADD ATTRIBUTE b text; -- fails
+ERROR:  relation "nosuchtype" does not exist
+ALTER TYPE test_type ADD ATTRIBUTE b text;
+\d test_type
+Composite type "public.test_type"
+ Column |  Type   
+--------+---------
+ a      | integer
+ b      | text
+
+ALTER TYPE test_type ADD ATTRIBUTE b text; -- fails
+ERROR:  column "b" of relation "test_type" already exists
+ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE varchar;
+\d test_type
+Composite type "public.test_type"
+ Column |       Type        
+--------+-------------------
+ a      | integer
+ b      | character varying
+
+ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE integer;
+\d test_type
+Composite type "public.test_type"
+ Column |  Type   
+--------+---------
+ a      | integer
+ b      | integer
+
+ALTER TYPE test_type DROP ATTRIBUTE b;
+\d test_type
+Composite type "public.test_type"
+ Column |  Type   
+--------+---------
+ a      | integer
+
+ALTER TYPE test_type DROP ATTRIBUTE c; -- fails
+ERROR:  column "c" of relation "test_type" does not exist
+ALTER TYPE test_type DROP ATTRIBUTE IF EXISTS c;
+NOTICE:  column "c" of relation "test_type" does not exist, skipping
+ALTER TYPE test_type DROP ATTRIBUTE a, ADD ATTRIBUTE d boolean;
+\d test_type
+Composite type "public.test_type"
+ Column |  Type   
+--------+---------
+ d      | boolean
+
+ALTER TYPE test_type RENAME ATTRIBUTE a TO aa;
+ERROR:  column "a" does not exist
+ALTER TYPE test_type RENAME ATTRIBUTE d TO dd;
+\d test_type
+Composite type "public.test_type"
+ Column |  Type   
+--------+---------
+ dd     | boolean
+
+DROP TYPE test_type;
+CREATE TYPE test_type1 AS (a int, b text);
+CREATE TABLE test_tbl1 (x int, y test_type1);
+ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
+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;
+ALTER TYPE test_type2 ADD ATTRIBUTE c text; -- fails
+ERROR:  cannot alter type "test_type2" because it is the type of a typed table
+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
+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
+ERROR:  cannot alter type "test_type2" because it is the type of a typed table
+CREATE TYPE test_type_empty AS ();
+DROP TYPE test_type_empty;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 760670c..3e1646a 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1090,6 +1090,11 @@ select * from another;
 
 drop table another;
 
+-- table's row type
+create table tab1 (a int, b text);
+create table tab2 (x int, y tab1);
+alter table tab1 alter column b type varchar; -- fails
+
 --
 -- lock levels
 --
@@ -1224,3 +1229,53 @@ select alter2.plus1(41);
 
 -- clean up
 drop schema alter2 cascade;
+
+--
+-- composite types
+--
+
+CREATE TYPE test_type AS (a int);
+\d test_type
+
+ALTER TYPE nosuchtype ADD ATTRIBUTE b text; -- fails
+
+ALTER TYPE test_type ADD ATTRIBUTE b text;
+\d test_type
+
+ALTER TYPE test_type ADD ATTRIBUTE b text; -- fails
+
+ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE varchar;
+\d test_type
+
+ALTER TYPE test_type ALTER ATTRIBUTE b SET DATA TYPE integer;
+\d test_type
+
+ALTER TYPE test_type DROP ATTRIBUTE b;
+\d test_type
+
+ALTER TYPE test_type DROP ATTRIBUTE c; -- fails
+
+ALTER TYPE test_type DROP ATTRIBUTE IF EXISTS c;
+
+ALTER TYPE test_type DROP ATTRIBUTE a, ADD ATTRIBUTE d boolean;
+\d test_type
+
+ALTER TYPE test_type RENAME ATTRIBUTE a TO aa;
+ALTER TYPE test_type RENAME ATTRIBUTE d TO dd;
+\d test_type
+
+DROP TYPE test_type;
+
+CREATE TYPE test_type1 AS (a int, b text);
+CREATE TABLE test_tbl1 (x int, y test_type1);
+ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
+
+CREATE TYPE test_type2 AS (a int, b text);
+CREATE TABLE test_tbl2 OF test_type2;
+ALTER TYPE test_type2 ADD ATTRIBUTE c text; -- fails
+ALTER TYPE test_type2 ALTER ATTRIBUTE b TYPE varchar; -- fails
+ALTER TYPE test_type2 DROP ATTRIBUTE b; -- fails
+ALTER TYPE test_type2 RENAME ATTRIBUTE b TO bb; -- fails
+
+CREATE TYPE test_type_empty AS ();
+DROP TYPE test_type_empty;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to