]> granicus.if.org Git - postgresql/commitdiff
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 4b2d281f2c9cec4c487819f19e99744b78fcf73e..525273ef3ec785b17810dd81b5238d924e90ff62 100644 (file)
@@ -700,6 +700,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);
 
@@ -713,13 +714,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);
@@ -734,8 +750,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 3886d4e6bc430a5e2c1697f1942613164e954485..de9677c5b762d8bc766aa67fd2830858723e283d 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 d01b8b0be3f1819d0aad2b8481db73f800e7a128..fe78067ae75e904be0f763bd61c082265b9998a0 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)