Fix ALTER INDEX RENAME so that if the index belongs to a unique or primary key
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 17 Jan 2008 18:56:54 +0000 (18:56 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 17 Jan 2008 18:56:54 +0000 (18:56 +0000)
constraint, the constraint is renamed as well.  This avoids inconsistent
situations that could confuse pg_dump (not to mention humans).  We might at
some point provide ALTER TABLE RENAME CONSTRAINT as a more general solution,
but there seems no reason not to allow doing it this way too.  Per bug #3854
and related discussions.

src/backend/catalog/pg_constraint.c
src/backend/commands/tablecmds.c
src/include/catalog/pg_constraint.h

index c55b392248df81c219630c42d517d249345f4643..073047b1568db6578f97320c83821f87eed9cea1 100644 (file)
@@ -25,6 +25,7 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -568,6 +569,67 @@ RemoveConstraintById(Oid conId)
        heap_close(conDesc, RowExclusiveLock);
 }
 
+/*
+ * RenameConstraintById
+ *             Rename a constraint.
+ *
+ * Note: this isn't intended to be a user-exposed function; it doesn't check
+ * permissions etc.  Currently this is only invoked when renaming an index
+ * that is associated with a constraint, but it's made a little more general
+ * than that with the expectation of someday having ALTER TABLE RENAME
+ * CONSTRAINT.
+ */
+void
+RenameConstraintById(Oid conId, const char *newname)
+{
+       Relation        conDesc;
+       HeapTuple       tuple;
+       Form_pg_constraint con;
+
+       conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
+
+       tuple = SearchSysCacheCopy(CONSTROID,
+                                                          ObjectIdGetDatum(conId),
+                                                          0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "cache lookup failed for constraint %u", conId);
+       con = (Form_pg_constraint) GETSTRUCT(tuple);
+
+       /*
+        * We need to check whether the name is already in use --- note that
+        * there currently is not a unique index that would catch this.
+        */
+       if (OidIsValid(con->conrelid) &&
+               ConstraintNameIsUsed(CONSTRAINT_RELATION,
+                                                        con->conrelid,
+                                                        con->connamespace,
+                                                        newname))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                errmsg("constraint \"%s\" for relation \"%s\" already exists",
+                                               newname, get_rel_name(con->conrelid))));
+       if (OidIsValid(con->contypid) &&
+               ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
+                                                        con->contypid,
+                                                        con->connamespace,
+                                                        newname))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                errmsg("constraint \"%s\" for domain \"%s\" already exists",
+                                               newname, format_type_be(con->contypid))));
+
+       /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
+       namestrcpy(&(con->conname), newname);
+
+       simple_heap_update(conDesc, &tuple->t_self, tuple);
+
+       /* update the system catalog indexes */
+       CatalogUpdateIndexes(conDesc, tuple);
+
+       heap_freetuple(tuple);
+       heap_close(conDesc, RowExclusiveLock);
+}
+
 /*
  * AlterConstraintNamespaces
  *             Find any constraints belonging to the specified object,
index cfb8cc77cb9277d31e22d33c4c5c0a57c491126e..722553622fef27df0421bc06543b1ddced914e55 100644 (file)
@@ -1659,13 +1659,13 @@ renamerel(Oid myrelid, const char *newrelname, ObjectType reltype)
         * or ALTER INDEX is used to rename a sequence or view.
         */
        relkind = targetrelation->rd_rel->relkind;
-       if (reltype == OBJECT_SEQUENCE && relkind != 'S')
+       if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
                ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 errmsg("\"%s\" is not a sequence",
                                                RelationGetRelationName(targetrelation))));
 
-       if (reltype == OBJECT_VIEW && relkind != 'v')
+       if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
                ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 errmsg("\"%s\" is not a view",
@@ -1711,6 +1711,17 @@ renamerel(Oid myrelid, const char *newrelname, ObjectType reltype)
        if (OidIsValid(targetrelation->rd_rel->reltype))
                TypeRename(targetrelation->rd_rel->reltype, newrelname, namespaceId);
 
+       /*
+        * Also rename the associated constraint, if any.
+        */
+       if (relkind == RELKIND_INDEX)
+       {
+               Oid                     constraintId = get_index_constraint(myrelid);
+
+               if (OidIsValid(constraintId))
+                       RenameConstraintById(constraintId, newrelname);
+       }
+
        /*
         * Close rel, but keep exclusive lock!
         */
index 290c86e438a9286022c5edf2ba27e65a9550424a..ed71aa2f3e6b793db38b30e9dff61bd1b088b942 100644 (file)
@@ -201,6 +201,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
                                          const char *conSrc);
 
 extern void RemoveConstraintById(Oid conId);
+extern void RenameConstraintById(Oid conId, const char *newname);
 
 extern bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
                                         Oid objNamespace, const char *conname);