Move autogenerated array types out of the way during ALTER ... RENAME.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 26 May 2017 19:16:59 +0000 (15:16 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 26 May 2017 19:16:59 +0000 (15:16 -0400)
Commit 9aa3c782c added code to allow CREATE TABLE/CREATE TYPE to not fail
when the desired type name conflicts with an autogenerated array type, by
dint of renaming the array type out of the way.  But I (tgl) overlooked
that the same case arises in ALTER TABLE/TYPE RENAME.  Fix that too.
Back-patch to all supported branches.

Report and patch by Vik Fearing, modified a bit by me

Discussion: https://postgr.es/m/0f4ade49-4f0b-a9a3-c120-7589f01d1eb8@2ndquadrant.com

src/backend/catalog/pg_type.c
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index 04c10c6347b81183aa5c5ec3906bc0367672da29..6b0e4f4729f33596a02cb7f7af871ca31ae94f12 100644 (file)
@@ -695,6 +695,7 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
    HeapTuple   tuple;
    Form_pg_type typ;
    Oid         arrayOid;
+   Oid         oldTypeOid;
 
    pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
 
@@ -708,13 +709,28 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
 
    arrayOid = typ->typarray;
 
-   /* Just to give a more friendly error than unique-index violation */
-   if (SearchSysCacheExists2(TYPENAMENSP,
-                             CStringGetDatum(newTypeName),
-                             ObjectIdGetDatum(typeNamespace)))
-       ereport(ERROR,
-               (errcode(ERRCODE_DUPLICATE_OBJECT),
-                errmsg("type \"%s\" already exists", newTypeName)));
+   /* Check for a conflicting type name. */
+   oldTypeOid = GetSysCacheOid2(TYPENAMENSP,
+                                CStringGetDatum(newTypeName),
+                                ObjectIdGetDatum(typeNamespace));
+
+   /*
+    * If there is one, see if it's an autogenerated array type, and if so
+    * rename it out of the way.  (But we must skip that for a shell type
+    * because moveArrayTypeName will do the wrong thing in that case.)
+    * Otherwise, we can at least give a more friendly error than unique-index
+    * violation.
+    */
+   if (OidIsValid(oldTypeOid))
+   {
+       if (get_typisdefined(oldTypeOid) &&
+           moveArrayTypeName(oldTypeOid, newTypeName, typeNamespace))
+            /* successfully dodged the problem */ ;
+       else
+           ereport(ERROR,
+                   (errcode(ERRCODE_DUPLICATE_OBJECT),
+                    errmsg("type \"%s\" already exists", newTypeName)));
+   }
 
    /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
    namestrcpy(&(typ->typname), newTypeName);
@@ -726,8 +742,12 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
    heap_freetuple(tuple);
    heap_close(pg_type_desc, RowExclusiveLock);
 
-   /* If the type has an array type, recurse to handle that */
-   if (OidIsValid(arrayOid))
+   /*
+    * If the type has an array type, recurse to handle that.  But we don't
+    * need to do anything more if we already renamed that array type above
+    * (which would happen when, eg, renaming "foo" to "_foo").
+    */
+   if (OidIsValid(arrayOid) && arrayOid != oldTypeOid)
    {
        char       *arrname = makeArrayTypeName(newTypeName, typeNamespace);
 
index c88fd768482792bd8376dd21cc5b63be927f7727..8aadbb88a348571dd2fe6e22d1e2a2f72380d35b 100644 (file)
@@ -128,6 +128,55 @@ SELECT * FROM tmp_new2;
 
 DROP TABLE tmp_new;
 DROP TABLE tmp_new2;
+--
+-- check renaming to a table's array type's autogenerated name
+-- (the array type's name should get out of the way)
+--
+CREATE TABLE tmp_array (id int);
+CREATE TABLE tmp_array2 (id int);
+SELECT typname FROM pg_type WHERE oid = 'tmp_array[]'::regtype;
+  typname   
+------------
+ _tmp_array
+(1 row)
+
+SELECT typname FROM pg_type WHERE oid = 'tmp_array2[]'::regtype;
+   typname   
+-------------
+ _tmp_array2
+(1 row)
+
+ALTER TABLE tmp_array2 RENAME TO _tmp_array;
+SELECT typname FROM pg_type WHERE oid = 'tmp_array[]'::regtype;
+   typname   
+-------------
+ __tmp_array
+(1 row)
+
+SELECT typname FROM pg_type WHERE oid = '_tmp_array[]'::regtype;
+   typname    
+--------------
+ ___tmp_array
+(1 row)
+
+DROP TABLE _tmp_array;
+DROP TABLE tmp_array;
+-- renaming to table's own array type's name is an interesting corner case
+CREATE TABLE tmp_array (id int);
+SELECT typname FROM pg_type WHERE oid = 'tmp_array[]'::regtype;
+  typname   
+------------
+ _tmp_array
+(1 row)
+
+ALTER TABLE tmp_array RENAME TO _tmp_array;
+SELECT typname FROM pg_type WHERE oid = '_tmp_array[]'::regtype;
+   typname   
+-------------
+ __tmp_array
+(1 row)
+
+DROP TABLE _tmp_array;
 -- ALTER TABLE ... RENAME on non-table relations
 -- renaming indexes (FIXME: this should probably test the index's functionality)
 ALTER INDEX IF EXISTS __onek_unique1 RENAME TO tmp_onek_unique1;
index c0e29720dc5dbd6ef0b230ae129a32f5c1f456f0..c41b48785b5cdf2f4ad76ea8ec414059b6de1824 100644 (file)
@@ -165,6 +165,26 @@ SELECT * FROM tmp_new2;
 DROP TABLE tmp_new;
 DROP TABLE tmp_new2;
 
+--
+-- check renaming to a table's array type's autogenerated name
+-- (the array type's name should get out of the way)
+--
+CREATE TABLE tmp_array (id int);
+CREATE TABLE tmp_array2 (id int);
+SELECT typname FROM pg_type WHERE oid = 'tmp_array[]'::regtype;
+SELECT typname FROM pg_type WHERE oid = 'tmp_array2[]'::regtype;
+ALTER TABLE tmp_array2 RENAME TO _tmp_array;
+SELECT typname FROM pg_type WHERE oid = 'tmp_array[]'::regtype;
+SELECT typname FROM pg_type WHERE oid = '_tmp_array[]'::regtype;
+DROP TABLE _tmp_array;
+DROP TABLE tmp_array;
+
+-- renaming to table's own array type's name is an interesting corner case
+CREATE TABLE tmp_array (id int);
+SELECT typname FROM pg_type WHERE oid = 'tmp_array[]'::regtype;
+ALTER TABLE tmp_array RENAME TO _tmp_array;
+SELECT typname FROM pg_type WHERE oid = '_tmp_array[]'::regtype;
+DROP TABLE _tmp_array;
 
 -- ALTER TABLE ... RENAME on non-table relations
 -- renaming indexes (FIXME: this should probably test the index's functionality)