Thread: [HACKERS] Renaming a table to an array's autogenerated name

[HACKERS] Renaming a table to an array's autogenerated name

From
Vik Fearing
Date:
In commit 9aa3c782c93, Tom fixed a bug in which creating a table _foo
when an array type of that name already existed would make the array
type change its name to get out of the way. But it missed a trick in
that renaming a table would still cause a conflict.

Steps to reproduce:

postgres=# create table foo (id int);
CREATE TABLE
postgres=# create table bar (id int);
CREATE TABLE
postgres=# alter table bar rename to _foo;
ERROR:  type "_foo" already exists

Attached is a patch that fixes this bug.

One interesting thing to note however, is that if you rename a table to
its own array's name (which was my case when I found this bug), the new
array name will be ___foo instead of just __foo.  I don't know if it's
worth worrying about that.
-- 
Vik Fearing                                          +33 6 46 75 15 36
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Attachment

Re: [HACKERS] Renaming a table to an array's autogenerated name

From
Tom Lane
Date:
Vik Fearing <vik.fearing@2ndquadrant.com> writes:
> In commit 9aa3c782c93, Tom fixed a bug in which creating a table _foo
> when an array type of that name already existed would make the array
> type change its name to get out of the way. But it missed a trick in
> that renaming a table would still cause a conflict.

Good catch.

> One interesting thing to note however, is that if you rename a table to
> its own array's name (which was my case when I found this bug), the new
> array name will be ___foo instead of just __foo.  I don't know if it's
> worth worrying about that.

This seemed weird to me.  Stepping through the logic, I see that what's
happening is that the moveArrayTypeName call you added renames the
array type from "_foo" to "__foo", and then at the bottom of
RenameTypeInternal we call makeArrayTypeName/RenameTypeInternal again
on that array type.  makeArrayTypeName sees that "__foo" exists and
assumes the name is in use, even though it's really this same type and
no more renaming is needed.  So it ends up picking the next choice
"___foo".

After some experimentation, I came up with the attached, which simply
skips the "recursive" step if it would apply to the same array type we
already moved.

I added some queries to the regression test case to show exactly what
happens to the array type names, and in that, you can see that the
behavior for the "normal" case with distinct array types is that neither
array type ends up with a name that is just the unsurprising case of
an underscore followed by its element type's name; they *both* have an
extra underscore compared to that.  Maybe that's okay.  We could possibly
rejigger the order of renaming so that one of them ends with an
unsurprising name, but I failed to make that happen without considerably
more surgery.

            regards, tom lane

diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 04c10c6347b81183aa5c5ec3906bc0367672da29..74eeba6e0ab3aa127c1ba01e2c37b770f92629cd 100644
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
*************** RenameTypeInternal(Oid typeOid, const ch
*** 695,700 ****
--- 695,701 ----
      HeapTuple    tuple;
      Form_pg_type typ;
      Oid            arrayOid;
+     Oid            oldTypeOid;

      pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);

*************** RenameTypeInternal(Oid typeOid, const ch
*** 708,720 ****

      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)));

      /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
      namestrcpy(&(typ->typname), newTypeName);
--- 709,736 ----

      arrayOid = typ->typarray;

!     /* 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);
*************** RenameTypeInternal(Oid typeOid, const ch
*** 726,733 ****
      heap_freetuple(tuple);
      heap_close(pg_type_desc, RowExclusiveLock);

!     /* If the type has an array type, recurse to handle that */
!     if (OidIsValid(arrayOid))
      {
          char       *arrname = makeArrayTypeName(newTypeName, typeNamespace);

--- 742,753 ----
      heap_freetuple(tuple);
      heap_close(pg_type_desc, RowExclusiveLock);

!     /*
!      * 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);

diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index c88fd768482792bd8376dd21cc5b63be927f7727..8aadbb88a348571dd2fe6e22d1e2a2f72380d35b 100644
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
*************** SELECT * FROM tmp_new2;
*** 128,133 ****
--- 128,182 ----

  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;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index c0e29720dc5dbd6ef0b230ae129a32f5c1f456f0..c41b48785b5cdf2f4ad76ea8ec414059b6de1824 100644
*** a/src/test/regress/sql/alter_table.sql
--- b/src/test/regress/sql/alter_table.sql
*************** SELECT * FROM tmp_new2;
*** 165,170 ****
--- 165,190 ----
  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)

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Re: [HACKERS] Renaming a table to an array's autogenerated name

From
Vik Fearing
Date:
On 05/25/2017 05:24 PM, Tom Lane wrote:
> Vik Fearing <vik.fearing@2ndquadrant.com> writes:
>> In commit 9aa3c782c93, Tom fixed a bug in which creating a table _foo
>> when an array type of that name already existed would make the array
>> type change its name to get out of the way. But it missed a trick in
>> that renaming a table would still cause a conflict.
> 
> Good catch.
> 
>> One interesting thing to note however, is that if you rename a table to
>> its own array's name (which was my case when I found this bug), the new
>> array name will be ___foo instead of just __foo.  I don't know if it's
>> worth worrying about that.
> 
> After some experimentation, I came up with the attached, which simply
> skips the "recursive" step if it would apply to the same array type we
> already moved.

This looks good to me.

> I added some queries to the regression test case to show exactly what
> happens to the array type names, and in that, you can see that the
> behavior for the "normal" case with distinct array types is that neither
> array type ends up with a name that is just the unsurprising case of
> an underscore followed by its element type's name; they *both* have an
> extra underscore compared to that.  Maybe that's okay.  We could possibly
> rejigger the order of renaming so that one of them ends with an
> unsurprising name, but I failed to make that happen without considerably
> more surgery.

I don't think this really matters to anyone in practice, so I'm fine
with it.
-- 
Vik Fearing                                          +33 6 46 75 15 36
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support



Re: [HACKERS] Renaming a table to an array's autogenerated name

From
Tom Lane
Date:
Vik Fearing <vik.fearing@2ndquadrant.com> writes:
> On 05/25/2017 05:24 PM, Tom Lane wrote:
>> After some experimentation, I came up with the attached, which simply
>> skips the "recursive" step if it would apply to the same array type we
>> already moved.

> This looks good to me.

Pushed, thanks for reviewing.
        regards, tom lane



Re: [HACKERS] Renaming a table to an array's autogenerated name

From
Vik Fearing
Date:
On 05/26/2017 03:20 PM, Tom Lane wrote:
> Vik Fearing <vik.fearing@2ndquadrant.com> writes:
>> On 05/25/2017 05:24 PM, Tom Lane wrote:
>>> After some experimentation, I came up with the attached, which simply
>>> skips the "recursive" step if it would apply to the same array type we
>>> already moved.
> 
>> This looks good to me.
> 
> Pushed, thanks for reviewing.

Thanks!
-- 
Vik Fearing                                          +33 6 46 75 15 36
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support