From 75a1f36daeebcaa1bf9333f1912b83c2b7a54472 Mon Sep 17 00:00:00 2001 From: Faizan Qazi Date: Thu, 5 Sep 2024 18:17:53 +0000 Subject: [PATCH 1/2] rpc,tenantcapabilities: allow cross tenant reads in shared service Previously, we added support for cross tenant reads, but these would only function from the system tenant. When running as a shared service tenant, we should also allow reading the spans of other tenants. To address this, this patch updates authorization logic to look at the authorization mode to determine if the cross tenant read check should be enforced, which will allow shared service tenants to exempt. Fixes: #130182 Release note: None --- pkg/kv/kvserver/tenantrate/limiter_test.go | 4 +- .../tenantcapabilities/interfaces.go | 2 +- .../allow_everything.go | 4 +- .../allow_nothing.go | 4 +- .../authorizer.go | 18 ++++++- pkg/rpc/auth_tenant.go | 54 ++++++++++--------- pkg/rpc/auth_test.go | 2 +- 7 files changed, 56 insertions(+), 32 deletions(-) diff --git a/pkg/kv/kvserver/tenantrate/limiter_test.go b/pkg/kv/kvserver/tenantrate/limiter_test.go index 50fe77716cc4..65251847bbf2 100644 --- a/pkg/kv/kvserver/tenantrate/limiter_test.go +++ b/pkg/kv/kvserver/tenantrate/limiter_test.go @@ -667,7 +667,7 @@ func (ts *testState) BindReader(tenantcapabilities.Reader) {} var _ tenantcapabilities.Authorizer = &testState{} -func (ts *testState) HasCrossTenantRead(tenID roachpb.TenantID) bool { +func (ts *testState) HasCrossTenantRead(ctx context.Context, tenID roachpb.TenantID) bool { return false } @@ -789,7 +789,7 @@ type fakeAuthorizer struct{} var _ tenantcapabilities.Authorizer = &fakeAuthorizer{} -func (fakeAuthorizer) HasCrossTenantRead(tenID roachpb.TenantID) bool { +func (fakeAuthorizer) HasCrossTenantRead(ctx context.Context, tenID roachpb.TenantID) bool { return false } diff --git a/pkg/multitenant/tenantcapabilities/interfaces.go b/pkg/multitenant/tenantcapabilities/interfaces.go index 62b17dd34474..4565c28de0c2 100644 --- a/pkg/multitenant/tenantcapabilities/interfaces.go +++ b/pkg/multitenant/tenantcapabilities/interfaces.go @@ -42,7 +42,7 @@ type Reader interface { // usage pattern over a timespan. type Authorizer interface { // HasCrossTenantRead returns true if a tenant can read other tenant spans. - HasCrossTenantRead(tenID roachpb.TenantID) bool + HasCrossTenantRead(ctx context.Context, tenID roachpb.TenantID) bool // HasCapabilityForBatch returns an error if a tenant, referenced by its ID, // is not allowed to execute the supplied batch request given the capabilities diff --git a/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/allow_everything.go b/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/allow_everything.go index 14c236ccd306..51d02e67f1cb 100644 --- a/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/allow_everything.go +++ b/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/allow_everything.go @@ -30,7 +30,9 @@ func NewAllowEverythingAuthorizer() *AllowEverythingAuthorizer { } // HasCrossTenantRead returns true if a tenant can read from other tenants. -func (n *AllowEverythingAuthorizer) HasCrossTenantRead(tenID roachpb.TenantID) bool { +func (n *AllowEverythingAuthorizer) HasCrossTenantRead( + ctx context.Context, tenID roachpb.TenantID, +) bool { return true } diff --git a/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/allow_nothing.go b/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/allow_nothing.go index 8fb0fc170bd2..2af86695ecaf 100644 --- a/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/allow_nothing.go +++ b/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/allow_nothing.go @@ -31,7 +31,9 @@ func NewAllowNothingAuthorizer() *AllowNothingAuthorizer { } // HasCrossTenantRead returns true if a tenant can read from other tenants. -func (n *AllowNothingAuthorizer) HasCrossTenantRead(tenID roachpb.TenantID) bool { +func (n *AllowNothingAuthorizer) HasCrossTenantRead( + ctx context.Context, tenID roachpb.TenantID, +) bool { return false } diff --git a/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/authorizer.go b/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/authorizer.go index f66fb9c78ef9..41c214909167 100644 --- a/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/authorizer.go +++ b/pkg/multitenant/tenantcapabilities/tenantcapabilitiesauthorizer/authorizer.go @@ -98,8 +98,22 @@ func New(settings *cluster.Settings, knobs *tenantcapabilities.TestingKnobs) *Au } // HasCrossTenantRead returns true if a tenant can read from other tenants. -func (a *Authorizer) HasCrossTenantRead(tenID roachpb.TenantID) bool { - return tenID.IsSystem() +func (a *Authorizer) HasCrossTenantRead(ctx context.Context, tenID roachpb.TenantID) bool { + if tenID.IsSystem() { + // The system tenant has access to all request types. + return true + } + _, mode := a.getMode(ctx, tenID) + switch mode { + case authorizerModeOn, authorizerModeV222: + return false + case authorizerModeAllowAll: + return true + default: + err := errors.AssertionFailedf("unknown authorizer mode: %d", mode) + logcrash.ReportOrPanic(ctx, &a.settings.SV, "%v", err) + return false + } } // HasCapabilityForBatch implements the tenantcapabilities.Authorizer interface. diff --git a/pkg/rpc/auth_tenant.go b/pkg/rpc/auth_tenant.go index dda79e12fc4e..377c1c8a26c8 100644 --- a/pkg/rpc/auth_tenant.go +++ b/pkg/rpc/auth_tenant.go @@ -64,7 +64,7 @@ func (a tenantAuthorizer) authorize( return a.authBatch(ctx, sv, tenID, req.(*kvpb.BatchRequest)) case "/cockroach.roachpb.Internal/RangeLookup": - return a.authRangeLookup(tenID, req.(*kvpb.RangeLookupRequest)) + return a.authRangeLookup(ctx, tenID, req.(*kvpb.RangeLookupRequest)) case "/cockroach.roachpb.Internal/RangeFeed", "/cockroach.roachpb.Internal/MuxRangeFeed": return a.authRangeFeed(tenID, req.(*kvpb.RangeFeedRequest)) @@ -123,22 +123,22 @@ func (a tenantAuthorizer) authorize( return a.authTenant(tenID) case "/cockroach.server.serverpb.Status/SpanStats": - return a.authSpanStats(tenID, req.(*roachpb.SpanStatsRequest)) + return a.authSpanStats(ctx, tenID, req.(*roachpb.SpanStatsRequest)) case "/cockroach.roachpb.Internal/GetSpanConfigs": - return a.authGetSpanConfigs(tenID, req.(*roachpb.GetSpanConfigsRequest)) + return a.authGetSpanConfigs(ctx, tenID, req.(*roachpb.GetSpanConfigsRequest)) case "/cockroach.roachpb.Internal/SpanConfigConformance": - return a.authSpanConfigConformance(tenID, req.(*roachpb.SpanConfigConformanceRequest)) + return a.authSpanConfigConformance(ctx, tenID, req.(*roachpb.SpanConfigConformanceRequest)) case "/cockroach.roachpb.Internal/GetAllSystemSpanConfigsThatApply": return a.authGetAllSystemSpanConfigsThatApply(tenID, req.(*roachpb.GetAllSystemSpanConfigsThatApplyRequest)) case "/cockroach.roachpb.Internal/UpdateSpanConfigs": - return a.authUpdateSpanConfigs(tenID, req.(*roachpb.UpdateSpanConfigsRequest)) + return a.authUpdateSpanConfigs(ctx, tenID, req.(*roachpb.UpdateSpanConfigsRequest)) case "/cockroach.roachpb.Internal/GetRangeDescriptors": - return a.authGetRangeDescriptors(tenID, req.(*kvpb.GetRangeDescriptorsRequest)) + return a.authGetRangeDescriptors(ctx, tenID, req.(*kvpb.GetRangeDescriptorsRequest)) case "/cockroach.server.serverpb.Status/HotRangesV2": return a.authHotRangesV2(tenID) @@ -199,7 +199,7 @@ func (a tenantAuthorizer) authBatch( tenSpan := tenantPrefix(tenID) if outsideTenant(rSpan, tenSpan) { - if args.IsReadOnly() && a.capabilitiesAuthorizer.HasCrossTenantRead(tenID) { + if args.IsReadOnly() && a.capabilitiesAuthorizer.HasCrossTenantRead(ctx, tenID) { return nil } return spanErr(rSpan, tenSpan) @@ -208,16 +208,16 @@ func (a tenantAuthorizer) authBatch( } func (a tenantAuthorizer) authGetRangeDescriptors( - tenID roachpb.TenantID, args *kvpb.GetRangeDescriptorsRequest, + ctx context.Context, tenID roachpb.TenantID, args *kvpb.GetRangeDescriptorsRequest, ) error { - return validateSpan(tenID, args.Span, true, a) + return validateSpan(ctx, tenID, args.Span, true, a) } func (a tenantAuthorizer) authSpanStats( - tenID roachpb.TenantID, args *roachpb.SpanStatsRequest, + ctx context.Context, tenID roachpb.TenantID, args *roachpb.SpanStatsRequest, ) error { for _, span := range args.Spans { - err := validateSpan(tenID, span, true, a) + err := validateSpan(ctx, tenID, span, true, a) if err != nil { return err } @@ -228,12 +228,12 @@ func (a tenantAuthorizer) authSpanStats( // authRangeLookup authorizes the provided tenant to invoke the RangeLookup RPC // with the provided args. func (a tenantAuthorizer) authRangeLookup( - tenID roachpb.TenantID, args *kvpb.RangeLookupRequest, + ctx context.Context, tenID roachpb.TenantID, args *kvpb.RangeLookupRequest, ) error { tenSpan := tenantPrefix(tenID) if !tenSpan.ContainsKey(args.Key) { // Allow it anyway if the tenant can read other tenants. - if a.capabilitiesAuthorizer.HasCrossTenantRead(tenID) { + if a.capabilitiesAuthorizer.HasCrossTenantRead(ctx, tenID) { return nil } return authErrorf("requested key %s not fully contained in tenant keyspace %s", args.Key, tenSpan) @@ -351,10 +351,10 @@ func (a tenantAuthorizer) authGetAllSystemSpanConfigsThatApply( // authGetSpanConfigs authorizes the provided tenant to invoke the // GetSpanConfigs RPC with the provided args. func (a tenantAuthorizer) authGetSpanConfigs( - tenID roachpb.TenantID, args *roachpb.GetSpanConfigsRequest, + ctx context.Context, tenID roachpb.TenantID, args *roachpb.GetSpanConfigsRequest, ) error { for _, target := range args.Targets { - if err := validateSpanConfigTarget(tenID, target, true, a); err != nil { + if err := validateSpanConfigTarget(ctx, tenID, target, true, a); err != nil { return err } } @@ -364,15 +364,15 @@ func (a tenantAuthorizer) authGetSpanConfigs( // authUpdateSpanConfigs authorizes the provided tenant to invoke the // UpdateSpanConfigs RPC with the provided args. func (a tenantAuthorizer) authUpdateSpanConfigs( - tenID roachpb.TenantID, args *roachpb.UpdateSpanConfigsRequest, + ctx context.Context, tenID roachpb.TenantID, args *roachpb.UpdateSpanConfigsRequest, ) error { for _, entry := range args.ToUpsert { - if err := validateSpanConfigTarget(tenID, entry.Target, false, a); err != nil { + if err := validateSpanConfigTarget(ctx, tenID, entry.Target, false, a); err != nil { return err } } for _, target := range args.ToDelete { - if err := validateSpanConfigTarget(tenID, target, false, a); err != nil { + if err := validateSpanConfigTarget(ctx, tenID, target, false, a); err != nil { return err } } @@ -393,10 +393,10 @@ func (a tenantAuthorizer) authHotRangesV2(tenID roachpb.TenantID) error { // authSpanConfigConformance authorizes the provided tenant to invoke the // SpanConfigConformance RPC with the provided args. func (a tenantAuthorizer) authSpanConfigConformance( - tenID roachpb.TenantID, args *roachpb.SpanConfigConformanceRequest, + ctx context.Context, tenID roachpb.TenantID, args *roachpb.SpanConfigConformanceRequest, ) error { for _, sp := range args.Spans { - if err := validateSpan(tenID, sp, false, a); err != nil { + if err := validateSpan(ctx, tenID, sp, false, a); err != nil { return err } } @@ -434,7 +434,11 @@ func (a tenantAuthorizer) authTSDBQuery( // wholly contained within the tenant keyspace and system span config targets // must be well-formed. func validateSpanConfigTarget( - tenID roachpb.TenantID, spanConfigTarget roachpb.SpanConfigTarget, read bool, a tenantAuthorizer, + ctx context.Context, + tenID roachpb.TenantID, + spanConfigTarget roachpb.SpanConfigTarget, + read bool, + a tenantAuthorizer, ) error { validateSystemTarget := func(target roachpb.SystemSpanConfigTarget) error { if target.SourceTenantID != tenID { @@ -463,7 +467,7 @@ func validateSpanConfigTarget( switch spanConfigTarget.Union.(type) { case *roachpb.SpanConfigTarget_Span: - return validateSpan(tenID, *spanConfigTarget.GetSpan(), read, a) + return validateSpan(ctx, tenID, *spanConfigTarget.GetSpan(), read, a) case *roachpb.SpanConfigTarget_SystemSpanConfigTarget: return validateSystemTarget(*spanConfigTarget.GetSystemSpanConfigTarget()) default: @@ -471,7 +475,9 @@ func validateSpanConfigTarget( } } -func validateSpan(tenID roachpb.TenantID, sp roachpb.Span, isRead bool, a tenantAuthorizer) error { +func validateSpan( + ctx context.Context, tenID roachpb.TenantID, sp roachpb.Span, isRead bool, a tenantAuthorizer, +) error { tenSpan := tenantPrefix(tenID) rSpan, err := keys.SpanAddr(sp) if err != nil { @@ -479,7 +485,7 @@ func validateSpan(tenID roachpb.TenantID, sp roachpb.Span, isRead bool, a tenant } if outsideTenant(rSpan, tenSpan) { // Allow it anyway if the tenant can read other tenants. - if isRead && a.capabilitiesAuthorizer.HasCrossTenantRead(tenID) { + if isRead && a.capabilitiesAuthorizer.HasCrossTenantRead(ctx, tenID) { return nil } return spanErr(rSpan, tenSpan) diff --git a/pkg/rpc/auth_test.go b/pkg/rpc/auth_test.go index b59b29a777dc..f656522965ba 100644 --- a/pkg/rpc/auth_test.go +++ b/pkg/rpc/auth_test.go @@ -1120,7 +1120,7 @@ func (m mockAuthorizer) HasProcessDebugCapability( return errors.New("tenant does not have capability") } -func (m mockAuthorizer) HasCrossTenantRead(tenID roachpb.TenantID) bool { +func (m mockAuthorizer) HasCrossTenantRead(ctx context.Context, tenID roachpb.TenantID) bool { return m.hasCrossTenantRead } From e023883deff2708056567eacf9e0e3f5530a1e7a Mon Sep 17 00:00:00 2001 From: Bergin Dedej Date: Tue, 20 Aug 2024 18:53:48 -0400 Subject: [PATCH 2/2] sql/schemachanger: support ADD COLUMN SERIAL for DSC unique_rowid only Previously we could only add SERIAL columns with the legacy schema changer with these code changes the ALTER TABLE ADD COLUMN statement can now add SERIAL type columns via the declarative schema changer for the default rowid serial_normalization mode. Informs: #126900 Release note: none --- .../backup_base_generated_test.go | 28 + pkg/sql/catalog/BUILD.bazel | 1 + pkg/sql/catalog/serial_helper.go | 54 ++ .../logictest/testdata/logic_test/alter_table | 34 + .../scbuildstmt/alter_table_add_column.go | 47 +- .../scbuild/testdata/alter_table_add_column | 42 ++ .../testdata/unimplemented_alter_table | 4 - .../scplan/testdata/alter_table_add_column | 368 ++++++++++ .../schemachanger/sctest_generated_test.go | 42 ++ .../add_column_serial.definition | 33 + .../add_column_serial.explain | 183 +++++ .../add_column_serial.explain_shape | 18 + .../add_column_serial.side_effects | 634 ++++++++++++++++++ ...add_column_serial__rollback_1_of_7.explain | 36 + ...add_column_serial__rollback_2_of_7.explain | 49 ++ ...add_column_serial__rollback_3_of_7.explain | 49 ++ ...add_column_serial__rollback_4_of_7.explain | 49 ++ ...add_column_serial__rollback_5_of_7.explain | 51 ++ ...add_column_serial__rollback_6_of_7.explain | 51 ++ ...add_column_serial__rollback_7_of_7.explain | 49 ++ pkg/sql/serial.go | 36 +- 21 files changed, 1820 insertions(+), 38 deletions(-) create mode 100644 pkg/sql/catalog/serial_helper.go create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.definition create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.explain create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.explain_shape create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.side_effects create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_1_of_7.explain create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_2_of_7.explain create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_3_of_7.explain create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_4_of_7.explain create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_5_of_7.explain create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_6_of_7.explain create mode 100644 pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_7_of_7.explain diff --git a/pkg/ccl/schemachangerccl/backup_base_generated_test.go b/pkg/ccl/schemachangerccl/backup_base_generated_test.go index 4e5e35c7ea38..e7e01bc8dc67 100644 --- a/pkg/ccl/schemachangerccl/backup_base_generated_test.go +++ b/pkg/ccl/schemachangerccl/backup_base_generated_test.go @@ -53,6 +53,13 @@ func TestBackupRollbacks_base_add_column_no_default(t *testing.T) { sctest.BackupRollbacks(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestBackupRollbacks_base_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.BackupRollbacks(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestBackupRollbacks_base_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -557,6 +564,13 @@ func TestBackupRollbacksMixedVersion_base_add_column_no_default(t *testing.T) { sctest.BackupRollbacksMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestBackupRollbacksMixedVersion_base_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.BackupRollbacksMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestBackupRollbacksMixedVersion_base_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1061,6 +1075,13 @@ func TestBackupSuccess_base_add_column_no_default(t *testing.T) { sctest.BackupSuccess(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestBackupSuccess_base_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.BackupSuccess(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestBackupSuccess_base_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1565,6 +1586,13 @@ func TestBackupSuccessMixedVersion_base_add_column_no_default(t *testing.T) { sctest.BackupSuccessMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestBackupSuccessMixedVersion_base_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.BackupSuccessMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestBackupSuccessMixedVersion_base_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) diff --git a/pkg/sql/catalog/BUILD.bazel b/pkg/sql/catalog/BUILD.bazel index 105b137fcf20..e859418225e6 100644 --- a/pkg/sql/catalog/BUILD.bazel +++ b/pkg/sql/catalog/BUILD.bazel @@ -11,6 +11,7 @@ go_library( "metadata.go", "post_deserialization_changes.go", "schema.go", + "serial_helper.go", "system_table.go", "table_col_map.go", "table_col_set.go", diff --git a/pkg/sql/catalog/serial_helper.go b/pkg/sql/catalog/serial_helper.go new file mode 100644 index 000000000000..530ed06bdcc5 --- /dev/null +++ b/pkg/sql/catalog/serial_helper.go @@ -0,0 +1,54 @@ +// Copyright 2024 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package catalog + +import ( + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/types" +) + +func UseRowID(d tree.ColumnTableDef) *tree.ColumnTableDef { + d.DefaultExpr.Expr = &tree.FuncExpr{Func: tree.WrapFunction("unique_rowid")} + d.Type = types.Int + // Column is non-nullable in all cases. PostgreSQL requires this. + d.Nullable.Nullability = tree.NotNull + + return &d +} + +func AssertValidSerialColumnDef(d *tree.ColumnTableDef, tableName *tree.TableName) error { + if d.HasDefaultExpr() { + // SERIAL implies a new default expression, we can't have one to + // start with. This is the error produced by pg in such case. + return pgerror.Newf(pgcode.Syntax, + "multiple default values specified for column %q of table %q", + tree.ErrString(&d.Name), tree.ErrString(tableName)) + } + + if d.Nullable.Nullability == tree.Null { + // SERIAL implies a non-NULL column, we can't accept a nullability + // spec. This is the error produced by pg in such case. + return pgerror.Newf(pgcode.Syntax, + "conflicting NULL/NOT NULL declarations for column %q of table %q", + tree.ErrString(&d.Name), tree.ErrString(tableName)) + } + + if d.Computed.Expr != nil { + // SERIAL cannot be a computed column. + return pgerror.Newf(pgcode.Syntax, + "SERIAL column %q of table %q cannot be computed", + tree.ErrString(&d.Name), tree.ErrString(tableName)) + } + + return nil +} diff --git a/pkg/sql/logictest/testdata/logic_test/alter_table b/pkg/sql/logictest/testdata/logic_test/alter_table index 440caa1fbb62..49985febb498 100644 --- a/pkg/sql/logictest/testdata/logic_test/alter_table +++ b/pkg/sql/logictest/testdata/logic_test/alter_table @@ -3967,3 +3967,37 @@ statement error pgcode 42601 variable sub-expressions are not allowed in EXPRESS ALTER TABLE t_124546 ADD CONSTRAINT ident UNIQUE ( ( EXISTS ( TABLE error FOR READ ONLY ) ) DESC ) STORING ( ident , ident ); subtest end + +subtest alter_table_add_column_serial + +statement ok +create table roach (id int); +insert into roach DEFAULT VALUES; +insert into roach DEFAULT VALUES; +SET serial_normalization = rowid + +statement ok +alter table roach add column serial_id SERIAL; + +query TTBTTTB colnames,rowsort +show columns from roach; +---- +column_name data_type is_nullable column_default generation_expression indices is_hidden +id INT8 true NULL · {roach_pkey} false +rowid INT8 false unique_rowid() · {roach_pkey} true +serial_id INT8 false unique_rowid() · {roach_pkey} false + +subtest end + +subtest unimplemented_for_non_rowid_in_DSC + +statement ok +SET serial_normalization = sql_sequence + +statement ok +SET use_declarative_schema_changer = unsafe_always + +statement error pq: \*tree.ColumnTableDef not implemented in the new schema changer: contains serial data type in unsupported mode +alter table roach add column serial_id2 SERIAL + +subtest end diff --git a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_column.go b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_column.go index 830b5d56168c..4fa86bd1fda6 100644 --- a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_column.go +++ b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_column.go @@ -15,6 +15,8 @@ import ( "sort" "strings" + "github.com/cockroachdb/cockroach/pkg/docs" + "github.com/cockroachdb/cockroach/pkg/server/telemetry" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catenumpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catpb" @@ -25,12 +27,14 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgnotice" "github.com/cockroachdb/cockroach/pkg/sql/privilege" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdecomp" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scerrors" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" "github.com/cockroachdb/cockroach/pkg/sql/sem/catid" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" "github.com/cockroachdb/cockroach/pkg/sql/sqlerrors" "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" "github.com/cockroachdb/cockroach/pkg/sql/types" @@ -71,7 +75,11 @@ func alterTableAddColumn( } } if d.IsSerial { - panic(scerrors.NotImplementedErrorf(d, "contains serial data type")) + if b.SessionData().SerialNormalizationMode != sessiondatapb.SerialUsesRowID { + panic(scerrors.NotImplementedErrorf(d, "contains serial data type in unsupported mode")) + } + d = alterTableAddColumnSerial(b, d, tn) + } if d.GeneratedIdentity.IsGeneratedAsIdentity { panic(scerrors.NotImplementedErrorf(d, "contains generated identity type")) @@ -259,6 +267,43 @@ func alterTableAddColumn( } } +func alterTableAddColumnSerial( + b BuildCtx, d *tree.ColumnTableDef, tn *tree.TableName, +) *tree.ColumnTableDef { + if err := catalog.AssertValidSerialColumnDef(d, tn); err != nil { + panic(err) + } + + defType, err := tree.ResolveType(b, d.Type, b.SemaCtx().GetTypeResolver()) + if err != nil { + panic(err) + } + + telemetry.Inc(sqltelemetry.SerialColumnNormalizationCounter( + defType.Name(), b.SessionData().SerialNormalizationMode.String())) + + if defType.Width() < types.Int.Width() { + b.EvalCtx().ClientNoticeSender.BufferClientNotice( + b, + errors.WithHintf( + pgnotice.Newf( + "upgrading the column %s to %s to utilize the session serial_normalization setting", + d.Name.String(), + types.Int.SQLString(), + ), + "change the serial_normalization to sql_sequence or sql_sequence_cached if you wish "+ + "to use a smaller sized serial column at the cost of performance. See %s", + docs.URL("serial.html"), + ), + ) + } + + // Serial is an alias for a real column definition. False indicates a remapped alias. + d.IsSerial = false + + return catalog.UseRowID(*d) +} + func columnNamesToIDs(b BuildCtx, tbl *scpb.Table) map[string]descpb.ColumnID { tableElts := b.QueryByID(tbl.TableID) namesToIDs := make(map[string]descpb.ColumnID) diff --git a/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_column b/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_column index ce1dc7dcb0d4..63b1756fa29a 100644 --- a/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_column +++ b/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_column @@ -153,6 +153,48 @@ ALTER TABLE defaultdb.foo ADD COLUMN a INT AS (i+1) STORED - [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 3}, TRANSIENT_ABSENT], ABSENT] {columnId: 2, indexId: 3, kind: STORED, tableId: 104} +build +ALTER TABLE defaultdb.foo ADD COLUMN serial_id SERIAL +---- +- [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 1}, ABSENT], PUBLIC] + {columnId: 1, indexId: 1, tableId: 104} +- [[PrimaryIndex:{DescID: 104, IndexID: 1, ConstraintID: 1}, ABSENT], PUBLIC] + {constraintId: 1, indexId: 1, isUnique: true, tableId: 104} +- [[IndexName:{DescID: 104, Name: foo_pkey, IndexID: 1}, ABSENT], PUBLIC] + {indexId: 1, name: foo_pkey, tableId: 104} +- [[IndexData:{DescID: 104, IndexID: 1}, ABSENT], PUBLIC] + {indexId: 1, tableId: 104} +- [[TableData:{DescID: 104, ReferencedDescID: 100}, PUBLIC], PUBLIC] + {databaseId: 100, tableId: 104} +- [[Column:{DescID: 104, ColumnID: 2}, PUBLIC], ABSENT] + {columnId: 2, tableId: 104} +- [[ColumnName:{DescID: 104, Name: serial_id, ColumnID: 2}, PUBLIC], ABSENT] + {columnId: 2, name: serial_id, tableId: 104} +- [[ColumnType:{DescID: 104, ColumnFamilyID: 0, ColumnID: 2, TypeName: INT8}, PUBLIC], ABSENT] + {columnId: 2, elementCreationMetadata: {in231OrLater: true, in243OrLater: true}, tableId: 104, type: {family: IntFamily, oid: 20, width: 64}, typeName: INT8} +- [[ColumnDefaultExpression:{DescID: 104, ColumnID: 2, Expr: unique_rowid()}, PUBLIC], ABSENT] + {columnId: 2, expr: unique_rowid(), tableId: 104} +- [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], ABSENT] + {constraintId: 2, indexId: 2, isUnique: true, sourceIndexId: 1, tableId: 104, temporaryIndexId: 3} +- [[IndexName:{DescID: 104, Name: foo_pkey, IndexID: 2}, PUBLIC], ABSENT] + {indexId: 2, name: foo_pkey, tableId: 104} +- [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 2}, PUBLIC], ABSENT] + {columnId: 1, indexId: 2, tableId: 104} +- [[IndexData:{DescID: 104, IndexID: 2}, PUBLIC], ABSENT] + {indexId: 2, tableId: 104} +- [[TemporaryIndex:{DescID: 104, IndexID: 3, ConstraintID: 3, SourceIndexID: 1}, TRANSIENT_ABSENT], ABSENT] + {constraintId: 3, indexId: 3, isUnique: true, sourceIndexId: 1, tableId: 104} +- [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 3}, TRANSIENT_ABSENT], ABSENT] + {columnId: 1, indexId: 3, tableId: 104} +- [[IndexData:{DescID: 104, IndexID: 3}, TRANSIENT_ABSENT], ABSENT] + {indexId: 3, tableId: 104} +- [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 2}, PUBLIC], ABSENT] + {columnId: 2, indexId: 2, kind: STORED, tableId: 104} +- [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 3}, TRANSIENT_ABSENT], ABSENT] + {columnId: 2, indexId: 3, kind: STORED, tableId: 104} +- [[ColumnNotNull:{DescID: 104, ColumnID: 2, IndexID: 2}, PUBLIC], ABSENT] + {columnId: 2, indexIdForValidation: 2, tableId: 104} + setup CREATE TABLE defaultdb.bar (j INT); ---- diff --git a/pkg/sql/schemachanger/scbuild/testdata/unimplemented_alter_table b/pkg/sql/schemachanger/scbuild/testdata/unimplemented_alter_table index ecc469652668..31350b82d852 100644 --- a/pkg/sql/schemachanger/scbuild/testdata/unimplemented_alter_table +++ b/pkg/sql/schemachanger/scbuild/testdata/unimplemented_alter_table @@ -12,10 +12,6 @@ CREATE TABLE defaultdb.foo ( ); ---- -unimplemented -ALTER TABLE defaultdb.foo ADD COLUMN j SERIAL ----- - unimplemented ALTER TABLE defaultdb.foo ALTER COLUMN i DROP NOT NULL ---- diff --git a/pkg/sql/schemachanger/scplan/testdata/alter_table_add_column b/pkg/sql/schemachanger/scplan/testdata/alter_table_add_column index 0ab9b0a1f1c8..4c226eea6f14 100644 --- a/pkg/sql/schemachanger/scplan/testdata/alter_table_add_column +++ b/pkg/sql/schemachanger/scplan/testdata/alter_table_add_column @@ -1374,6 +1374,374 @@ PostCommitNonRevertiblePhase stage 3 of 3 with 5 MutationType ops IsNonCancelable: true JobID: 1 +ops +ALTER TABLE defaultdb.foo ADD COLUMN serial_id SERIAL +---- +StatementPhase stage 1 of 1 with 10 MutationType ops + transitions: + [[Column:{DescID: 104, ColumnID: 2}, PUBLIC], ABSENT] -> DELETE_ONLY + [[ColumnName:{DescID: 104, Name: serial_id, ColumnID: 2}, PUBLIC], ABSENT] -> PUBLIC + [[ColumnType:{DescID: 104, ColumnFamilyID: 0, ColumnID: 2, TypeName: INT8}, PUBLIC], ABSENT] -> PUBLIC + [[ColumnDefaultExpression:{DescID: 104, ColumnID: 2, Expr: unique_rowid()}, PUBLIC], ABSENT] -> PUBLIC + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], ABSENT] -> BACKFILL_ONLY + [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 2}, PUBLIC], ABSENT] -> PUBLIC + [[IndexData:{DescID: 104, IndexID: 2}, PUBLIC], ABSENT] -> PUBLIC + [[TemporaryIndex:{DescID: 104, IndexID: 3, ConstraintID: 3, SourceIndexID: 1}, TRANSIENT_ABSENT], ABSENT] -> DELETE_ONLY + [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 3}, TRANSIENT_ABSENT], ABSENT] -> PUBLIC + [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 2}, PUBLIC], ABSENT] -> PUBLIC + [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 3}, TRANSIENT_ABSENT], ABSENT] -> PUBLIC + ops: + *scop.MakeAbsentColumnDeleteOnly + Column: + ColumnID: 2 + TableID: 104 + *scop.SetColumnName + ColumnID: 2 + Name: serial_id + TableID: 104 + *scop.UpsertColumnType + ColumnType: + ColumnID: 2 + ElementCreationMetadata: + in231OrLater: true + in243OrLater: true + TableID: 104 + TypeT: + Type: + family: IntFamily + oid: 20 + width: 64 + TypeName: INT8 + *scop.AddColumnDefaultExpression + Default: + ColumnID: 2 + Expression: + Expr: unique_rowid() + TableID: 104 + *scop.MakeAbsentIndexBackfilling + Index: + ConstraintID: 2 + IndexID: 2 + IsUnique: true + SourceIndexID: 1 + TableID: 104 + TemporaryIndexID: 3 + *scop.AddColumnToIndex + ColumnID: 1 + IndexID: 2 + TableID: 104 + *scop.MakeAbsentTempIndexDeleteOnly + Index: + ConstraintID: 3 + IndexID: 3 + IsUnique: true + SourceIndexID: 1 + TableID: 104 + *scop.AddColumnToIndex + ColumnID: 1 + IndexID: 3 + TableID: 104 + *scop.AddColumnToIndex + ColumnID: 2 + IndexID: 2 + Kind: 2 + TableID: 104 + *scop.AddColumnToIndex + ColumnID: 2 + IndexID: 3 + Kind: 2 + TableID: 104 +PreCommitPhase stage 1 of 2 with 1 MutationType op + transitions: + [[Column:{DescID: 104, ColumnID: 2}, PUBLIC], DELETE_ONLY] -> ABSENT + [[ColumnName:{DescID: 104, Name: serial_id, ColumnID: 2}, PUBLIC], PUBLIC] -> ABSENT + [[ColumnType:{DescID: 104, ColumnFamilyID: 0, ColumnID: 2, TypeName: INT8}, PUBLIC], PUBLIC] -> ABSENT + [[ColumnDefaultExpression:{DescID: 104, ColumnID: 2, Expr: unique_rowid()}, PUBLIC], PUBLIC] -> ABSENT + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], BACKFILL_ONLY] -> ABSENT + [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 2}, PUBLIC], PUBLIC] -> ABSENT + [[IndexData:{DescID: 104, IndexID: 2}, PUBLIC], PUBLIC] -> ABSENT + [[TemporaryIndex:{DescID: 104, IndexID: 3, ConstraintID: 3, SourceIndexID: 1}, TRANSIENT_ABSENT], DELETE_ONLY] -> ABSENT + [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 3}, TRANSIENT_ABSENT], PUBLIC] -> ABSENT + [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 2}, PUBLIC], PUBLIC] -> ABSENT + [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 3}, TRANSIENT_ABSENT], PUBLIC] -> ABSENT + ops: + *scop.UndoAllInTxnImmediateMutationOpSideEffects + {} +PreCommitPhase stage 2 of 2 with 14 MutationType ops + transitions: + [[Column:{DescID: 104, ColumnID: 2}, PUBLIC], ABSENT] -> DELETE_ONLY + [[ColumnName:{DescID: 104, Name: serial_id, ColumnID: 2}, PUBLIC], ABSENT] -> PUBLIC + [[ColumnType:{DescID: 104, ColumnFamilyID: 0, ColumnID: 2, TypeName: INT8}, PUBLIC], ABSENT] -> PUBLIC + [[ColumnDefaultExpression:{DescID: 104, ColumnID: 2, Expr: unique_rowid()}, PUBLIC], ABSENT] -> PUBLIC + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], ABSENT] -> BACKFILL_ONLY + [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 2}, PUBLIC], ABSENT] -> PUBLIC + [[IndexData:{DescID: 104, IndexID: 2}, PUBLIC], ABSENT] -> PUBLIC + [[TemporaryIndex:{DescID: 104, IndexID: 3, ConstraintID: 3, SourceIndexID: 1}, TRANSIENT_ABSENT], ABSENT] -> DELETE_ONLY + [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 3}, TRANSIENT_ABSENT], ABSENT] -> PUBLIC + [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 2}, PUBLIC], ABSENT] -> PUBLIC + [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 3}, TRANSIENT_ABSENT], ABSENT] -> PUBLIC + ops: + *scop.MakeAbsentColumnDeleteOnly + Column: + ColumnID: 2 + TableID: 104 + *scop.SetColumnName + ColumnID: 2 + Name: serial_id + TableID: 104 + *scop.UpsertColumnType + ColumnType: + ColumnID: 2 + ElementCreationMetadata: + in231OrLater: true + in243OrLater: true + TableID: 104 + TypeT: + Type: + family: IntFamily + oid: 20 + width: 64 + TypeName: INT8 + *scop.AddColumnDefaultExpression + Default: + ColumnID: 2 + Expression: + Expr: unique_rowid() + TableID: 104 + *scop.MakeAbsentIndexBackfilling + Index: + ConstraintID: 2 + IndexID: 2 + IsUnique: true + SourceIndexID: 1 + TableID: 104 + TemporaryIndexID: 3 + *scop.MaybeAddSplitForIndex + IndexID: 2 + TableID: 104 + *scop.AddColumnToIndex + ColumnID: 1 + IndexID: 2 + TableID: 104 + *scop.MakeAbsentTempIndexDeleteOnly + Index: + ConstraintID: 3 + IndexID: 3 + IsUnique: true + SourceIndexID: 1 + TableID: 104 + *scop.MaybeAddSplitForIndex + IndexID: 3 + TableID: 104 + *scop.AddColumnToIndex + ColumnID: 1 + IndexID: 3 + TableID: 104 + *scop.AddColumnToIndex + ColumnID: 2 + IndexID: 2 + Kind: 2 + TableID: 104 + *scop.AddColumnToIndex + ColumnID: 2 + IndexID: 3 + Kind: 2 + TableID: 104 + *scop.SetJobStateOnDescriptor + DescriptorID: 104 + Initialize: true + *scop.CreateSchemaChangerJob + Authorization: + AppName: $ internal-test + UserName: root + DescriptorIDs: + - 104 + JobID: 1 + RunningStatus: PostCommitPhase stage 1 of 7 with 3 MutationType ops pending + Statements: + - statement: ALTER TABLE defaultdb.foo ADD COLUMN serial_id SERIAL8 + redactedstatement: ALTER TABLE ‹defaultdb›.public.‹foo› ADD COLUMN ‹serial_id› INT8 + statementtag: ALTER TABLE +PostCommitPhase stage 1 of 7 with 5 MutationType ops + transitions: + [[Column:{DescID: 104, ColumnID: 2}, PUBLIC], DELETE_ONLY] -> WRITE_ONLY + [[TemporaryIndex:{DescID: 104, IndexID: 3, ConstraintID: 3, SourceIndexID: 1}, TRANSIENT_ABSENT], DELETE_ONLY] -> WRITE_ONLY + [[IndexData:{DescID: 104, IndexID: 3}, TRANSIENT_ABSENT], ABSENT] -> PUBLIC + [[ColumnNotNull:{DescID: 104, ColumnID: 2, IndexID: 2}, PUBLIC], ABSENT] -> WRITE_ONLY + ops: + *scop.MakeDeleteOnlyColumnWriteOnly + ColumnID: 2 + TableID: 104 + *scop.MakeDeleteOnlyIndexWriteOnly + IndexID: 3 + TableID: 104 + *scop.MakeAbsentColumnNotNullWriteOnly + ColumnID: 2 + TableID: 104 + *scop.SetJobStateOnDescriptor + DescriptorID: 104 + *scop.UpdateSchemaChangerJob + JobID: 1 +PostCommitPhase stage 2 of 7 with 1 BackfillType op + transitions: + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], BACKFILL_ONLY] -> BACKFILLED + ops: + *scop.BackfillIndex + IndexID: 2 + SourceIndexID: 1 + TableID: 104 +PostCommitPhase stage 3 of 7 with 3 MutationType ops + transitions: + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], BACKFILLED] -> DELETE_ONLY + ops: + *scop.MakeBackfillingIndexDeleteOnly + IndexID: 2 + TableID: 104 + *scop.SetJobStateOnDescriptor + DescriptorID: 104 + *scop.UpdateSchemaChangerJob + JobID: 1 +PostCommitPhase stage 4 of 7 with 3 MutationType ops + transitions: + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], DELETE_ONLY] -> MERGE_ONLY + ops: + *scop.MakeBackfilledIndexMerging + IndexID: 2 + TableID: 104 + *scop.SetJobStateOnDescriptor + DescriptorID: 104 + *scop.UpdateSchemaChangerJob + JobID: 1 +PostCommitPhase stage 5 of 7 with 1 BackfillType op + transitions: + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], MERGE_ONLY] -> MERGED + ops: + *scop.MergeIndex + BackfilledIndexID: 2 + TableID: 104 + TemporaryIndexID: 3 +PostCommitPhase stage 6 of 7 with 4 MutationType ops + transitions: + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], MERGED] -> WRITE_ONLY + [[TemporaryIndex:{DescID: 104, IndexID: 3, ConstraintID: 3, SourceIndexID: 1}, TRANSIENT_ABSENT], WRITE_ONLY] -> TRANSIENT_DELETE_ONLY + ops: + *scop.MakeWriteOnlyIndexDeleteOnly + IndexID: 3 + TableID: 104 + *scop.MakeMergedIndexWriteOnly + IndexID: 2 + TableID: 104 + *scop.SetJobStateOnDescriptor + DescriptorID: 104 + *scop.UpdateSchemaChangerJob + JobID: 1 +PostCommitPhase stage 7 of 7 with 2 ValidationType ops + transitions: + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], WRITE_ONLY] -> VALIDATED + [[ColumnNotNull:{DescID: 104, ColumnID: 2, IndexID: 2}, PUBLIC], WRITE_ONLY] -> VALIDATED + ops: + *scop.ValidateIndex + IndexID: 2 + TableID: 104 + *scop.ValidateColumnNotNull + ColumnID: 2 + IndexIDForValidation: 2 + TableID: 104 +PostCommitNonRevertiblePhase stage 1 of 3 with 12 MutationType ops + transitions: + [[PrimaryIndex:{DescID: 104, IndexID: 1, ConstraintID: 1}, ABSENT], PUBLIC] -> VALIDATED + [[IndexName:{DescID: 104, Name: foo_pkey, IndexID: 1}, ABSENT], PUBLIC] -> ABSENT + [[Column:{DescID: 104, ColumnID: 2}, PUBLIC], WRITE_ONLY] -> PUBLIC + [[PrimaryIndex:{DescID: 104, IndexID: 2, ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1}, PUBLIC], VALIDATED] -> PUBLIC + [[IndexName:{DescID: 104, Name: foo_pkey, IndexID: 2}, PUBLIC], ABSENT] -> PUBLIC + [[TemporaryIndex:{DescID: 104, IndexID: 3, ConstraintID: 3, SourceIndexID: 1}, TRANSIENT_ABSENT], TRANSIENT_DELETE_ONLY] -> TRANSIENT_ABSENT + [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 3}, TRANSIENT_ABSENT], PUBLIC] -> TRANSIENT_ABSENT + [[IndexColumn:{DescID: 104, ColumnID: 2, IndexID: 3}, TRANSIENT_ABSENT], PUBLIC] -> TRANSIENT_ABSENT + [[ColumnNotNull:{DescID: 104, ColumnID: 2, IndexID: 2}, PUBLIC], VALIDATED] -> PUBLIC + ops: + *scop.MakePublicPrimaryIndexWriteOnly + IndexID: 1 + TableID: 104 + *scop.SetIndexName + IndexID: 1 + Name: crdb_internal_index_1_name_placeholder + TableID: 104 + *scop.SetIndexName + IndexID: 2 + Name: foo_pkey + TableID: 104 + *scop.RemoveColumnFromIndex + ColumnID: 1 + IndexID: 3 + TableID: 104 + *scop.RemoveColumnFromIndex + ColumnID: 2 + IndexID: 3 + Kind: 2 + TableID: 104 + *scop.MakeValidatedColumnNotNullPublic + ColumnID: 2 + TableID: 104 + *scop.MakeValidatedPrimaryIndexPublic + IndexID: 2 + TableID: 104 + *scop.MakeIndexAbsent + IndexID: 3 + TableID: 104 + *scop.MakeWriteOnlyColumnPublic + ColumnID: 2 + TableID: 104 + *scop.RefreshStats + TableID: 104 + *scop.SetJobStateOnDescriptor + DescriptorID: 104 + *scop.UpdateSchemaChangerJob + IsNonCancelable: true + JobID: 1 +PostCommitNonRevertiblePhase stage 2 of 3 with 4 MutationType ops + transitions: + [[IndexColumn:{DescID: 104, ColumnID: 1, IndexID: 1}, ABSENT], PUBLIC] -> ABSENT + [[PrimaryIndex:{DescID: 104, IndexID: 1, ConstraintID: 1}, ABSENT], VALIDATED] -> DELETE_ONLY + ops: + *scop.MakeWriteOnlyIndexDeleteOnly + IndexID: 1 + TableID: 104 + *scop.RemoveColumnFromIndex + ColumnID: 1 + IndexID: 1 + TableID: 104 + *scop.SetJobStateOnDescriptor + DescriptorID: 104 + *scop.UpdateSchemaChangerJob + IsNonCancelable: true + JobID: 1 +PostCommitNonRevertiblePhase stage 3 of 3 with 5 MutationType ops + transitions: + [[PrimaryIndex:{DescID: 104, IndexID: 1, ConstraintID: 1}, ABSENT], DELETE_ONLY] -> ABSENT + [[IndexData:{DescID: 104, IndexID: 1}, ABSENT], PUBLIC] -> ABSENT + [[IndexData:{DescID: 104, IndexID: 3}, TRANSIENT_ABSENT], PUBLIC] -> TRANSIENT_ABSENT + ops: + *scop.MakeIndexAbsent + IndexID: 1 + TableID: 104 + *scop.CreateGCJobForIndex + IndexID: 1 + StatementForDropJob: + Statement: ALTER TABLE defaultdb.public.foo ADD COLUMN serial_id INT8 + TableID: 104 + *scop.CreateGCJobForIndex + IndexID: 3 + StatementForDropJob: + Statement: ALTER TABLE defaultdb.public.foo ADD COLUMN serial_id INT8 + TableID: 104 + *scop.RemoveJobStateFromDescriptor + DescriptorID: 104 + JobID: 1 + *scop.UpdateSchemaChangerJob + DescriptorIDsToRemove: + - 104 + IsNonCancelable: true + JobID: 1 setup CREATE TABLE defaultdb.bar (j INT); diff --git a/pkg/sql/schemachanger/sctest_generated_test.go b/pkg/sql/schemachanger/sctest_generated_test.go index 6ac77ba1a821..26cca79fa4df 100644 --- a/pkg/sql/schemachanger/sctest_generated_test.go +++ b/pkg/sql/schemachanger/sctest_generated_test.go @@ -55,6 +55,13 @@ func TestEndToEndSideEffects_add_column_no_default(t *testing.T) { sctest.EndToEndSideEffects(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestEndToEndSideEffects_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.EndToEndSideEffects(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestEndToEndSideEffects_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -559,6 +566,13 @@ func TestExecuteWithDMLInjection_add_column_no_default(t *testing.T) { sctest.ExecuteWithDMLInjection(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestExecuteWithDMLInjection_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.ExecuteWithDMLInjection(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestExecuteWithDMLInjection_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1063,6 +1077,13 @@ func TestGenerateSchemaChangeCorpus_add_column_no_default(t *testing.T) { sctest.GenerateSchemaChangeCorpus(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestGenerateSchemaChangeCorpus_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.GenerateSchemaChangeCorpus(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestGenerateSchemaChangeCorpus_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1567,6 +1588,13 @@ func TestPause_add_column_no_default(t *testing.T) { sctest.Pause(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestPause_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.Pause(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestPause_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -2071,6 +2099,13 @@ func TestPauseMixedVersion_add_column_no_default(t *testing.T) { sctest.PauseMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestPauseMixedVersion_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.PauseMixedVersion(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestPauseMixedVersion_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -2575,6 +2610,13 @@ func TestRollback_add_column_no_default(t *testing.T) { sctest.Rollback(t, path, sctest.SingleNodeTestClusterFactory{}) } +func TestRollback_add_column_serial(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + const path = "pkg/sql/schemachanger/testdata/end_to_end/add_column_serial" + sctest.Rollback(t, path, sctest.SingleNodeTestClusterFactory{}) +} + func TestRollback_add_column_virtual_not_null(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.definition b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.definition new file mode 100644 index 000000000000..a72237f7ddb4 --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.definition @@ -0,0 +1,33 @@ +setup +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); +---- + +stage-exec phase=PostCommitPhase stage=: +INSERT INTO db.public.tbl VALUES($stageKey); +INSERT INTO db.public.tbl VALUES($stageKey + 1); +---- + +# Each insert will be injected twice per stage, so we should always, +# see a count of 2. +stage-query phase=PostCommitPhase stage=: +SELECT count(*)=$successfulStageCount*2 FROM db.public.tbl; +---- +true + + +stage-exec phase=PostCommitNonRevertiblePhase stage=: +INSERT INTO db.public.tbl VALUES($stageKey); +INSERT INTO db.public.tbl VALUES($stageKey + 1); +---- + +# Each insert will be injected twice per stage, so we should always, +# see a count of 2. +stage-query phase=PostCommitNonRevertiblePhase stage=: +SELECT count(*)=$successfulStageCount*2 FROM db.public.tbl; +---- +true + +test +ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL +---- diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.explain b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.explain new file mode 100644 index 000000000000..7a0a68f11976 --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.explain @@ -0,0 +1,183 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); + +/* test */ +EXPLAIN (DDL) ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +---- +Schema change plan for ALTER TABLE ‹db›.‹public›.‹tbl› ADD COLUMN ‹serial_id› INT8; + ├── StatementPhase + │ └── Stage 1 of 1 in StatementPhase + │ ├── 8 elements transitioning toward PUBLIC + │ │ ├── ABSENT → DELETE_ONLY Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id+)} + │ │ ├── ABSENT → PUBLIC ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id+)} + │ │ ├── ABSENT → PUBLIC ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id+), TypeName: "INT8"} + │ │ ├── ABSENT → PUBLIC ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), Expr: unique_rowid()} + │ │ ├── ABSENT → BACKFILL_ONLY PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ ├── ABSENT → PUBLIC IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey+)} + │ │ ├── ABSENT → PUBLIC IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+)} + │ │ └── ABSENT → PUBLIC IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 2 (tbl_pkey+)} + │ ├── 3 elements transitioning toward TRANSIENT_ABSENT + │ │ ├── ABSENT → DELETE_ONLY TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ ├── ABSENT → PUBLIC IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ └── ABSENT → PUBLIC IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 3} + │ └── 10 Mutation operations + │ ├── MakeAbsentColumnDeleteOnly {"Column":{"ColumnID":2,"TableID":106}} + │ ├── SetColumnName {"ColumnID":2,"Name":"serial_id","TableID":106} + │ ├── UpsertColumnType {"ColumnType":{"ColumnID":2,"TableID":106}} + │ ├── AddColumnDefaultExpression {"Default":{"ColumnID":2,"TableID":106}} + │ ├── MakeAbsentIndexBackfilling {"Index":{"ConstraintID":2,"IndexID":2,"IsUnique":true,"SourceIndexID":1,"TableID":106,"TemporaryIndexID":3}} + │ ├── AddColumnToIndex {"ColumnID":1,"IndexID":2,"TableID":106} + │ ├── MakeAbsentTempIndexDeleteOnly {"Index":{"ConstraintID":3,"IndexID":3,"IsUnique":true,"SourceIndexID":1,"TableID":106}} + │ ├── AddColumnToIndex {"ColumnID":1,"IndexID":3,"TableID":106} + │ ├── AddColumnToIndex {"ColumnID":2,"IndexID":2,"Kind":2,"TableID":106} + │ └── AddColumnToIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + ├── PreCommitPhase + │ ├── Stage 1 of 2 in PreCommitPhase + │ │ ├── 8 elements transitioning toward PUBLIC + │ │ │ ├── DELETE_ONLY → ABSENT Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id+)} + │ │ │ ├── PUBLIC → ABSENT ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id+)} + │ │ │ ├── PUBLIC → ABSENT ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id+), TypeName: "INT8"} + │ │ │ ├── PUBLIC → ABSENT ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), Expr: unique_rowid()} + │ │ │ ├── BACKFILL_ONLY → ABSENT PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey+)} + │ │ │ ├── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+)} + │ │ │ └── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 2 (tbl_pkey+)} + │ │ ├── 3 elements transitioning toward TRANSIENT_ABSENT + │ │ │ ├── DELETE_ONLY → ABSENT TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ │ └── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 3} + │ │ └── 1 Mutation operation + │ │ └── UndoAllInTxnImmediateMutationOpSideEffects + │ └── Stage 2 of 2 in PreCommitPhase + │ ├── 8 elements transitioning toward PUBLIC + │ │ ├── ABSENT → DELETE_ONLY Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id+)} + │ │ ├── ABSENT → PUBLIC ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id+)} + │ │ ├── ABSENT → PUBLIC ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id+), TypeName: "INT8"} + │ │ ├── ABSENT → PUBLIC ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), Expr: unique_rowid()} + │ │ ├── ABSENT → BACKFILL_ONLY PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ ├── ABSENT → PUBLIC IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey+)} + │ │ ├── ABSENT → PUBLIC IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+)} + │ │ └── ABSENT → PUBLIC IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 2 (tbl_pkey+)} + │ ├── 3 elements transitioning toward TRANSIENT_ABSENT + │ │ ├── ABSENT → DELETE_ONLY TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ ├── ABSENT → PUBLIC IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ └── ABSENT → PUBLIC IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 3} + │ └── 14 Mutation operations + │ ├── MakeAbsentColumnDeleteOnly {"Column":{"ColumnID":2,"TableID":106}} + │ ├── SetColumnName {"ColumnID":2,"Name":"serial_id","TableID":106} + │ ├── UpsertColumnType {"ColumnType":{"ColumnID":2,"TableID":106}} + │ ├── AddColumnDefaultExpression {"Default":{"ColumnID":2,"TableID":106}} + │ ├── MakeAbsentIndexBackfilling {"Index":{"ConstraintID":2,"IndexID":2,"IsUnique":true,"SourceIndexID":1,"TableID":106,"TemporaryIndexID":3}} + │ ├── MaybeAddSplitForIndex {"IndexID":2,"TableID":106} + │ ├── AddColumnToIndex {"ColumnID":1,"IndexID":2,"TableID":106} + │ ├── MakeAbsentTempIndexDeleteOnly {"Index":{"ConstraintID":3,"IndexID":3,"IsUnique":true,"SourceIndexID":1,"TableID":106}} + │ ├── MaybeAddSplitForIndex {"IndexID":3,"TableID":106} + │ ├── AddColumnToIndex {"ColumnID":1,"IndexID":3,"TableID":106} + │ ├── AddColumnToIndex {"ColumnID":2,"IndexID":2,"Kind":2,"TableID":106} + │ ├── AddColumnToIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + │ ├── SetJobStateOnDescriptor {"DescriptorID":106,"Initialize":true} + │ └── CreateSchemaChangerJob {"RunningStatus":"PostCommitPhase ..."} + ├── PostCommitPhase + │ ├── Stage 1 of 7 in PostCommitPhase + │ │ ├── 2 elements transitioning toward PUBLIC + │ │ │ ├── DELETE_ONLY → WRITE_ONLY Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id+)} + │ │ │ └── ABSENT → WRITE_ONLY ColumnNotNull:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 2 (tbl_pkey+)} + │ │ ├── 2 elements transitioning toward TRANSIENT_ABSENT + │ │ │ ├── DELETE_ONLY → WRITE_ONLY TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ │ └── ABSENT → PUBLIC IndexData:{DescID: 106 (tbl), IndexID: 3} + │ │ └── 5 Mutation operations + │ │ ├── MakeDeleteOnlyColumnWriteOnly {"ColumnID":2,"TableID":106} + │ │ ├── MakeDeleteOnlyIndexWriteOnly {"IndexID":3,"TableID":106} + │ │ ├── MakeAbsentColumnNotNullWriteOnly {"ColumnID":2,"TableID":106} + │ │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ │ └── UpdateSchemaChangerJob {"RunningStatus":"PostCommitPhase ..."} + │ ├── Stage 2 of 7 in PostCommitPhase + │ │ ├── 1 element transitioning toward PUBLIC + │ │ │ └── BACKFILL_ONLY → BACKFILLED PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ └── 1 Backfill operation + │ │ └── BackfillIndex {"IndexID":2,"SourceIndexID":1,"TableID":106} + │ ├── Stage 3 of 7 in PostCommitPhase + │ │ ├── 1 element transitioning toward PUBLIC + │ │ │ └── BACKFILLED → DELETE_ONLY PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ └── 3 Mutation operations + │ │ ├── MakeBackfillingIndexDeleteOnly {"IndexID":2,"TableID":106} + │ │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ │ └── UpdateSchemaChangerJob {"RunningStatus":"PostCommitPhase ..."} + │ ├── Stage 4 of 7 in PostCommitPhase + │ │ ├── 1 element transitioning toward PUBLIC + │ │ │ └── DELETE_ONLY → MERGE_ONLY PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ └── 3 Mutation operations + │ │ ├── MakeBackfilledIndexMerging {"IndexID":2,"TableID":106} + │ │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ │ └── UpdateSchemaChangerJob {"RunningStatus":"PostCommitPhase ..."} + │ ├── Stage 5 of 7 in PostCommitPhase + │ │ ├── 1 element transitioning toward PUBLIC + │ │ │ └── MERGE_ONLY → MERGED PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ └── 1 Backfill operation + │ │ └── MergeIndex {"BackfilledIndexID":2,"TableID":106,"TemporaryIndexID":3} + │ ├── Stage 6 of 7 in PostCommitPhase + │ │ ├── 1 element transitioning toward PUBLIC + │ │ │ └── MERGED → WRITE_ONLY PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ ├── 1 element transitioning toward TRANSIENT_ABSENT + │ │ │ └── WRITE_ONLY → TRANSIENT_DELETE_ONLY TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ └── 4 Mutation operations + │ │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":3,"TableID":106} + │ │ ├── MakeMergedIndexWriteOnly {"IndexID":2,"TableID":106} + │ │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ │ └── UpdateSchemaChangerJob {"RunningStatus":"PostCommitPhase ..."} + │ └── Stage 7 of 7 in PostCommitPhase + │ ├── 2 elements transitioning toward PUBLIC + │ │ ├── WRITE_ONLY → VALIDATED PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ └── WRITE_ONLY → VALIDATED ColumnNotNull:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 2 (tbl_pkey+)} + │ └── 2 Validation operations + │ ├── ValidateIndex {"IndexID":2,"TableID":106} + │ └── ValidateColumnNotNull {"ColumnID":2,"IndexIDForValidation":2,"TableID":106} + └── PostCommitNonRevertiblePhase + ├── Stage 1 of 3 in PostCommitNonRevertiblePhase + │ ├── 4 elements transitioning toward PUBLIC + │ │ ├── WRITE_ONLY → PUBLIC Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id+)} + │ │ ├── VALIDATED → PUBLIC PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey+), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ ├── ABSENT → PUBLIC IndexName:{DescID: 106 (tbl), Name: "tbl_pkey", IndexID: 2 (tbl_pkey+)} + │ │ └── VALIDATED → PUBLIC ColumnNotNull:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 2 (tbl_pkey+)} + │ ├── 3 elements transitioning toward TRANSIENT_ABSENT + │ │ ├── TRANSIENT_DELETE_ONLY → TRANSIENT_ABSENT TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey-)} + │ │ ├── PUBLIC → TRANSIENT_ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ └── PUBLIC → TRANSIENT_ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id+), IndexID: 3} + │ ├── 2 elements transitioning toward ABSENT + │ │ ├── PUBLIC → VALIDATED PrimaryIndex:{DescID: 106 (tbl), IndexID: 1 (tbl_pkey-), ConstraintID: 1} + │ │ └── PUBLIC → ABSENT IndexName:{DescID: 106 (tbl), Name: "tbl_pkey", IndexID: 1 (tbl_pkey-)} + │ └── 12 Mutation operations + │ ├── MakePublicPrimaryIndexWriteOnly {"IndexID":1,"TableID":106} + │ ├── SetIndexName {"IndexID":1,"Name":"crdb_internal_in...","TableID":106} + │ ├── SetIndexName {"IndexID":2,"Name":"tbl_pkey","TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + │ ├── MakeValidatedColumnNotNullPublic {"ColumnID":2,"TableID":106} + │ ├── MakeValidatedPrimaryIndexPublic {"IndexID":2,"TableID":106} + │ ├── MakeIndexAbsent {"IndexID":3,"TableID":106} + │ ├── MakeWriteOnlyColumnPublic {"ColumnID":2,"TableID":106} + │ ├── RefreshStats {"TableID":106} + │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"PostCommitNonRev..."} + ├── Stage 2 of 3 in PostCommitNonRevertiblePhase + │ ├── 2 elements transitioning toward ABSENT + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 1 (tbl_pkey-)} + │ │ └── VALIDATED → DELETE_ONLY PrimaryIndex:{DescID: 106 (tbl), IndexID: 1 (tbl_pkey-), ConstraintID: 1} + │ └── 4 Mutation operations + │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":1,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":1,"TableID":106} + │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"PostCommitNonRev..."} + └── Stage 3 of 3 in PostCommitNonRevertiblePhase + ├── 1 element transitioning toward TRANSIENT_ABSENT + │ └── PUBLIC → TRANSIENT_ABSENT IndexData:{DescID: 106 (tbl), IndexID: 3} + ├── 2 elements transitioning toward ABSENT + │ ├── DELETE_ONLY → ABSENT PrimaryIndex:{DescID: 106 (tbl), IndexID: 1 (tbl_pkey-), ConstraintID: 1} + │ └── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 1 (tbl_pkey-)} + └── 5 Mutation operations + ├── MakeIndexAbsent {"IndexID":1,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":1,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":3,"TableID":106} + ├── RemoveJobStateFromDescriptor {"DescriptorID":106} + └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"all stages compl..."} diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.explain_shape b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.explain_shape new file mode 100644 index 000000000000..9253f0462df0 --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.explain_shape @@ -0,0 +1,18 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); + +/* test */ +EXPLAIN (DDL, SHAPE) ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +---- +Schema change plan for ALTER TABLE ‹db›.‹public›.‹tbl› ADD COLUMN ‹serial_id› INT8; + ├── execute 2 system table mutations transactions + ├── backfill using primary index tbl_pkey- in relation tbl + │ └── into tbl_pkey+ (i; serial_id+) + ├── execute 2 system table mutations transactions + ├── merge temporary indexes into backfilled indexes in relation tbl + │ └── from tbl@[3] into tbl_pkey+ + ├── execute 1 system table mutations transaction + ├── validate UNIQUE constraint backed by index tbl_pkey+ in relation tbl + ├── validate NOT NULL constraint on column serial_id+ in index tbl_pkey+ in relation tbl + └── execute 3 system table mutations transactions diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.side_effects b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.side_effects new file mode 100644 index 000000000000..9faac8131640 --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial.side_effects @@ -0,0 +1,634 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); +---- +... ++database {0 0 db} -> 104 ++schema {104 0 public} -> 105 ++object {104 105 tbl} -> 106 + +/* test */ +ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +---- +begin transaction #1 +# begin StatementPhase +checking for feature: ALTER TABLE +increment telemetry for sql.schema.alter_table +increment telemetry for sql.schema.alter_table.add_column +increment telemetry for sql.schema.qualifcation.default_expr +increment telemetry for sql.schema.new_column_type.int8 +write *eventpb.AlterTable to event log: + mutationId: 1 + sql: + descriptorId: 106 + statement: ALTER TABLE ‹db›.‹public›.‹tbl› ADD COLUMN ‹serial_id› INT8 + tag: ALTER TABLE + user: root + tableName: db.public.tbl +## StatementPhase stage 1 of 1 with 10 MutationType ops +upsert descriptor #106 + ... + - columnIds: + - 1 + + - 2 + columnNames: + - i + + - serial_id + + defaultColumnId: 2 + name: primary + formatVersion: 3 + id: 106 + modificationTime: {} + + mutations: + + - column: + + defaultExpr: unique_rowid() + + id: 2 + + name: serial_id + + nullable: true + + type: + + family: IntFamily + + oid: 20 + + width: 64 + + direction: ADD + + mutationId: 1 + + state: DELETE_ONLY + + - direction: ADD + + index: + + constraintId: 2 + + createdExplicitly: true + + encodingType: 1 + + foreignKey: {} + + geoConfig: {} + + id: 2 + + interleave: {} + + keyColumnDirections: + + - ASC + + keyColumnIds: + + - 1 + + keyColumnNames: + + - i + + name: crdb_internal_index_2_name_placeholder + + partitioning: {} + + sharded: {} + + storeColumnIds: + + - 2 + + storeColumnNames: + + - serial_id + + unique: true + + version: 4 + + mutationId: 1 + + state: BACKFILLING + + - direction: ADD + + index: + + constraintId: 3 + + createdExplicitly: true + + encodingType: 1 + + foreignKey: {} + + geoConfig: {} + + id: 3 + + interleave: {} + + keyColumnDirections: + + - ASC + + keyColumnIds: + + - 1 + + keyColumnNames: + + - i + + name: crdb_internal_index_3_name_placeholder + + partitioning: {} + + sharded: {} + + storeColumnIds: + + - 2 + + storeColumnNames: + + - serial_id + + unique: true + + useDeletePreservingEncoding: true + + version: 4 + + mutationId: 1 + + state: DELETE_ONLY + name: tbl + - nextColumnId: 2 + - nextConstraintId: 2 + + nextColumnId: 3 + + nextConstraintId: 4 + nextFamilyId: 1 + - nextIndexId: 2 + + nextIndexId: 4 + nextMutationId: 1 + parentId: 104 + ... + time: {} + unexposedParentSchemaId: 105 + - 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 14 MutationType ops +upsert descriptor #106 + ... + createAsOfTime: + wallTime: "1640995200000000000" + + declarativeSchemaChangerState: + + authorization: + + userName: root + + currentStatuses: + + jobId: "1" + + nameMapping: + + columns: + + "1": i + + "2": serial_id + + "4294967294": tableoid + + "4294967295": crdb_internal_mvcc_timestamp + + families: + + "0": primary + + id: 106 + + indexes: + + "2": tbl_pkey + + name: tbl + + relevantStatements: + + - statement: + + redactedStatement: ALTER TABLE ‹db›.‹public›.‹tbl› ADD COLUMN ‹serial_id› INT8 + + statement: ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL8 + + statementTag: ALTER TABLE + + revertible: true + + targetRanks: + + targets: + families: + - columnIds: + - 1 + + - 2 + columnNames: + - i + + - serial_id + + defaultColumnId: 2 + name: primary + formatVersion: 3 + id: 106 + modificationTime: {} + + mutations: + + - column: + + defaultExpr: unique_rowid() + + id: 2 + + name: serial_id + + nullable: true + + type: + + family: IntFamily + + oid: 20 + + width: 64 + + direction: ADD + + mutationId: 1 + + state: DELETE_ONLY + + - direction: ADD + + index: + + constraintId: 2 + + createdExplicitly: true + + encodingType: 1 + + foreignKey: {} + + geoConfig: {} + + id: 2 + + interleave: {} + + keyColumnDirections: + + - ASC + + keyColumnIds: + + - 1 + + keyColumnNames: + + - i + + name: crdb_internal_index_2_name_placeholder + + partitioning: {} + + sharded: {} + + storeColumnIds: + + - 2 + + storeColumnNames: + + - serial_id + + unique: true + + version: 4 + + mutationId: 1 + + state: BACKFILLING + + - direction: ADD + + index: + + constraintId: 3 + + createdExplicitly: true + + encodingType: 1 + + foreignKey: {} + + geoConfig: {} + + id: 3 + + interleave: {} + + keyColumnDirections: + + - ASC + + keyColumnIds: + + - 1 + + keyColumnNames: + + - i + + name: crdb_internal_index_3_name_placeholder + + partitioning: {} + + sharded: {} + + storeColumnIds: + + - 2 + + storeColumnNames: + + - serial_id + + unique: true + + useDeletePreservingEncoding: true + + version: 4 + + mutationId: 1 + + state: DELETE_ONLY + name: tbl + - nextColumnId: 2 + - nextConstraintId: 2 + + nextColumnId: 3 + + nextConstraintId: 4 + nextFamilyId: 1 + - nextIndexId: 2 + + nextIndexId: 4 + nextMutationId: 1 + parentId: 104 + ... + time: {} + unexposedParentSchemaId: 105 + - version: "1" + + version: "2" +persist all catalog changes to storage +create job #1 (non-cancelable: false): "ALTER TABLE db.public.tbl ADD COLUMN serial_id INT8" + descriptor IDs: [106] +# end PreCommitPhase +commit transaction #1 +notified job registry to adopt jobs: [1] +# begin PostCommitPhase +begin transaction #2 +commit transaction #2 +begin transaction #3 +## PostCommitPhase stage 1 of 7 with 5 MutationType ops +upsert descriptor #106 + table: + + checks: + + - columnIds: + + - 2 + + expr: serial_id IS NOT NULL + + isNonNullConstraint: true + + name: serial_id_auto_not_null + + validity: Validating + columns: + - id: 1 + ... + direction: ADD + mutationId: 1 + - state: DELETE_ONLY + + state: WRITE_ONLY + - direction: ADD + index: + ... + version: 4 + mutationId: 1 + - state: DELETE_ONLY + + state: WRITE_ONLY + + - constraint: + + check: + + columnIds: + + - 2 + + expr: serial_id IS NOT NULL + + isNonNullConstraint: true + + name: serial_id_auto_not_null + + validity: Validating + + constraintType: NOT_NULL + + foreignKey: {} + + name: serial_id_auto_not_null + + notNullColumn: 2 + + uniqueWithoutIndexConstraint: {} + + direction: ADD + + mutationId: 1 + + state: WRITE_ONLY + name: tbl + nextColumnId: 3 + ... + time: {} + unexposedParentSchemaId: 105 + - version: "2" + + version: "3" +persist all catalog changes to storage +update progress of schema change job #1: "PostCommitPhase stage 2 of 7 with 1 BackfillType op pending" +commit transaction #3 +begin transaction #4 +## PostCommitPhase stage 2 of 7 with 1 BackfillType op +backfill indexes [2] from index #1 in table #106 +commit transaction #4 +begin transaction #5 +## PostCommitPhase stage 3 of 7 with 3 MutationType ops +upsert descriptor #106 + ... + version: 4 + mutationId: 1 + - state: BACKFILLING + + state: DELETE_ONLY + - direction: ADD + index: + ... + time: {} + unexposedParentSchemaId: 105 + - version: "3" + + version: "4" +persist all catalog changes to storage +update progress of schema change job #1: "PostCommitPhase stage 4 of 7 with 1 MutationType op pending" +commit transaction #5 +begin transaction #6 +## PostCommitPhase stage 4 of 7 with 3 MutationType ops +upsert descriptor #106 + ... + version: 4 + mutationId: 1 + - state: DELETE_ONLY + + state: MERGING + - direction: ADD + index: + ... + time: {} + unexposedParentSchemaId: 105 + - version: "4" + + version: "5" +persist all catalog changes to storage +update progress of schema change job #1: "PostCommitPhase stage 5 of 7 with 1 BackfillType op pending" +commit transaction #6 +begin transaction #7 +## PostCommitPhase stage 5 of 7 with 1 BackfillType op +merge temporary indexes [3] into backfilled indexes [2] in table #106 +commit transaction #7 +begin transaction #8 +## PostCommitPhase stage 6 of 7 with 4 MutationType ops +upsert descriptor #106 + ... + version: 4 + mutationId: 1 + - state: MERGING + - - direction: ADD + + state: WRITE_ONLY + + - direction: DROP + index: + constraintId: 3 + ... + version: 4 + mutationId: 1 + - state: WRITE_ONLY + + state: DELETE_ONLY + - constraint: + check: + ... + time: {} + unexposedParentSchemaId: 105 + - version: "5" + + version: "6" +persist all catalog changes to storage +update progress of schema change job #1: "PostCommitPhase stage 7 of 7 with 2 ValidationType ops pending" +commit transaction #8 +begin transaction #9 +## PostCommitPhase stage 7 of 7 with 2 ValidationType ops +validate forward indexes [2] in table #106 +validate CHECK constraint serial_id_auto_not_null in table #106 +commit transaction #9 +begin transaction #10 +## PostCommitNonRevertiblePhase stage 1 of 3 with 12 MutationType ops +upsert descriptor #106 + table: + - checks: + - - columnIds: + - - 2 + - expr: serial_id IS NOT NULL + - isNonNullConstraint: true + - name: serial_id_auto_not_null + - validity: Validating + + checks: [] + columns: + - id: 1 + ... + oid: 20 + width: 64 + + - defaultExpr: unique_rowid() + + id: 2 + + name: serial_id + + type: + + family: IntFamily + + oid: 20 + + width: 64 + createAsOfTime: + wallTime: "1640995200000000000" + ... + statement: ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL8 + statementTag: ALTER TABLE + - revertible: true + targetRanks: + targets: + ... + modificationTime: {} + mutations: + - - column: + - defaultExpr: unique_rowid() + - id: 2 + - name: serial_id + - nullable: true + - type: + - family: IntFamily + - oid: 20 + - width: 64 + - direction: ADD + - mutationId: 1 + - state: WRITE_ONLY + - - direction: ADD + - index: + - constraintId: 2 + - createdExplicitly: true + - encodingType: 1 + - foreignKey: {} + - geoConfig: {} + - id: 2 + - interleave: {} + - keyColumnDirections: + - - ASC + - keyColumnIds: + - - 1 + - keyColumnNames: + - - i + - name: crdb_internal_index_2_name_placeholder + - partitioning: {} + - sharded: {} + - storeColumnIds: + - - 2 + - storeColumnNames: + - - serial_id + - unique: true + - version: 4 + - mutationId: 1 + - state: WRITE_ONLY + - direction: DROP + index: + - constraintId: 3 + - createdExplicitly: true + + constraintId: 1 + + createdAtNanos: "1640995200000000000" + encodingType: 1 + foreignKey: {} + geoConfig: {} + - id: 3 + + id: 1 + interleave: {} + keyColumnDirections: + ... + keyColumnNames: + - i + - name: crdb_internal_index_3_name_placeholder + + name: crdb_internal_index_1_name_placeholder + partitioning: {} + sharded: {} + - storeColumnIds: + - - 2 + - storeColumnNames: + - - serial_id + unique: true + - useDeletePreservingEncoding: true + version: 4 + mutationId: 1 + - state: DELETE_ONLY + - - constraint: + - check: + - columnIds: + - - 2 + - expr: serial_id IS NOT NULL + - isNonNullConstraint: true + - name: serial_id_auto_not_null + - validity: Validating + - constraintType: NOT_NULL + - foreignKey: {} + - name: serial_id_auto_not_null + - notNullColumn: 2 + - uniqueWithoutIndexConstraint: {} + - direction: ADD + - mutationId: 1 + state: WRITE_ONLY + name: tbl + ... + parentId: 104 + primaryIndex: + - constraintId: 1 + - createdAtNanos: "1640995200000000000" + + constraintId: 2 + + createdExplicitly: true + encodingType: 1 + foreignKey: {} + geoConfig: {} + - id: 1 + + id: 2 + interleave: {} + keyColumnDirections: + ... + partitioning: {} + sharded: {} + + storeColumnIds: + + - 2 + + storeColumnNames: + + - serial_id + unique: true + version: 4 + ... + time: {} + unexposedParentSchemaId: 105 + - version: "6" + + version: "7" +persist all catalog changes to storage +adding table for stats refresh: 106 +update progress of schema change job #1: "PostCommitNonRevertiblePhase stage 2 of 3 with 2 MutationType ops pending" +set schema change job #1 to non-cancellable +commit transaction #10 +begin transaction #11 +## PostCommitNonRevertiblePhase stage 2 of 3 with 4 MutationType ops +upsert descriptor #106 + ... + version: 4 + mutationId: 1 + - state: WRITE_ONLY + + state: DELETE_ONLY + name: tbl + nextColumnId: 3 + ... + time: {} + unexposedParentSchemaId: 105 + - version: "7" + + version: "8" +persist all catalog changes to storage +update progress of schema change job #1: "PostCommitNonRevertiblePhase stage 3 of 3 with 3 MutationType ops pending" +commit transaction #11 +begin transaction #12 +## PostCommitNonRevertiblePhase stage 3 of 3 with 5 MutationType ops +upsert descriptor #106 + ... + createAsOfTime: + wallTime: "1640995200000000000" + - declarativeSchemaChangerState: + - authorization: + - userName: root + - currentStatuses: + - jobId: "1" + - nameMapping: + - columns: + - "1": i + - "2": serial_id + - "4294967294": tableoid + - "4294967295": crdb_internal_mvcc_timestamp + - families: + - "0": primary + - id: 106 + - indexes: + - "2": tbl_pkey + - name: tbl + - relevantStatements: + - - statement: + - redactedStatement: ALTER TABLE ‹db›.‹public›.‹tbl› ADD COLUMN ‹serial_id› INT8 + - statement: ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL8 + - statementTag: ALTER TABLE + - targetRanks: + - targets: + families: + - columnIds: + ... + id: 106 + modificationTime: {} + - mutations: + - - direction: DROP + - index: + - constraintId: 1 + - createdAtNanos: "1640995200000000000" + - encodingType: 1 + - foreignKey: {} + - geoConfig: {} + - id: 1 + - interleave: {} + - keyColumnDirections: + - - ASC + - keyColumnIds: + - - 1 + - keyColumnNames: + - - i + - name: crdb_internal_index_1_name_placeholder + - partitioning: {} + - sharded: {} + - unique: true + - version: 4 + - mutationId: 1 + - state: DELETE_ONLY + + mutations: [] + name: tbl + nextColumnId: 3 + ... + time: {} + unexposedParentSchemaId: 105 + - version: "8" + + version: "9" +persist all catalog changes to storage +create job #2 (non-cancelable: true): "GC for ALTER TABLE db.public.tbl ADD COLUMN serial_id INT8" + descriptor IDs: [106] +update progress of schema change job #1: "all stages completed" +set schema change job #1 to non-cancellable +updated schema change job #1 descriptor IDs to [] +write *eventpb.FinishSchemaChange to event log: + sc: + descriptorId: 106 +commit transaction #12 +notified job registry to adopt jobs: [2] +# end PostCommitPhase diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_1_of_7.explain b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_1_of_7.explain new file mode 100644 index 000000000000..1110227fae5a --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_1_of_7.explain @@ -0,0 +1,36 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); + +/* test */ +ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +EXPLAIN (DDL) rollback at post-commit stage 1 of 7; +---- +Schema change plan for rolling back ALTER TABLE ‹db›.public.‹tbl› ADD COLUMN ‹serial_id› INT8; + └── PostCommitNonRevertiblePhase + └── Stage 1 of 1 in PostCommitNonRevertiblePhase + ├── 11 elements transitioning toward ABSENT + │ ├── DELETE_ONLY → ABSENT Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ ├── PUBLIC → ABSENT ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id-)} + │ ├── PUBLIC → ABSENT ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id-), TypeName: "INT8"} + │ ├── PUBLIC → ABSENT ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), Expr: unique_rowid()} + │ ├── BACKFILL_ONLY → ABSENT PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey-)} + │ ├── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-)} + │ ├── DELETE_ONLY → ABSENT TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ └── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 3} + └── 12 Mutation operations + ├── SetColumnName {"ColumnID":2,"Name":"crdb_internal_co...","TableID":106} + ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":2,"TableID":106} + ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":3,"TableID":106} + ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":2,"Kind":2,"TableID":106} + ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + ├── RemoveColumnDefaultExpression {"ColumnID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":2,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":3,"TableID":106} + ├── MakeDeleteOnlyColumnAbsent {"ColumnID":2,"TableID":106} + ├── RemoveJobStateFromDescriptor {"DescriptorID":106} + └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"all stages compl..."} diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_2_of_7.explain b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_2_of_7.explain new file mode 100644 index 000000000000..46fcbfbff31c --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_2_of_7.explain @@ -0,0 +1,49 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); + +/* test */ +ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +EXPLAIN (DDL) rollback at post-commit stage 2 of 7; +---- +Schema change plan for rolling back ALTER TABLE ‹db›.public.‹tbl› ADD COLUMN ‹serial_id› INT8; + └── PostCommitNonRevertiblePhase + ├── Stage 1 of 2 in PostCommitNonRevertiblePhase + │ ├── 9 elements transitioning toward ABSENT + │ │ ├── WRITE_ONLY → DELETE_ONLY Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ │ ├── PUBLIC → ABSENT ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id-)} + │ │ ├── BACKFILL_ONLY → ABSENT PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey-)} + │ │ ├── WRITE_ONLY → DELETE_ONLY TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 3} + │ │ └── WRITE_ONLY → ABSENT ColumnNotNull:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ └── 11 Mutation operations + │ ├── MakeWriteOnlyColumnDeleteOnly {"ColumnID":2,"TableID":106} + │ ├── SetColumnName {"ColumnID":2,"Name":"crdb_internal_co...","TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":2,"TableID":106} + │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":2,"Kind":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + │ ├── MakeIndexAbsent {"IndexID":2,"TableID":106} + │ ├── RemoveColumnNotNull {"ColumnID":2,"TableID":106} + │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"PostCommitNonRev..."} + └── Stage 2 of 2 in PostCommitNonRevertiblePhase + ├── 6 elements transitioning toward ABSENT + │ ├── DELETE_ONLY → ABSENT Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ ├── PUBLIC → ABSENT ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id-), TypeName: "INT8"} + │ ├── PUBLIC → ABSENT ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), Expr: unique_rowid()} + │ ├── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-)} + │ ├── DELETE_ONLY → ABSENT TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ └── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 3} + └── 7 Mutation operations + ├── RemoveColumnDefaultExpression {"ColumnID":2,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":3,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":3,"TableID":106} + ├── MakeDeleteOnlyColumnAbsent {"ColumnID":2,"TableID":106} + ├── RemoveJobStateFromDescriptor {"DescriptorID":106} + └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"all stages compl..."} diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_3_of_7.explain b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_3_of_7.explain new file mode 100644 index 000000000000..343fb79a2d6e --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_3_of_7.explain @@ -0,0 +1,49 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); + +/* test */ +ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +EXPLAIN (DDL) rollback at post-commit stage 3 of 7; +---- +Schema change plan for rolling back ALTER TABLE ‹db›.public.‹tbl› ADD COLUMN ‹serial_id› INT8; + └── PostCommitNonRevertiblePhase + ├── Stage 1 of 2 in PostCommitNonRevertiblePhase + │ ├── 9 elements transitioning toward ABSENT + │ │ ├── WRITE_ONLY → DELETE_ONLY Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ │ ├── PUBLIC → ABSENT ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id-)} + │ │ ├── BACKFILL_ONLY → ABSENT PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey-)} + │ │ ├── WRITE_ONLY → DELETE_ONLY TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 3} + │ │ └── WRITE_ONLY → ABSENT ColumnNotNull:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ └── 11 Mutation operations + │ ├── MakeWriteOnlyColumnDeleteOnly {"ColumnID":2,"TableID":106} + │ ├── SetColumnName {"ColumnID":2,"Name":"crdb_internal_co...","TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":2,"TableID":106} + │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":2,"Kind":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + │ ├── MakeIndexAbsent {"IndexID":2,"TableID":106} + │ ├── RemoveColumnNotNull {"ColumnID":2,"TableID":106} + │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"PostCommitNonRev..."} + └── Stage 2 of 2 in PostCommitNonRevertiblePhase + ├── 6 elements transitioning toward ABSENT + │ ├── DELETE_ONLY → ABSENT Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ ├── PUBLIC → ABSENT ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id-), TypeName: "INT8"} + │ ├── PUBLIC → ABSENT ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), Expr: unique_rowid()} + │ ├── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-)} + │ ├── DELETE_ONLY → ABSENT TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ └── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 3} + └── 7 Mutation operations + ├── RemoveColumnDefaultExpression {"ColumnID":2,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":3,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":3,"TableID":106} + ├── MakeDeleteOnlyColumnAbsent {"ColumnID":2,"TableID":106} + ├── RemoveJobStateFromDescriptor {"DescriptorID":106} + └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"all stages compl..."} diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_4_of_7.explain b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_4_of_7.explain new file mode 100644 index 000000000000..ebd1507bad1a --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_4_of_7.explain @@ -0,0 +1,49 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); + +/* test */ +ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +EXPLAIN (DDL) rollback at post-commit stage 4 of 7; +---- +Schema change plan for rolling back ALTER TABLE ‹db›.public.‹tbl› ADD COLUMN ‹serial_id› INT8; + └── PostCommitNonRevertiblePhase + ├── Stage 1 of 2 in PostCommitNonRevertiblePhase + │ ├── 9 elements transitioning toward ABSENT + │ │ ├── WRITE_ONLY → DELETE_ONLY Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ │ ├── PUBLIC → ABSENT ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id-)} + │ │ ├── DELETE_ONLY → ABSENT PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey-)} + │ │ ├── WRITE_ONLY → DELETE_ONLY TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 3} + │ │ └── WRITE_ONLY → ABSENT ColumnNotNull:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ └── 11 Mutation operations + │ ├── MakeWriteOnlyColumnDeleteOnly {"ColumnID":2,"TableID":106} + │ ├── SetColumnName {"ColumnID":2,"Name":"crdb_internal_co...","TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":2,"TableID":106} + │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":2,"Kind":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + │ ├── MakeIndexAbsent {"IndexID":2,"TableID":106} + │ ├── RemoveColumnNotNull {"ColumnID":2,"TableID":106} + │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"PostCommitNonRev..."} + └── Stage 2 of 2 in PostCommitNonRevertiblePhase + ├── 6 elements transitioning toward ABSENT + │ ├── DELETE_ONLY → ABSENT Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ ├── PUBLIC → ABSENT ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id-), TypeName: "INT8"} + │ ├── PUBLIC → ABSENT ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), Expr: unique_rowid()} + │ ├── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-)} + │ ├── DELETE_ONLY → ABSENT TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ └── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 3} + └── 7 Mutation operations + ├── RemoveColumnDefaultExpression {"ColumnID":2,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":3,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":3,"TableID":106} + ├── MakeDeleteOnlyColumnAbsent {"ColumnID":2,"TableID":106} + ├── RemoveJobStateFromDescriptor {"DescriptorID":106} + └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"all stages compl..."} diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_5_of_7.explain b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_5_of_7.explain new file mode 100644 index 000000000000..c18d7bb02917 --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_5_of_7.explain @@ -0,0 +1,51 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); + +/* test */ +ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +EXPLAIN (DDL) rollback at post-commit stage 5 of 7; +---- +Schema change plan for rolling back ALTER TABLE ‹db›.public.‹tbl› ADD COLUMN ‹serial_id› INT8; + └── PostCommitNonRevertiblePhase + ├── Stage 1 of 2 in PostCommitNonRevertiblePhase + │ ├── 9 elements transitioning toward ABSENT + │ │ ├── WRITE_ONLY → DELETE_ONLY Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ │ ├── PUBLIC → ABSENT ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id-)} + │ │ ├── MERGE_ONLY → DELETE_ONLY PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey-)} + │ │ ├── WRITE_ONLY → DELETE_ONLY TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 3} + │ │ └── WRITE_ONLY → ABSENT ColumnNotNull:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ └── 11 Mutation operations + │ ├── MakeWriteOnlyColumnDeleteOnly {"ColumnID":2,"TableID":106} + │ ├── SetColumnName {"ColumnID":2,"Name":"crdb_internal_co...","TableID":106} + │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":2,"Kind":2,"TableID":106} + │ ├── RemoveColumnNotNull {"ColumnID":2,"TableID":106} + │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"PostCommitNonRev..."} + └── Stage 2 of 2 in PostCommitNonRevertiblePhase + ├── 7 elements transitioning toward ABSENT + │ ├── DELETE_ONLY → ABSENT Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ ├── PUBLIC → ABSENT ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id-), TypeName: "INT8"} + │ ├── PUBLIC → ABSENT ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), Expr: unique_rowid()} + │ ├── DELETE_ONLY → ABSENT PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ ├── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-)} + │ ├── DELETE_ONLY → ABSENT TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ └── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 3} + └── 8 Mutation operations + ├── RemoveColumnDefaultExpression {"ColumnID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":2,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":3,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":3,"TableID":106} + ├── MakeDeleteOnlyColumnAbsent {"ColumnID":2,"TableID":106} + ├── RemoveJobStateFromDescriptor {"DescriptorID":106} + └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"all stages compl..."} diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_6_of_7.explain b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_6_of_7.explain new file mode 100644 index 000000000000..d40517a05d1e --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_6_of_7.explain @@ -0,0 +1,51 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); + +/* test */ +ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +EXPLAIN (DDL) rollback at post-commit stage 6 of 7; +---- +Schema change plan for rolling back ALTER TABLE ‹db›.public.‹tbl› ADD COLUMN ‹serial_id› INT8; + └── PostCommitNonRevertiblePhase + ├── Stage 1 of 2 in PostCommitNonRevertiblePhase + │ ├── 9 elements transitioning toward ABSENT + │ │ ├── WRITE_ONLY → DELETE_ONLY Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ │ ├── PUBLIC → ABSENT ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id-)} + │ │ ├── MERGE_ONLY → DELETE_ONLY PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey-)} + │ │ ├── WRITE_ONLY → DELETE_ONLY TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 3} + │ │ └── WRITE_ONLY → ABSENT ColumnNotNull:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ └── 11 Mutation operations + │ ├── MakeWriteOnlyColumnDeleteOnly {"ColumnID":2,"TableID":106} + │ ├── SetColumnName {"ColumnID":2,"Name":"crdb_internal_co...","TableID":106} + │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":2,"Kind":2,"TableID":106} + │ ├── RemoveColumnNotNull {"ColumnID":2,"TableID":106} + │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"PostCommitNonRev..."} + └── Stage 2 of 2 in PostCommitNonRevertiblePhase + ├── 7 elements transitioning toward ABSENT + │ ├── DELETE_ONLY → ABSENT Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ ├── PUBLIC → ABSENT ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id-), TypeName: "INT8"} + │ ├── PUBLIC → ABSENT ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), Expr: unique_rowid()} + │ ├── DELETE_ONLY → ABSENT PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ ├── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-)} + │ ├── DELETE_ONLY → ABSENT TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ └── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 3} + └── 8 Mutation operations + ├── RemoveColumnDefaultExpression {"ColumnID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":2,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":3,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":3,"TableID":106} + ├── MakeDeleteOnlyColumnAbsent {"ColumnID":2,"TableID":106} + ├── RemoveJobStateFromDescriptor {"DescriptorID":106} + └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"all stages compl..."} diff --git a/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_7_of_7.explain b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_7_of_7.explain new file mode 100644 index 000000000000..e6f9a9498835 --- /dev/null +++ b/pkg/sql/schemachanger/testdata/end_to_end/add_column_serial/add_column_serial__rollback_7_of_7.explain @@ -0,0 +1,49 @@ +/* setup */ +CREATE DATABASE db; +CREATE TABLE db.public.tbl (i INT PRIMARY KEY); + +/* test */ +ALTER TABLE db.public.tbl ADD COLUMN serial_id SERIAL; +EXPLAIN (DDL) rollback at post-commit stage 7 of 7; +---- +Schema change plan for rolling back ALTER TABLE ‹db›.public.‹tbl› ADD COLUMN ‹serial_id› INT8; + └── PostCommitNonRevertiblePhase + ├── Stage 1 of 2 in PostCommitNonRevertiblePhase + │ ├── 9 elements transitioning toward ABSENT + │ │ ├── WRITE_ONLY → DELETE_ONLY Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ │ ├── PUBLIC → ABSENT ColumnName:{DescID: 106 (tbl), Name: "serial_id", ColumnID: 2 (serial_id-)} + │ │ ├── WRITE_ONLY → DELETE_ONLY PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 2 (tbl_pkey-)} + │ │ ├── TRANSIENT_DELETE_ONLY → ABSENT TemporaryIndex:{DescID: 106 (tbl), IndexID: 3, ConstraintID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 1 (i), IndexID: 3} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ │ ├── PUBLIC → ABSENT IndexColumn:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 3} + │ │ └── WRITE_ONLY → ABSENT ColumnNotNull:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), IndexID: 2 (tbl_pkey-)} + │ └── 11 Mutation operations + │ ├── MakeWriteOnlyColumnDeleteOnly {"ColumnID":2,"TableID":106} + │ ├── SetColumnName {"ColumnID":2,"Name":"crdb_internal_co...","TableID":106} + │ ├── MakeWriteOnlyIndexDeleteOnly {"IndexID":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":1,"IndexID":3,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":2,"Kind":2,"TableID":106} + │ ├── RemoveColumnFromIndex {"ColumnID":2,"IndexID":3,"Kind":2,"TableID":106} + │ ├── MakeIndexAbsent {"IndexID":3,"TableID":106} + │ ├── RemoveColumnNotNull {"ColumnID":2,"TableID":106} + │ ├── SetJobStateOnDescriptor {"DescriptorID":106} + │ └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"PostCommitNonRev..."} + └── Stage 2 of 2 in PostCommitNonRevertiblePhase + ├── 6 elements transitioning toward ABSENT + │ ├── DELETE_ONLY → ABSENT Column:{DescID: 106 (tbl), ColumnID: 2 (serial_id-)} + │ ├── PUBLIC → ABSENT ColumnType:{DescID: 106 (tbl), ColumnFamilyID: 0 (primary), ColumnID: 2 (serial_id-), TypeName: "INT8"} + │ ├── PUBLIC → ABSENT ColumnDefaultExpression:{DescID: 106 (tbl), ColumnID: 2 (serial_id-), Expr: unique_rowid()} + │ ├── DELETE_ONLY → ABSENT PrimaryIndex:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-), ConstraintID: 2, TemporaryIndexID: 3, SourceIndexID: 1 (tbl_pkey+)} + │ ├── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 2 (tbl_pkey-)} + │ └── PUBLIC → ABSENT IndexData:{DescID: 106 (tbl), IndexID: 3} + └── 7 Mutation operations + ├── RemoveColumnDefaultExpression {"ColumnID":2,"TableID":106} + ├── MakeIndexAbsent {"IndexID":2,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":2,"TableID":106} + ├── CreateGCJobForIndex {"IndexID":3,"TableID":106} + ├── MakeDeleteOnlyColumnAbsent {"ColumnID":2,"TableID":106} + ├── RemoveJobStateFromDescriptor {"DescriptorID":106} + └── UpdateSchemaChangerJob {"IsNonCancelable":true,"RunningStatus":"all stages compl..."} diff --git a/pkg/sql/serial.go b/pkg/sql/serial.go index e5f4808aaf2f..76e94a1aea82 100644 --- a/pkg/sql/serial.go +++ b/pkg/sql/serial.go @@ -19,8 +19,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" - "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" - "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgnotice" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" @@ -132,14 +130,13 @@ func (p *planner) generateSerialInColumnDef( error, ) { - if err := assertValidSerialColumnDef(d, tableName); err != nil { + if err := catalog.AssertValidSerialColumnDef(d, tableName); err != nil { return nil, nil, nil, nil, err } newSpec := *d - // Make the column non-nullable in all cases. PostgreSQL requires - // this. + // Column is non-nullable in all cases. PostgreSQL requires this. newSpec.Nullable.Nullability = tree.NotNull // Clear the IsSerial bit now that it's been remapped. @@ -356,7 +353,7 @@ func SimplifySerialInColumnDefWithRowID( return nil } - if err := assertValidSerialColumnDef(d, tableName); err != nil { + if err := catalog.AssertValidSerialColumnDef(d, tableName); err != nil { return err } @@ -374,30 +371,3 @@ func SimplifySerialInColumnDefWithRowID( return nil } - -func assertValidSerialColumnDef(d *tree.ColumnTableDef, tableName *tree.TableName) error { - if d.HasDefaultExpr() { - // SERIAL implies a new default expression, we can't have one to - // start with. This is the error produced by pg in such case. - return pgerror.Newf(pgcode.Syntax, - "multiple default values specified for column %q of table %q", - tree.ErrString(&d.Name), tree.ErrString(tableName)) - } - - if d.Nullable.Nullability == tree.Null { - // SERIAL implies a non-NULL column, we can't accept a nullability - // spec. This is the error produced by pg in such case. - return pgerror.Newf(pgcode.Syntax, - "conflicting NULL/NOT NULL declarations for column %q of table %q", - tree.ErrString(&d.Name), tree.ErrString(tableName)) - } - - if d.Computed.Expr != nil { - // SERIAL cannot be a computed column. - return pgerror.Newf(pgcode.Syntax, - "SERIAL column %q of table %q cannot be computed", - tree.ErrString(&d.Name), tree.ErrString(tableName)) - } - - return nil -}