From f1a7a14f64414876befb91be34f1dd264a34884e Mon Sep 17 00:00:00 2001 From: Brendan Gerrity Date: Tue, 24 Mar 2026 17:03:48 -0400 Subject: [PATCH] sql/schemachanger: add support for setting schema of enum types This change implements support for changing the schema of enum UDTs and rewires the command to it. Closes: #164216 Epic: CRDB-31325 Release note: None --- .../backup_base_generated_test.go | 28 +++++ pkg/sql/alter_type.go | 12 +- .../logictest/testdata/logic_test/alter_type | 101 ++++++++++++++++ .../testdata/logic_test/event_log_legacy | 3 + .../logictest/testdata/logic_test/set_schema | 3 +- .../scbuildstmt/alter_table_set_schema.go | 59 ++++++---- .../internal/scbuildstmt/alter_type.go | 37 +++++- .../schemachanger/scbuild/testdata/alter_type | 26 +++++ .../scbuild/testdata/unimplemented_alter_type | 7 -- .../scexec/scmutationexec/references.go | 8 ++ .../schemachanger/sctest_generated_test.go | 42 +++++++ .../alter_type_set_schema.definition | 8 ++ .../alter_type_set_schema.explain | 69 +++++++++++ .../alter_type_set_schema.explain_shape | 9 ++ .../alter_type_set_schema.side_effects | 108 ++++++++++++++++++ 15 files changed, 481 insertions(+), 39 deletions(-) delete mode 100644 pkg/sql/schemachanger/scbuild/testdata/unimplemented_alter_type create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.definition create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.explain create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.explain_shape create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.side_effects diff --git a/pkg/ccl/schemachangerccl/sctestbackupccl/backup_base_generated_test.go b/pkg/ccl/schemachangerccl/sctestbackupccl/backup_base_generated_test.go index 4b62728410f2..acb0b2b10153 100644 --- a/pkg/ccl/schemachangerccl/sctestbackupccl/backup_base_generated_test.go +++ b/pkg/ccl/schemachangerccl/sctestbackupccl/backup_base_generated_test.go @@ -589,6 +589,13 @@ func TestBackupRollbacks_base_alter_type_rename_value(t *testing.T) { sctest.BackupRollbacks(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestBackupRollbacks_base_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.BackupRollbacks(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestBackupRollbacks_base_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1492,6 +1499,13 @@ func TestBackupRollbacksMixedVersion_base_alter_type_rename_value(t *testing.T) sctest.BackupRollbacksMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestBackupRollbacksMixedVersion_base_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.BackupRollbacksMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestBackupRollbacksMixedVersion_base_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -2395,6 +2409,13 @@ func TestBackupSuccess_base_alter_type_rename_value(t *testing.T) { sctest.BackupSuccess(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestBackupSuccess_base_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.BackupSuccess(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestBackupSuccess_base_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -3298,6 +3319,13 @@ func TestBackupSuccessMixedVersion_base_alter_type_rename_value(t *testing.T) { sctest.BackupSuccessMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestBackupSuccessMixedVersion_base_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.BackupSuccessMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestBackupSuccessMixedVersion_base_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) diff --git a/pkg/sql/alter_type.go b/pkg/sql/alter_type.go index 2b7a59b4b61f..25c839db759d 100644 --- a/pkg/sql/alter_type.go +++ b/pkg/sql/alter_type.go @@ -355,8 +355,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. + // The prepareSetSchema checks the primary type's name while 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 @@ -373,12 +373,12 @@ func (p *planner) setTypeSchema(ctx context.Context, n *alterTypeNode, schema st return err } - // The CheckObjectNameCollision checks that the companion array can be moved - // without colliding with + // CheckObjectNameCollision checks that the companion array can be moved + // without a name collision. // // 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. + // `SET SCHEMA` errors on collision while `ALTER TYPE ... RENAME` + // auto-resolves conflicts on companion array names. if err := descs.CheckObjectNameCollision( ctx, p.Descriptors(), diff --git a/pkg/sql/logictest/testdata/logic_test/alter_type b/pkg/sql/logictest/testdata/logic_test/alter_type index 3fc907da7a53..6814737236b3 100644 --- a/pkg/sql/logictest/testdata/logic_test/alter_type +++ b/pkg/sql/logictest/testdata/logic_test/alter_type @@ -928,3 +928,104 @@ t_rename_computed CREATE TABLE public.t_rename_computed ( ); subtest end + +subtest alter_type_set_schema + +statement ok +CREATE SCHEMA s1; +CREATE SCHEMA s2; +CREATE TYPE sc_type AS ENUM ('a', 'b', 'c'); + +# Move type from public to s1. +statement ok +ALTER TYPE sc_type SET SCHEMA s1 + +# The type should be resolvable in the new schema. +query T +SELECT 'a'::s1.sc_type +---- +a + +# The array type should have moved too. +query T +SELECT ARRAY['a']::s1._sc_type +---- +{a} + +# Setting the same schema should be a no-op. +statement ok +ALTER TYPE s1.sc_type SET SCHEMA s1 + +# Move from s1 to s2. +statement ok +ALTER TYPE s1.sc_type SET SCHEMA s2 + +query T +SELECT 'b'::s2.sc_type +---- +b + +query T +SELECT ARRAY['b']::s2._sc_type +---- +{b} + +# The type should no longer exist in s1. +query error pq: type "s1.sc_type" does not exist +SELECT 'a'::s1.sc_type + +# Error when target schema doesn't exist. +statement error pq: unknown schema "does_not_exist" +ALTER TYPE s2.sc_type SET SCHEMA does_not_exist + +# Error when there's a name conflict in the target schema. +statement ok +CREATE TYPE s1.sc_type AS ENUM ('x', 'y') + +statement error pq: type "test.s1.sc_type" already exists +ALTER TYPE s2.sc_type SET SCHEMA s1 + +# Clean up. +statement ok +DROP TYPE s1.sc_type; +DROP TYPE s2.sc_type; +DROP SCHEMA s1; +DROP SCHEMA s2 + +subtest end + +subtest alter_type_set_schema_with_table_ref + +# Moving a type that is referenced by a table column should succeed +# because types are referenced by ID, not name. +statement ok +CREATE SCHEMA s_ref; +CREATE TYPE ref_type AS ENUM ('x', 'y', 'z'); +CREATE TABLE t_ref (c ref_type); +INSERT INTO t_ref VALUES ('x') + +statement ok +ALTER TYPE ref_type SET SCHEMA s_ref + +# The table should still work with the moved type. +query T +SELECT c FROM t_ref +---- +x + +statement ok +INSERT INTO t_ref VALUES ('y') + +query T rowsort +SELECT c FROM t_ref +---- +x +y + +# Clean up. +statement ok +DROP TABLE t_ref; +DROP TYPE s_ref.ref_type; +DROP SCHEMA s_ref + +subtest end diff --git a/pkg/sql/logictest/testdata/logic_test/event_log_legacy b/pkg/sql/logictest/testdata/logic_test/event_log_legacy index 1664d82f3d93..79d52f415592 100644 --- a/pkg/sql/logictest/testdata/logic_test/event_log_legacy +++ b/pkg/sql/logictest/testdata/logic_test/event_log_legacy @@ -937,6 +937,9 @@ DROP USER u # event log. subtest regression_57734 +statement ok +USE defaultdb + statement ok CREATE TYPE eventlog AS ENUM ('event', 'log') diff --git a/pkg/sql/logictest/testdata/logic_test/set_schema b/pkg/sql/logictest/testdata/logic_test/set_schema index e1678895011a..e71861929c4d 100644 --- a/pkg/sql/logictest/testdata/logic_test/set_schema +++ b/pkg/sql/logictest/testdata/logic_test/set_schema @@ -28,8 +28,7 @@ statement error pq: relation "test.s2.t" already exists ALTER TABLE t SET SCHEMA s2 # Ensure we cannot set schema to a virtual schema. -# use regex to accept both the legacy and the declarative schema changer format -statement error pq: (cannot move objects into or out of virtual schemas)?(user root does not have CREATE privilege on schema information_schema)? +statement error pq: cannot move objects into or out of virtual schemas ALTER TABLE t SET SCHEMA information_schema # Ensure we cannot set schema for a table in a virtual schema. diff --git a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_set_schema.go b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_set_schema.go index 18e947fadb72..ef879ed7db9b 100644 --- a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_set_schema.go +++ b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_set_schema.go @@ -6,10 +6,13 @@ package scbuildstmt import ( + "strings" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/privilege" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" + "github.com/cockroachdb/cockroach/pkg/sql/sem/catconstants" "github.com/cockroachdb/cockroach/pkg/sql/sem/catid" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/util/log/eventpb" @@ -49,10 +52,10 @@ func AlterTableSetSchema(b BuildCtx, n *tree.AlterTableSetSchema) { // Get the schema ID directly from the namespace element currNamespace := mustRetrieveNamespaceElem(b, descID) currSchemaID := currNamespace.SchemaID + // Ensure that new schema is neither temporary nor virtual. + panicIfSchemaIsTemporaryOrVirtual(n.Schema) newSchema := resolveSchemaByName(b, n.Schema, currNamespace.DatabaseID) newSchemaID := newSchema.SchemaID - // Ensure that new schema is not temporary or virtual - panicIfSchemaIsTemporaryOrVirtual(newSchema) // If new schema is the same as the curr schema, do a no-op if currSchemaID == newSchemaID { return @@ -63,20 +66,7 @@ func AlterTableSetSchema(b BuildCtx, n *tree.AlterTableSetSchema) { // Check for name conflicts checkTableNameConflicts(b, currName, newName, currNamespace) - // drop the old namespace and add a new one - newNamespace := *currNamespace - newNamespace.SchemaID = newSchemaID - b.Drop(currNamespace) - b.Add(&newNamespace) - - // drop old schema child and add new one - currSchemaChild := b.QueryByID(descID).FilterSchemaChild().MustGetOneElement() - newSchemaChild := scpb.SchemaChild{ - ChildObjectID: descID, - SchemaID: newSchemaID, - } - b.Drop(currSchemaChild) - b.Add(&newSchemaChild) + newNamespace, _ := moveDescriptorToSchema(b, descID, currNamespace, newSchemaID) // Log event for audit logging. kind := tree.GetTableType(n.IsSequence, n.IsView, n.IsMaterialized) @@ -85,7 +75,7 @@ func AlterTableSetSchema(b BuildCtx, n *tree.AlterTableSetSchema) { NewDescriptorName: newName.FQString(), DescriptorType: kind, } - b.LogEventForExistingPayload(&newNamespace, setSchemaEvent) + b.LogEventForExistingPayload(newNamespace, setSchemaEvent) } func resolveSchemaByName(b BuildCtx, schemaName tree.Name, databaseID catid.DescID) *scpb.Schema { @@ -104,13 +94,38 @@ func resolveSchemaByName(b BuildCtx, schemaName tree.Name, databaseID catid.Desc return newSchema } -func panicIfSchemaIsTemporaryOrVirtual(newSchema *scpb.Schema) { - if newSchema.IsTemporary { +func panicIfSchemaIsTemporaryOrVirtual(schemaName tree.Name) { + name := string(schemaName) + if _, ok := catconstants.VirtualSchemaNames[name]; ok { panic(pgerror.Newf(pgcode.FeatureNotSupported, - "cannot move objects into or out of temporary schemas")) + "cannot move objects into or out of virtual schemas")) } - if newSchema.IsVirtual { + if strings.HasPrefix(name, catconstants.PgTempSchemaName) { panic(pgerror.Newf(pgcode.FeatureNotSupported, - "cannot move objects into or out of virtual schemas")) + "cannot move objects into or out of temporary schemas")) + } +} + +// moveDescriptorToSchema drops the old Namespace and SchemaChild elements for +// the given descriptor and adds new ones with the updated schema ID. +// It returns the new Namespace and SchemaChild elements. +func moveDescriptorToSchema( + b BuildCtx, descID catid.DescID, currNamespace *scpb.Namespace, newSchemaID catid.DescID, +) (*scpb.Namespace, *scpb.SchemaChild) { + // drop the old namespace and add a new one + newNamespace := *currNamespace + newNamespace.SchemaID = newSchemaID + b.Drop(currNamespace) + b.Add(&newNamespace) + + // drop old schema child and add new one + currSchemaChild := b.QueryByID(descID).FilterSchemaChild().MustGetOneElement() + newSchemaChild := &scpb.SchemaChild{ + ChildObjectID: descID, + SchemaID: newSchemaID, } + b.Drop(currSchemaChild) + b.Add(newSchemaChild) + + return &newNamespace, newSchemaChild } diff --git a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_type.go b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_type.go index 94bc72102de5..d1c9fa368a91 100644 --- a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_type.go +++ b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_type.go @@ -37,7 +37,7 @@ var supportedAlterTypeStatements = map[reflect.Type]supportedAlterTypeCommand{ reflect.TypeOf((*tree.AlterTypeAddValue)(nil)): {fn: alterTypeAddValue, on: true, checks: isV263Active}, reflect.TypeOf((*tree.AlterTypeRenameValue)(nil)): {fn: alterTypeRenameValue, on: true, checks: isV263Active}, reflect.TypeOf((*tree.AlterTypeRename)(nil)): {fn: alterTypeRename, on: true, checks: isV263Active}, - reflect.TypeOf((*tree.AlterTypeSetSchema)(nil)): {fn: alterTypeSetSchema, on: false, checks: nil}, + reflect.TypeOf((*tree.AlterTypeSetSchema)(nil)): {fn: alterTypeSetSchema, on: true, checks: isV263Active}, reflect.TypeOf((*tree.AlterTypeOwner)(nil)): {fn: alterTypeOwner, on: true, checks: isV263Active}, reflect.TypeOf((*tree.AlterTypeDropValue)(nil)): {fn: alterTypeDropValue, on: true, checks: isV263Active}, } @@ -280,7 +280,40 @@ func alterTypeRenameValue( func alterTypeSetSchema( b BuildCtx, tn *tree.TypeName, enumType *scpb.EnumType, t *tree.AlterTypeSetSchema, ) { - panic(scerrors.NotImplementedErrorf(t, "ALTER TYPE SET SCHEMA is not supported")) + typeID := enumType.TypeID + + currNamespace := mustRetrieveNamespaceElem(b, typeID) + currSchemaID := currNamespace.SchemaID + panicIfSchemaIsTemporaryOrVirtual(t.Schema) + newSchema := resolveSchemaByName(b, t.Schema, currNamespace.DatabaseID) + newSchemaID := newSchema.SchemaID + + if currSchemaID == newSchemaID { + return + } + + currName := tree.MakeTableNameFromPrefix(b.NamePrefix(enumType), tree.Name(currNamespace.Name)) + newName := currName + newName.SchemaName = t.Schema + + checkTableNameConflicts(b, currName, newName, currNamespace) + + arrayNamespace := mustRetrieveNamespaceElem(b, enumType.ArrayTypeID) + arrayName := tree.MakeTableNameFromPrefix( + b.NamePrefix(enumType), tree.Name(arrayNamespace.Name), + ) + newArrayName := arrayName + newArrayName.SchemaName = t.Schema + checkTableNameConflicts(b, arrayName, newArrayName, arrayNamespace) + + newNS, _ := moveDescriptorToSchema(b, typeID, currNamespace, newSchemaID) + moveDescriptorToSchema(b, enumType.ArrayTypeID, arrayNamespace, newSchemaID) + + b.LogEventForExistingPayload(newNS, &eventpb.SetSchema{ + DescriptorName: currName.FQString(), + NewDescriptorName: newName.FQString(), + DescriptorType: "type", + }) } func alterTypeOwner( diff --git a/pkg/sql/schemachanger/scbuild/testdata/alter_type b/pkg/sql/schemachanger/scbuild/testdata/alter_type index 9263f94851a4..aefdacf05eba 100644 --- a/pkg/sql/schemachanger/scbuild/testdata/alter_type +++ b/pkg/sql/schemachanger/scbuild/testdata/alter_type @@ -33,3 +33,29 @@ ALTER TYPE defaultdb.climes RENAME TO environs {databaseId: 100, descriptorId: 105, name: _climes, schemaId: 101} - [[Namespace:{DescID: 105, Name: _environs, ReferencedDescID: 100, IntValue: 101}, PUBLIC], ABSENT] {databaseId: 100, descriptorId: 105, name: _environs, schemaId: 101} +ALTER TYPE defaultdb.climes SET SCHEMA public +---- + +setup +CREATE SCHEMA s; +---- + +build +ALTER TYPE defaultdb.climes SET SCHEMA s +---- +- [[Namespace:{DescID: 104, Name: climes, ReferencedDescID: 100, IntValue: 101}, ABSENT], PUBLIC] + {databaseId: 100, descriptorId: 104, name: climes, schemaId: 101} +- [[SchemaChild:{DescID: 104, ReferencedDescID: 101}, ABSENT], PUBLIC] + {childObjectId: 104, schemaId: 101} +- [[Namespace:{DescID: 105, Name: _climes, ReferencedDescID: 100, IntValue: 101}, ABSENT], PUBLIC] + {databaseId: 100, descriptorId: 105, name: _climes, schemaId: 101} +- [[SchemaChild:{DescID: 105, ReferencedDescID: 101}, ABSENT], PUBLIC] + {childObjectId: 105, schemaId: 101} +- [[Namespace:{DescID: 104, Name: climes, ReferencedDescID: 100, IntValue: 106}, PUBLIC], ABSENT] + {databaseId: 100, descriptorId: 104, name: climes, schemaId: 106} +- [[SchemaChild:{DescID: 104, ReferencedDescID: 106}, PUBLIC], ABSENT] + {childObjectId: 104, schemaId: 106} +- [[Namespace:{DescID: 105, Name: _climes, ReferencedDescID: 100, IntValue: 106}, PUBLIC], ABSENT] + {databaseId: 100, descriptorId: 105, name: _climes, schemaId: 106} +- [[SchemaChild:{DescID: 105, ReferencedDescID: 106}, PUBLIC], ABSENT] + {childObjectId: 105, schemaId: 106} diff --git a/pkg/sql/schemachanger/scbuild/testdata/unimplemented_alter_type b/pkg/sql/schemachanger/scbuild/testdata/unimplemented_alter_type deleted file mode 100644 index ccfb5dcc7757..000000000000 --- a/pkg/sql/schemachanger/scbuild/testdata/unimplemented_alter_type +++ /dev/null @@ -1,7 +0,0 @@ -setup -CREATE TYPE defaultdb.climes AS ENUM ('tropical', 'arctic', 'mediterranean'); ----- - -unimplemented -ALTER TYPE defaultdb.climes SET SCHEMA public ----- diff --git a/pkg/sql/schemachanger/scexec/scmutationexec/references.go b/pkg/sql/schemachanger/scexec/scmutationexec/references.go index c8cf89a2b372..c27b14f53d79 100644 --- a/pkg/sql/schemachanger/scexec/scmutationexec/references.go +++ b/pkg/sql/schemachanger/scexec/scmutationexec/references.go @@ -12,6 +12,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/funcdesc" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" "github.com/cockroachdb/cockroach/pkg/sql/sem/catid" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" @@ -885,6 +886,13 @@ func (i *immediateVisitor) SetObjectParentID(ctx context.Context, op scop.SetObj sc.AddFunction(obj.GetName(), ol) case *tabledesc.Mutable: t.UnexposedParentSchemaID = op.ObjParent.SchemaID + case *typedesc.Mutable: + sc, err := i.checkOutSchema(ctx, op.ObjParent.SchemaID) + if err != nil { + return err + } + t.ParentID = sc.GetParentID() + t.ParentSchemaID = sc.GetID() default: return errors.AssertionFailedf("unexpected descriptor type %T for SetObjectParentID", obj) } diff --git a/pkg/sql/schemachanger/sctest_generated_test.go b/pkg/sql/schemachanger/sctest_generated_test.go index d972b892fe51..fcbbe54c7576 100644 --- a/pkg/sql/schemachanger/sctest_generated_test.go +++ b/pkg/sql/schemachanger/sctest_generated_test.go @@ -589,6 +589,13 @@ func TestEndToEndSideEffects_alter_type_rename_value(t *testing.T) { sctest.EndToEndSideEffects(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestEndToEndSideEffects_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.EndToEndSideEffects(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestEndToEndSideEffects_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1492,6 +1499,13 @@ func TestExecuteWithDMLInjection_alter_type_rename_value(t *testing.T) { sctest.ExecuteWithDMLInjection(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestExecuteWithDMLInjection_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.ExecuteWithDMLInjection(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestExecuteWithDMLInjection_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -2395,6 +2409,13 @@ func TestGenerateSchemaChangeCorpus_alter_type_rename_value(t *testing.T) { sctest.GenerateSchemaChangeCorpus(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestGenerateSchemaChangeCorpus_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.GenerateSchemaChangeCorpus(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestGenerateSchemaChangeCorpus_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -3298,6 +3319,13 @@ func TestPause_alter_type_rename_value(t *testing.T) { sctest.Pause(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestPause_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.Pause(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestPause_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -4201,6 +4229,13 @@ func TestPauseMixedVersion_alter_type_rename_value(t *testing.T) { sctest.PauseMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestPauseMixedVersion_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.PauseMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestPauseMixedVersion_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -5104,6 +5139,13 @@ func TestRollback_alter_type_rename_value(t *testing.T) { sctest.Rollback(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestRollback_alter_type_set_schema(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema" + sctest.Rollback(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestRollback_comment_on_type_composite(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) diff --git a/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.definition b/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.definition new file mode 100644 index 000000000000..90f8f9dc573f --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.definition @@ -0,0 +1,8 @@ +setup +CREATE TYPE salmon AS ENUM('chinook', 'sockeye'); +CREATE SCHEMA fish; +---- + +test +ALTER TYPE salmon SET SCHEMA fish; +---- diff --git a/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.explain b/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.explain new file mode 100644 index 000000000000..10fc29dad22f --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.explain @@ -0,0 +1,69 @@ +/* setup */ +CREATE TYPE salmon AS ENUM('chinook', 'sockeye'); +CREATE SCHEMA fish; + +/* test */ +EXPLAIN (DDL) ALTER TYPE salmon SET SCHEMA fish; +---- +Schema change plan for ALTER TYPE ‹defaultdb›.‹public›.‹salmon› SET SCHEMA ‹fish›; + ├── StatementPhase + │ └── Stage 1 of 1 in StatementPhase + │ ├── 4 elements transitioning toward PUBLIC + │ │ ├── ABSENT → PUBLIC Namespace:{DescID: 104 (salmon-salmon+), Name: "salmon", ReferencedDescID: 100 (defaultdb), IntValue: 106} + │ │ ├── ABSENT → PUBLIC SchemaChild:{DescID: 104 (salmon-salmon+), ReferencedDescID: 106 (fish)} + │ │ ├── ABSENT → PUBLIC Namespace:{DescID: 105 (_salmon-_salmon+), Name: "_salmon", ReferencedDescID: 100 (defaultdb), IntValue: 106} + │ │ └── ABSENT → PUBLIC SchemaChild:{DescID: 105 (_salmon-_salmon+), ReferencedDescID: 106 (fish)} + │ ├── 4 elements transitioning toward ABSENT + │ │ ├── PUBLIC → ABSENT Namespace:{DescID: 104 (salmon-salmon+), Name: "salmon", ReferencedDescID: 100 (defaultdb), IntValue: 101} + │ │ ├── PUBLIC → ABSENT SchemaChild:{DescID: 104 (salmon-salmon+), ReferencedDescID: 101 (public)} + │ │ ├── PUBLIC → ABSENT Namespace:{DescID: 105 (_salmon-_salmon+), Name: "_salmon", ReferencedDescID: 100 (defaultdb), IntValue: 101} + │ │ └── PUBLIC → ABSENT SchemaChild:{DescID: 105 (_salmon-_salmon+), ReferencedDescID: 101 (public)} + │ └── 10 Mutation operations + │ ├── DrainDescriptorName {"Namespace":{"DatabaseID":100,"DescriptorID":104,"Name":"salmon","SchemaID":101}} + │ ├── RemoveObjectParent {"ObjectID":104,"ParentSchemaID":101} + │ ├── DrainDescriptorName {"Namespace":{"DatabaseID":100,"DescriptorID":105,"Name":"_salmon","SchemaID":101}} + │ ├── RemoveObjectParent {"ObjectID":105,"ParentSchemaID":101} + │ ├── SetNameInDescriptor {"DescriptorID":104,"Name":"salmon"} + │ ├── AddDescriptorName {"Namespace":{"DatabaseID":100,"DescriptorID":104,"Name":"salmon","SchemaID":106}} + │ ├── SetObjectParentID {"ObjParent":{"ChildObjectID":104,"SchemaID":106}} + │ ├── SetNameInDescriptor {"DescriptorID":105,"Name":"_salmon"} + │ ├── AddDescriptorName {"Namespace":{"DatabaseID":100,"DescriptorID":105,"Name":"_salmon","SchemaID":106}} + │ └── SetObjectParentID {"ObjParent":{"ChildObjectID":105,"SchemaID":106}} + └── PreCommitPhase + ├── Stage 1 of 2 in PreCommitPhase + │ ├── 4 elements transitioning toward PUBLIC + │ │ ├── PUBLIC → ABSENT Namespace:{DescID: 104 (salmon-salmon+), Name: "salmon", ReferencedDescID: 100 (defaultdb), IntValue: 106} + │ │ ├── PUBLIC → ABSENT SchemaChild:{DescID: 104 (salmon-salmon+), ReferencedDescID: 106 (fish)} + │ │ ├── PUBLIC → ABSENT Namespace:{DescID: 105 (_salmon-_salmon+), Name: "_salmon", ReferencedDescID: 100 (defaultdb), IntValue: 106} + │ │ └── PUBLIC → ABSENT SchemaChild:{DescID: 105 (_salmon-_salmon+), ReferencedDescID: 106 (fish)} + │ ├── 4 elements transitioning toward ABSENT + │ │ ├── ABSENT → PUBLIC Namespace:{DescID: 104 (salmon-salmon+), Name: "salmon", ReferencedDescID: 100 (defaultdb), IntValue: 101} + │ │ ├── ABSENT → PUBLIC SchemaChild:{DescID: 104 (salmon-salmon+), ReferencedDescID: 101 (public)} + │ │ ├── ABSENT → PUBLIC Namespace:{DescID: 105 (_salmon-_salmon+), Name: "_salmon", ReferencedDescID: 100 (defaultdb), IntValue: 101} + │ │ └── ABSENT → PUBLIC SchemaChild:{DescID: 105 (_salmon-_salmon+), ReferencedDescID: 101 (public)} + │ └── 1 Mutation operation + │ └── UndoAllInTxnImmediateMutationOpSideEffects + └── Stage 2 of 2 in PreCommitPhase + ├── 4 elements transitioning toward PUBLIC + │ ├── ABSENT → PUBLIC Namespace:{DescID: 104 (salmon-salmon+), Name: "salmon", ReferencedDescID: 100 (defaultdb), IntValue: 106} + │ ├── ABSENT → PUBLIC SchemaChild:{DescID: 104 (salmon-salmon+), ReferencedDescID: 106 (fish)} + │ ├── ABSENT → PUBLIC Namespace:{DescID: 105 (_salmon-_salmon+), Name: "_salmon", ReferencedDescID: 100 (defaultdb), IntValue: 106} + │ └── ABSENT → PUBLIC SchemaChild:{DescID: 105 (_salmon-_salmon+), ReferencedDescID: 106 (fish)} + ├── 4 elements transitioning toward ABSENT + │ ├── PUBLIC → ABSENT Namespace:{DescID: 104 (salmon-salmon+), Name: "salmon", ReferencedDescID: 100 (defaultdb), IntValue: 101} + │ ├── PUBLIC → ABSENT SchemaChild:{DescID: 104 (salmon-salmon+), ReferencedDescID: 101 (public)} + │ ├── PUBLIC → ABSENT Namespace:{DescID: 105 (_salmon-_salmon+), Name: "_salmon", ReferencedDescID: 100 (defaultdb), IntValue: 101} + │ └── PUBLIC → ABSENT SchemaChild:{DescID: 105 (_salmon-_salmon+), ReferencedDescID: 101 (public)} + └── 12 Mutation operations + ├── DrainDescriptorName {"Namespace":{"DatabaseID":100,"DescriptorID":104,"Name":"salmon","SchemaID":101}} + ├── RemoveObjectParent {"ObjectID":104,"ParentSchemaID":101} + ├── DrainDescriptorName {"Namespace":{"DatabaseID":100,"DescriptorID":105,"Name":"_salmon","SchemaID":101}} + ├── RemoveObjectParent {"ObjectID":105,"ParentSchemaID":101} + ├── SetNameInDescriptor {"DescriptorID":104,"Name":"salmon"} + ├── AddDescriptorName {"Namespace":{"DatabaseID":100,"DescriptorID":104,"Name":"salmon","SchemaID":106}} + ├── UpdateTTLScheduleMetadata {"NewName":"salmon","TableID":104} + ├── SetObjectParentID {"ObjParent":{"ChildObjectID":104,"SchemaID":106}} + ├── SetNameInDescriptor {"DescriptorID":105,"Name":"_salmon"} + ├── AddDescriptorName {"Namespace":{"DatabaseID":100,"DescriptorID":105,"Name":"_salmon","SchemaID":106}} + ├── UpdateTTLScheduleMetadata {"NewName":"_salmon","TableID":105} + └── SetObjectParentID {"ObjParent":{"ChildObjectID":105,"SchemaID":106}} diff --git a/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.explain_shape b/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.explain_shape new file mode 100644 index 000000000000..f5d614f1e8c9 --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.explain_shape @@ -0,0 +1,9 @@ +/* setup */ +CREATE TYPE salmon AS ENUM('chinook', 'sockeye'); +CREATE SCHEMA fish; + +/* test */ +EXPLAIN (DDL, SHAPE) ALTER TYPE salmon SET SCHEMA fish; +---- +Schema change plan for ALTER TYPE ‹defaultdb›.‹public›.‹salmon› SET SCHEMA ‹fish›; + └── execute 1 system table mutations transaction diff --git a/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.side_effects b/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.side_effects new file mode 100644 index 000000000000..39d08442c28a --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/alter_type_set_schema/alter_type_set_schema.side_effects @@ -0,0 +1,108 @@ +/* setup */ +CREATE TYPE salmon AS ENUM('chinook', 'sockeye'); +CREATE SCHEMA fish; +---- +... ++object {100 101 salmon} -> 104 ++object {100 101 _salmon} -> 105 ++schema {100 0 fish} -> 106 + +/* test */ +ALTER TYPE salmon SET SCHEMA fish; +---- +begin transaction #1 +# begin StatementPhase +checking for feature: ALTER TYPE +increment telemetry for sql.schema.alter_type.set_schema +increment telemetry for sql.udts.alter_enum +write *eventpb.SetSchema to event log: + descriptorName: defaultdb.public.salmon + descriptorType: type + newDescriptorName: defaultdb.fish.salmon + sql: + descriptorId: 104 + statement: ALTER TYPE ‹defaultdb›.‹public›.‹salmon› SET SCHEMA ‹fish› + tag: ALTER TYPE + user: root +## StatementPhase stage 1 of 1 with 10 MutationType ops +delete object namespace entry {100 101 salmon} -> 104 +delete object namespace entry {100 101 _salmon} -> 105 +add object namespace entry {100 106 salmon} -> 104 +add object namespace entry {100 106 _salmon} -> 105 +upsert descriptor #104 + ... + name: salmon + parentId: 100 + - parentSchemaId: 101 + + parentSchemaId: 106 + privileges: + ownerProto: root + ... + withGrantOption: "2" + version: 3 + - version: "1" + + version: "2" +upsert descriptor #105 + ... + name: _salmon + parentId: 100 + - parentSchemaId: 101 + + parentSchemaId: 106 + privileges: + ownerProto: root + ... + withGrantOption: "2" + version: 3 + - version: "1" + + version: "2" +upsert descriptor #106 + ... + withGrantOption: "2" + version: 3 + - version: "1" + + version: "2" +# end StatementPhase +# begin PreCommitPhase +## PreCommitPhase stage 1 of 2 with 1 MutationType op +undo all catalog changes within txn #1 +persist all catalog changes to storage +## PreCommitPhase stage 2 of 2 with 12 MutationType ops +delete object namespace entry {100 101 salmon} -> 104 +delete object namespace entry {100 101 _salmon} -> 105 +add object namespace entry {100 106 salmon} -> 104 +add object namespace entry {100 106 _salmon} -> 105 +upsert descriptor #104 + ... + name: salmon + parentId: 100 + - parentSchemaId: 101 + + parentSchemaId: 106 + privileges: + ownerProto: root + ... + withGrantOption: "2" + version: 3 + - version: "1" + + version: "2" +upsert descriptor #105 + ... + name: _salmon + parentId: 100 + - parentSchemaId: 101 + + parentSchemaId: 106 + privileges: + ownerProto: root + ... + withGrantOption: "2" + version: 3 + - version: "1" + + version: "2" +upsert descriptor #106 + ... + withGrantOption: "2" + version: 3 + - version: "1" + + version: "2" +persist all catalog changes to storage +# end PreCommitPhase +commit transaction #1