Add support for renaming constraints
authorPeter Eisentraut <peter_e@gmx.net>
Sat, 10 Mar 2012 18:19:13 +0000 (20:19 +0200)
committerPeter Eisentraut <peter_e@gmx.net>
Sat, 10 Mar 2012 18:19:13 +0000 (20:19 +0200)
reviewed by Josh Berkus and Dimitri Fontaine

doc/src/sgml/ref/alter_table.sgml
src/backend/commands/alter.c
src/backend/commands/tablecmds.c
src/backend/parser/gram.y
src/include/commands/tablecmds.h
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index 951b63b5b4fa41efbd2594d510a27e907040a042..c3039c816727052167e7bd1291d058ebfcc373b0 100644 (file)
@@ -25,6 +25,8 @@ ALTER TABLE [ IF EXISTS ] [ ONLY ] <replaceable class="PARAMETER">name</replacea
     <replaceable class="PARAMETER">action</replaceable> [, ... ]
 ALTER TABLE [ IF EXISTS ] [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
     RENAME [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> TO <replaceable class="PARAMETER">new_column</replaceable>
+ALTER TABLE [ IF EXISTS ] [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
+    RENAME CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> TO <replaceable class="PARAMETER">new_constraint_name</replaceable>
 ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
     RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
 ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
@@ -569,8 +571,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
     <listitem>
      <para>
       The <literal>RENAME</literal> forms change the name of a table
-      (or an index, sequence, or view) or the name of an individual column in
-      a table. There is no effect on the stored data.
+      (or an index, sequence, or view), the name of an individual column in
+      a table, or the name of a constraint of the table. There is no effect on the stored data.
      </para>
     </listitem>
    </varlistentry>
@@ -883,7 +885,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
 
    <para>
     If a table has any descendant tables, it is not permitted to add,
-    rename, or change the type of a column in the parent table without doing
+    rename, or change the type of a column, or rename an inherited constraint
+    in the parent table without doing
     the same to the descendants.  That is, <command>ALTER TABLE ONLY</command>
     will be rejected.  This ensures that the descendants always have
     columns matching the parent.
@@ -982,6 +985,13 @@ ALTER TABLE distributors RENAME TO suppliers;
 </programlisting>
   </para>
 
+  <para>
+   To rename an existing constraint:
+<programlisting>
+ALTER TABLE distributors RENAME CONSTRAINT zipchk TO zip_check;
+</programlisting>
+  </para>
+
   <para>
    To add a not-null constraint to a column:
 <programlisting>
index 9175405af2af5f2a0dd4c7dcd0c95a1b416ed60f..4dd9927afbafaab2bec6c32597f134033fe1ee7c 100644 (file)
@@ -57,6 +57,10 @@ ExecRenameStmt(RenameStmt *stmt)
            RenameCollation(stmt->object, stmt->newname);
            break;
 
+       case OBJECT_CONSTRAINT:
+           RenameConstraint(stmt);
+           break;
+
        case OBJECT_CONVERSION:
            RenameConversion(stmt->object, stmt->newname);
            break;
index 25ca356b867ec799625cae5b215151a0284472f0..9615380f05b366ff67db234e68f45fd94aafdcea 100644 (file)
@@ -2327,6 +2327,108 @@ renameatt(RenameStmt *stmt)
                       stmt->behavior);
 }
 
+
+/*
+ * same logic as renameatt_internal
+ */
+static void
+rename_constraint_internal(Oid myrelid,
+                          const char *oldconname,
+                          const char *newconname,
+                          bool recurse,
+                          bool recursing,
+                          int expected_parents)
+{
+   Relation    targetrelation;
+   Oid         constraintOid;
+   HeapTuple   tuple;
+   Form_pg_constraint con;
+
+   targetrelation = relation_open(myrelid, AccessExclusiveLock);
+   /* don't tell it whether we're recursing; we allow changing typed tables here */
+   renameatt_check(myrelid, RelationGetForm(targetrelation), false);
+
+   constraintOid = get_constraint_oid(myrelid, oldconname, false);
+
+   tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
+   if (!HeapTupleIsValid(tuple))
+       elog(ERROR, "cache lookup failed for constraint %u",
+            constraintOid);
+   con = (Form_pg_constraint) GETSTRUCT(tuple);
+
+   if (con->contype == CONSTRAINT_CHECK && !con->conisonly)
+   {
+       if (recurse)
+       {
+           List       *child_oids,
+               *child_numparents;
+           ListCell   *lo,
+               *li;
+
+           child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
+                                            &child_numparents);
+
+           forboth(lo, child_oids, li, child_numparents)
+           {
+               Oid         childrelid = lfirst_oid(lo);
+               int         numparents = lfirst_int(li);
+
+               if (childrelid == myrelid)
+                   continue;
+
+               rename_constraint_internal(childrelid, oldconname, newconname, false, true, numparents);
+           }
+       }
+       else
+       {
+           if (expected_parents == 0 &&
+               find_inheritance_children(myrelid, NoLock) != NIL)
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                        errmsg("inherited constraint \"%s\" must be renamed in child tables too",
+                               oldconname)));
+       }
+
+       if (con->coninhcount > expected_parents)
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                    errmsg("cannot rename inherited constraint \"%s\"",
+                           oldconname)));
+   }
+
+   if (con->conindid
+       && (con->contype == CONSTRAINT_PRIMARY
+           || con->contype == CONSTRAINT_UNIQUE
+           || con->contype == CONSTRAINT_EXCLUSION))
+       /* rename the index; this renames the constraint as well */
+       RenameRelationInternal(con->conindid, newconname);
+   else
+       RenameConstraintById(constraintOid, newconname);
+
+   ReleaseSysCache(tuple);
+
+   relation_close(targetrelation, NoLock);     /* close rel but keep lock */
+}
+
+void
+RenameConstraint(RenameStmt *stmt)
+{
+   Oid         relid;
+
+   /* lock level taken here should match rename_constraint_internal */
+   relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
+                                    false, false,
+                                    RangeVarCallbackForRenameAttribute,
+                                    NULL);
+
+   rename_constraint_internal(relid,
+                              stmt->subname,
+                              stmt->newname,
+                              interpretInhOption(stmt->relation->inhOpt),      /* recursive? */
+                              false,   /* recursing? */
+                              0        /* expected inhcount */);
+}
+
 /*
  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
  */
