Add ALTER TYPE ... ADD/DROP/ALTER/RENAME ATTRIBUTE
authorPeter Eisentraut <peter_e@gmx.net>
Sun, 26 Sep 2010 11:41:03 +0000 (14:41 +0300)
committerPeter Eisentraut <peter_e@gmx.net>
Sun, 26 Sep 2010 11:41:03 +0000 (14:41 +0300)
Like with tables, this also requires allowing the existence of
composite types with zero attributes.

reviewed by KaiGai Kohei

15 files changed:
doc/src/sgml/ref/alter_type.sgml
doc/src/sgml/ref/create_type.sgml
doc/src/sgml/ref/drop_type.sgml
src/backend/commands/alter.c
src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/backend/tcop/utility.c
src/bin/pg_dump/pg_dump.c
src/bin/psql/tab-complete.c
src/include/nodes/parsenodes.h
src/include/parser/kwlist.h
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index d76feb48c30ee2108a4a4f2e10d3524f9be3cf0c..315922ea8365c482cdd787d1a775e0220ba1625c 100644 (file)
@@ -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>
@@ -117,16 +223,32 @@ ALTER TYPE email OWNER TO joe;
    to <literal>customers</literal>:
 <programlisting>
 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>
index ca521b387b341dfee96c6c25b158789a0d0cbc7c..a3c75b51d0fb3f461005249b9fbfc65a12adf35a 100644 (file)
@@ -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>
 
index 10e3e143bc2de43ac8c8fb0ac1b1b0704c9d11fd..76e31517a1a6bf252da0585f865b56b44b6cf7df 100644 (file)
@@ -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>
 
index a315096fa4392928ffe6cc9510b44b5f08b927ab..794d355944d3bec337deef3f4fa59336587c2fd3 100644 (file)
@@ -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 */
index 82143682d582d7b9110006c83d3a98d0d2d1c18e..a5d7af604209c9fa9bc23a981f402011bf2cba5f 100644 (file)
@@ -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),
@@ -3758,6 +3784,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
  *
@@ -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,76 +6105,93 @@ ATPrepAlterColumnType(List **wqueue,
        /* make sure datatype is legal for a column */
        CheckAttributeType(colName, targettype, false);
 
-       /*
-        * 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
-        * just take the old value and try to coerce it.  We do this first so that
-        * type incompatibility can be detected before we waste effort, and
-        * because we need the expression to be parsed against the original table
-        * rowtype.
-        */
-       if (cmd->transform)
+       if (tab->relkind == RELKIND_RELATION)
        {
-               RangeTblEntry *rte;
+               /*
+                * 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
+                * just take the old value and try to coerce it.  We do this first so that
+                * type incompatibility can be detected before we waste effort, and
+                * because we need the expression to be parsed against the original table
+                * rowtype.
+                */
+               if (cmd->transform)
+               {
+                       RangeTblEntry *rte;
 
-               /* Expression must be able to access vars of old table */
-               rte = addRangeTableEntryForRelation(pstate,
-                                                                                       rel,
-                                                                                       NULL,
-                                                                                       false,
-                                                                                       true);
-               addRTEtoQuery(pstate, rte, false, true, true);
+                       /* Expression must be able to access vars of old table */
+                       rte = addRangeTableEntryForRelation(pstate,
+                                                                                               rel,
+                                                                                               NULL,
+                                                                                               false,
+                                                                                               true);
+                       addRTEtoQuery(pstate, rte, false, true, true);
 
-               transform = transformExpr(pstate, cmd->transform);
+                       transform = transformExpr(pstate, cmd->transform);
 
-               /* It can't return a set */
-               if (expression_returns_set(transform))
+                       /* It can't return a set */
+                       if (expression_returns_set(transform))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("transform expression must not return a set")));
+
+                       /* No subplans or aggregates, either... */
+                       if (pstate->p_hasSubLinks)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("cannot use subquery in transform expression")));
+                       if (pstate->p_hasAggs)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_GROUPING_ERROR),
+                                                errmsg("cannot use aggregate function in transform expression")));
+                       if (pstate->p_hasWindowFuncs)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_WINDOWING_ERROR),
+                                                errmsg("cannot use window function in transform expression")));
+               }
+               else
+               {
+                       transform = (Node *) makeVar(1, attnum,
+                                                                                attTup->atttypid, attTup->atttypmod,
+                                                                                0);
+               }
+
+               transform = coerce_to_target_type(pstate,
+                                                                                 transform, exprType(transform),
+                                                                                 targettype, targettypmod,
+                                                                                 COERCION_ASSIGNMENT,
+                                                                                 COERCE_IMPLICIT_CAST,
+                                                                                 -1);
+               if (transform == NULL)
                        ereport(ERROR,
                                        (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                        errmsg("transform expression must not return a set")));
