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;