index 9aea2cd80b5b05429cf72bb43e5e2613a9e1604b..feb28a41720f20d44ee3eb8fb666772203b1ed9d 100644 (file)
@@ -6731,6 +6731,16 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
                    n->missing_ok = true;
                    $$ = (Node *)n;
                }
+           | ALTER TABLE relation_expr RENAME CONSTRAINT name TO name
+               {
+                   RenameStmt *n = makeNode(RenameStmt);
+                   n->renameType = OBJECT_CONSTRAINT;
+                   n->relationType = OBJECT_TABLE;
+                   n->relation = $3;
+                   n->subname = $6;
+                   n->newname = $8;
+                   $$ = (Node *)n;
+               }
            | ALTER FOREIGN TABLE relation_expr RENAME opt_column name TO name
                {
                    RenameStmt *n = makeNode(RenameStmt);
index 03f397de6390dd28911ec98ecc645dc00ac40230..47b0cddc9bb8634c1a0f655b4db695676dd7f984 100644 (file)
@@ -48,6 +48,8 @@ extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass);
 
 extern void renameatt(RenameStmt *stmt);
 
+extern void RenameConstraint(RenameStmt *stmt);
+
 extern void RenameRelation(RenameStmt *stmt);
 
 extern void RenameRelationInternal(Oid myrelid,
index 4aba58c450361519a547d17d1b1d34c0b53b2be7..eba0493089f938a087ee9176e1f2d21b733dde3d 100644 (file)
@@ -160,6 +160,141 @@ DROP VIEW tmp_view_new;
 -- toast-like relation name
 alter table stud_emp rename to pg_toast_stud_emp;
 alter table pg_toast_stud_emp rename to stud_emp;
+-- renaming index should rename constraint as well
+ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1);
+NOTICE:  ALTER TABLE / ADD UNIQUE will create implicit index "onek_unique1_constraint" for table "onek"
+ALTER INDEX onek_unique1_constraint RENAME TO onek_unique1_constraint_foo;
+ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo;
+-- renaming constraint
+ALTER TABLE onek ADD CONSTRAINT onek_check_constraint CHECK (unique1 >= 0);
+ALTER TABLE onek RENAME CONSTRAINT onek_check_constraint TO onek_check_constraint_foo;
+ALTER TABLE onek DROP CONSTRAINT onek_check_constraint_foo;
+-- renaming constraint should rename index as well
+ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1);
+NOTICE:  ALTER TABLE / ADD UNIQUE will create implicit index "onek_unique1_constraint" for table "onek"
+DROP INDEX onek_unique1_constraint;  -- to see whether it's there
+ERROR:  cannot drop index onek_unique1_constraint because constraint onek_unique1_constraint on table onek requires it
+HINT:  You can drop constraint onek_unique1_constraint on table onek instead.
+ALTER TABLE onek RENAME CONSTRAINT onek_unique1_constraint TO onek_unique1_constraint_foo;
+DROP INDEX onek_unique1_constraint_foo;  -- to see whether it's there
+ERROR:  cannot drop index onek_unique1_constraint_foo because constraint onek_unique1_constraint_foo on table onek requires it
+HINT:  You can drop constraint onek_unique1_constraint_foo on table onek instead.
+ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo;
+-- renaming constraints vs. inheritance
+CREATE TABLE constraint_rename_test (a int CONSTRAINT con1 CHECK (a > 0), b int, c int);
+\d constraint_rename_test
+Table "public.constraint_rename_test"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ b      | integer | 
+ c      | integer | 
+Check constraints:
+    "con1" CHECK (a > 0)
+
+CREATE TABLE constraint_rename_test2 (a int CONSTRAINT con1 CHECK (a > 0), d int) INHERITS (constraint_rename_test);
+NOTICE:  merging column "a" with inherited definition
+NOTICE:  merging constraint "con1" with inherited definition
+\d constraint_rename_test2
+Table "public.constraint_rename_test2"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ b      | integer | 
+ c      | integer | 
+ d      | integer | 
+Check constraints:
+    "con1" CHECK (a > 0)
+Inherits: constraint_rename_test
+
+ALTER TABLE constraint_rename_test2 RENAME CONSTRAINT con1 TO con1foo; -- fail
+ERROR:  cannot rename inherited constraint "con1"
+ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- fail
+ERROR:  inherited constraint "con1" must be renamed in child tables too
+ALTER TABLE constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- ok
+\d constraint_rename_test
+Table "public.constraint_rename_test"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ b      | integer | 
+ c      | integer | 
+Check constraints:
+    "con1foo" CHECK (a > 0)
+Number of child tables: 1 (Use \d+ to list them.)
+
+\d constraint_rename_test2
+Table "public.constraint_rename_test2"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ b      | integer | 
+ c      | integer | 
+ d      | integer | 
+Check constraints:
+    "con1foo" CHECK (a > 0)
+Inherits: constraint_rename_test
+
+ALTER TABLE ONLY constraint_rename_test ADD CONSTRAINT con2 CHECK (b > 0);
+ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok
+ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok
+\d constraint_rename_test
+Table "public.constraint_rename_test"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ b      | integer | 
+ c      | integer | 
+Check constraints:
+    "con2bar" (ONLY) CHECK (b > 0)
+    "con1foo" CHECK (a > 0)
+Number of child tables: 1 (Use \d+ to list them.)
+
+\d constraint_rename_test2
+Table "public.constraint_rename_test2"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ b      | integer | 
+ c      | integer | 
+ d      | integer | 
+Check constraints:
+    "con1foo" CHECK (a > 0)
+Inherits: constraint_rename_test
+
+ALTER TABLE constraint_rename_test ADD CONSTRAINT con3 PRIMARY KEY (a);
+NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "con3" for table "constraint_rename_test"
+ALTER TABLE constraint_rename_test RENAME CONSTRAINT con3 TO con3foo; -- ok
+\d constraint_rename_test
+Table "public.constraint_rename_test"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | not null
+ b      | integer | 
+ c      | integer | 
+Indexes:
+    "con3foo" PRIMARY KEY, btree (a)
+Check constraints:
+    "con2bar" (ONLY) CHECK (b > 0)
+    "con1foo" CHECK (a > 0)
+Number of child tables: 1 (Use \d+ to list them.)
+
+\d constraint_rename_test2
+Table "public.constraint_rename_test2"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ a      | integer | 
+ b      | integer | 
+ c      | integer | 
+ d      | integer | 
+Check constraints:
+    "con1foo" CHECK (a > 0)
+Inherits: constraint_rename_test
+
+DROP TABLE constraint_rename_test2;
+DROP TABLE constraint_rename_test;
+ALTER TABLE IF EXISTS constraint_rename_test ADD CONSTRAINT con4 UNIQUE (a);
+NOTICE:  relation "constraint_rename_test" does not exist, skipping
 -- FOREIGN KEY CONSTRAINT adding TEST
 CREATE TABLE tmp2 (a int primary key);
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "tmp2_pkey" for table "tmp2"
index d4e4c4958d186eb2a96699fac519db8ef8445554..50c58d23e18799c3959add0f51c4348b1a03cc8e 100644 (file)
@@ -191,6 +191,46 @@ DROP VIEW tmp_view_new;
 alter table stud_emp rename to pg_toast_stud_emp;
 alter table pg_toast_stud_emp rename to stud_emp;
 