+                                        errmsg("column \"%s\" cannot be cast to type %s",
+                                                       colName, format_type_be(targettype))));
 
-               /* No subplans or aggregates, either... */
-               if (pstate->p_hasSubLinks)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("cannot use subquery in transform expression")));
-               if (pstate->p_hasAggs)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_GROUPING_ERROR),
-                       errmsg("cannot use aggregate function in transform expression")));
-               if (pstate->p_hasWindowFuncs)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_WINDOWING_ERROR),
-                         errmsg("cannot use window function in transform expression")));
-       }
-       else
-       {
-               transform = (Node *) makeVar(1, attnum,
-                                                                        attTup->atttypid, attTup->atttypmod,
-                                                                        0);
-       }
+               /*
+                * Add a work queue item to make ATRewriteTable update the column
+                * contents.
+                */
+               newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
+               newval->attnum = attnum;
+               newval->expr = (Expr *) transform;
 
-       transform = coerce_to_target_type(pstate,
-                                                                         transform, exprType(transform),
-                                                                         targettype, targettypmod,
-                                                                         COERCION_ASSIGNMENT,
-                                                                         COERCE_IMPLICIT_CAST,
-                                                                         -1);
-       if (transform == NULL)
-               ereport(ERROR,
-                               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                errmsg("column \"%s\" cannot be cast to type %s",
-                                               colName, format_type_be(targettype))));
+               tab->newvals = lappend(tab->newvals, newval);
+       }
 
-       /*
-        * Add a work queue item to make ATRewriteTable update the column
-        * contents.
-        */
-       newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
-       newval->attnum = attnum;
-       newval->expr = (Expr *) transform;
+       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));
 
-       tab->newvals = lappend(tab->newvals, newval);
+               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)
index 87fcf29bf2ff78174d8977b317c8fc7e68e51067..25503bda4f48661575ea5782c088b41dd92b8831 100644 (file)
@@ -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...
index 22ce35382bc65566ed1b6e02b3ce57885649cfa3..40bd7a393251cd2ed2b3c6f713ce1ce0cc47a599 100644 (file)
@@ -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
@@ -1968,6 +1970,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 :
@@ -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
@@ -11857,6 +11913,47 @@ TableFuncTypeName(List *columns)
        return result;
 }
 
+/*
+ * 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.
index 552a6af8f874563fabf87f1d0c1f9da7fc0aca32..37ca331c215661fde72d5e0a4bc3be613c801588 100644 (file)
@@ -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;
index 1865f843d2a0807ff468c5252ee81cc4cbc7cfb5..f69559f0e438849d2c3b5d709b7e0313ee4c2fe0 100644 (file)
@@ -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;
index 06c1f3ae605103593f2af3d9a8d858b098658914..2e1486f221680f435c77c313465dbd2c625ed6b7 100644 (file)
@@ -7249,13 +7249,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
        res = PQexec(g_conn, query->data);
        check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
 
-       /* Expecting at least a single result */
        ntups = PQntuples(res);
-       if (ntups < 1)
-       {
-               write_msg(NULL, "query returned no rows: %s\n", query->data);
-               exit_nicely();
-       }
 
        i_attname = PQfnumber(res, "attname");
        i_atttypdefn = PQfnumber(res, "atttypdefn");
@@ -7356,12 +7350,12 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo)
        res = PQexec(g_conn, query->data);
        check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
 
-       /* Expecting at least a single result */
        ntups = PQntuples(res);
        if (ntups < 1)
        {
-               write_msg(NULL, "query returned no rows: %s\n", query->data);
-               exit_nicely();
+               PQclear(res);
+               destroyPQExpBuffer(query);
+               return;
        }
 
        pgClassOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "tableoid")));
index 3efa97ad88f3160d9757d1a15e77a5c64dd0e688..04c81844da59e1f4a288b82f100c6a82a8b270f2 100644 (file)
@@ -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)
index b67ab68298bcc28e6526eead1489421d2b6c957b..68de95e49fd1ed93963d001fdb9052b05496fd1f 100644 (file)
@@ -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,
index 5e3ccd5ee2fb145bc1c63b7a6576c9c8ac92c0c9..01005d83c2c5dc1d288485a5d53720edf2e2e285 100644 (file)
@@ -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)
index 83e24fd8c96415d73f0af3053c9faa14e30ecf22..ab19a8e4fc8a9818b1f1386e513548f75452fbb1 100644 (file)
@@ -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;
index 760670cd0693b22ebd05ba8927595c0e126ef5ca..3e1646a96dde940ce5db3f4f221c6865ce72a699 100644 (file)
@@ -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;