diff --git a/pkg/sql/alter_type.go b/pkg/sql/alter_type.go index 777b9bfcb745..c861e722909e 100644 --- a/pkg/sql/alter_type.go +++ b/pkg/sql/alter_type.go @@ -356,6 +356,8 @@ func (p *planner) setTypeSchema(ctx context.Context, n *alterTypeNode, schema st return err } + // The prepareSetSchema checks the primary type's name. The name of the + // companion array is collision checked below. desiredSchemaID, err := p.prepareSetSchema(ctx, n.prefix.Database, typeDesc, schema) if err != nil { return err @@ -367,16 +369,31 @@ func (p *planner) setTypeSchema(ctx context.Context, n *alterTypeNode, schema st return nil } - err = p.performRenameTypeDesc( - ctx, typeDesc, typeDesc.Name, desiredSchemaID, tree.AsStringWithFQNames(n.n, p.Ann()), - ) - + arrayDesc, err := p.Descriptors().MutableByID(p.txn).Type(ctx, n.desc.ArrayTypeID) if err != nil { return err } - arrayDesc, err := p.Descriptors().MutableByID(p.txn).Type(ctx, n.desc.ArrayTypeID) - if err != nil { + // The CheckObjectNameCollision checks that the companion array can be moved + // without colliding with + // + // This is consistent with the PG behavior which itself is inconsistent: + // `SET SCHEMA` errors on collision while `ALTER TYPE ... RENAME` auto-resolves + // conflicts on companion array names. + if err := descs.CheckObjectNameCollision( + ctx, + p.Descriptors(), + p.txn, + typeDesc.GetParentID(), + desiredSchemaID, + tree.NewUnqualifiedTypeName(arrayDesc.GetName()), + ); err != nil { + return err + } + + if err := p.performRenameTypeDesc( + ctx, typeDesc, typeDesc.Name, desiredSchemaID, tree.AsStringWithFQNames(n.n, p.Ann()), + ); err != nil { return err } diff --git a/pkg/sql/logictest/testdata/logic_test/set_schema b/pkg/sql/logictest/testdata/logic_test/set_schema index f2066be6ca6c..e1678895011a 100644 --- a/pkg/sql/logictest/testdata/logic_test/set_schema +++ b/pkg/sql/logictest/testdata/logic_test/set_schema @@ -590,3 +590,67 @@ true user testuser subtest end + +subtest array_type_name_conflict + +statement ok +CREATE SCHEMA IF NOT EXISTS ocean + +statement ok +CREATE SCHEMA IF NOT EXISTS stream + +statement ok +CREATE TYPE ocean.salmon AS ENUM ('sockeye', 'king', 'atlantic'); + +statement ok +CREATE TYPE stream."_salmon" AS ENUM ('steelhead', 'cherry'); + +# The companion array type "_salmon" collides. +statement error pq: type "test.stream._salmon" already exists +ALTER TYPE ocean.salmon SET SCHEMA stream + +# Verify the source type and array are still usable in the original schema. +statement ok +SELECT 'sockeye'::ocean.salmon + +statement ok +SELECT ARRAY['sockeye']::ocean._salmon + +# The target schema should not contain the primary type. +query error pq: type "stream.salmon" does not exist +SELECT NULL::stream.salmon + +user testuser + +subtest end + +# Regression test: The companion array type collides with a table in the destination schema. +subtest array_type_table_collision + +user root + +statement ok +CREATE SCHEMA IF NOT EXISTS lake + +statement ok +CREATE SCHEMA IF NOT EXISTS pond + +statement ok +CREATE TYPE lake.trout AS ENUM ('brown', 'rainbow'); + +statement ok +CREATE TABLE pond."_trout" (id INT PRIMARY KEY) + +statement error pq: relation "test.pond._trout" already exists +ALTER TYPE lake.trout SET SCHEMA pond + +statement ok +SELECT 'brown'::lake.trout + +# The target schema should not contain the primary type. +query error pq: type "pond.trout" does not exist +SELECT NULL::pond.trout + +user testuser + +subtest end