+-- renaming index should rename constraint as well
+ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1);
+ALTER INDEX onek_unique1_constraint RENAME TO onek_unique1_constraint_foo;
+ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo;
+
+-- renaming constraint
+ALTER TABLE onek ADD CONSTRAINT onek_check_constraint CHECK (unique1 >= 0);
+ALTER TABLE onek RENAME CONSTRAINT onek_check_constraint TO onek_check_constraint_foo;
+ALTER TABLE onek DROP CONSTRAINT onek_check_constraint_foo;
+
+-- renaming constraint should rename index as well
+ALTER TABLE onek ADD CONSTRAINT onek_unique1_constraint UNIQUE (unique1);
+DROP INDEX onek_unique1_constraint;  -- to see whether it's there
+ALTER TABLE onek RENAME CONSTRAINT onek_unique1_constraint TO onek_unique1_constraint_foo;
+DROP INDEX onek_unique1_constraint_foo;  -- to see whether it's there
+ALTER TABLE onek DROP CONSTRAINT onek_unique1_constraint_foo;
+
+-- renaming constraints vs. inheritance
+CREATE TABLE constraint_rename_test (a int CONSTRAINT con1 CHECK (a > 0), b int, c int);
+\d constraint_rename_test
+CREATE TABLE constraint_rename_test2 (a int CONSTRAINT con1 CHECK (a > 0), d int) INHERITS (constraint_rename_test);
+\d constraint_rename_test2
+ALTER TABLE constraint_rename_test2 RENAME CONSTRAINT con1 TO con1foo; -- fail
+ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- fail
+ALTER TABLE constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- ok
+\d constraint_rename_test
+\d constraint_rename_test2
+ALTER TABLE ONLY constraint_rename_test ADD CONSTRAINT con2 CHECK (b > 0);
+ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok
+ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok
+\d constraint_rename_test
+\d constraint_rename_test2
+ALTER TABLE constraint_rename_test ADD CONSTRAINT con3 PRIMARY KEY (a);
+ALTER TABLE constraint_rename_test RENAME CONSTRAINT con3 TO con3foo; -- ok
+\d constraint_rename_test
+\d constraint_rename_test2
+DROP TABLE constraint_rename_test2;
+DROP TABLE constraint_rename_test;
+ALTER TABLE IF EXISTS constraint_rename_test ADD CONSTRAINT con4 UNIQUE (a);
+
 -- FOREIGN KEY CONSTRAINT adding TEST
 
 CREATE TABLE tmp2 (a int primary key);