-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
schemachanger: Implement ALTER COLUMN ... SET NOT NULL
#95556
schemachanger: Implement ALTER COLUMN ... SET NOT NULL
#95556
Conversation
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_column.go
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, the TestXiang stuff, of course, should use some cleanup.
Can you add some end-to-end tests?
9c628ff
to
c53bc23
Compare
47ad22f
to
5444e27
Compare
@ajwerner I've addressed your early feedback. I'm investigating the one and last test failure in CI. At the same time, can you give it another look? Thank you a lot |
This is officially ready for review! |
ALTER COLUMN ... SET NOT NULL
ALTER COLUMN ... SET NOT NULL
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice!
Reviewed 7 of 32 files at r1, 8 of 27 files at r2, 50 of 168 files at r3, 7 of 15 files at r4.
Reviewable status:complete! 0 of 0 LGTMs obtained (waiting on @Xiang-Gu)
pkg/sql/schemachanger/scdecomp/decomp.go
line 449 at r4 (raw file):
//ConstraintID: tbl.GetNextConstraintID(), }) //tbl.TableDesc().NextConstraintID++
detritus?
Code quote:
//ConstraintID: tbl.GetNextConstraintID(),
})
//tbl.TableDesc().NextConstraintID++
}
pkg/sql/schemachanger/scexec/scmutationexec/helpers.go
line 222 at r4 (raw file):
} func enqueueAddNotNullMutation(
nit: this subtraction is getting silly, and nothing seems to return an error. We can use generics now. What if you do:
func addMutation[M any](f func(M, descpb.DescriptorMutation_Direction), m M, dir descpb.DescriptoMutation_Direction) {
f(tbl, m, dir)
tbl.NextMutationID--
}
Then you can call this with:
addMutation(tbl.AddNotNullMutation, ck, descpb.DescriptorMutation_ADD)
pkg/sql/schemachanger/testdata/end_to_end/alter_table_alter_column_set_not_null
line 7 at r4 (raw file):
+object {100 101 t} -> 104 test
can you add some statements during this which attempt to write null values and fail at the appropriate stages?
Can you hold off before merging this please? I would like to get #95631 in first, because otherwise a lot of rebasing is needed on my end and time is running out before I go on leave. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a pass, this approach seems sound.
@@ -2023,7 +2120,7 @@ deprules | |||
to: idx-or-col-Node | |||
query: | |||
- $descriptor[Type] IN ['*scpb.Database', '*scpb.Schema', '*scpb.View', '*scpb.Sequence', '*scpb.Table', '*scpb.EnumType', '*scpb.AliasType', '*scpb.CompositeType'] | |||
- $idx-or-col[Type] IN ['*scpb.Column', '*scpb.PrimaryIndex', '*scpb.SecondaryIndex', '*scpb.TemporaryIndex', '*scpb.UniqueWithoutIndexConstraint', '*scpb.CheckConstraint', '*scpb.ForeignKeyConstraint'] | |||
- $idx-or-col[Type] IN ['*scpb.Column', '*scpb.PrimaryIndex', '*scpb.SecondaryIndex', '*scpb.TemporaryIndex', '*scpb.UniqueWithoutIndexConstraint', '*scpb.CheckConstraint', '*scpb.ForeignKeyConstraint', '*scpb.ColumnNotNull'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of the test output in the release_22_2 package should change, ever. Also, I don't think that foreign key constraints are featured in 22.2 so I filed an issue: #95849
)) | ||
} | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Should this be in alter_table_add_constraint.go
instead? I realize there's no actual ADD CONSTRAINT syntax for NOT NULL, so you be the judge.
@@ -0,0 +1,51 @@ | |||
// Copyright 2022 The Cockroach Authors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/2022/2023
@@ -172,6 +172,7 @@ func init() { | |||
(*scpb.CheckConstraint)(nil), | |||
(*scpb.ForeignKeyConstraint)(nil), | |||
(*scpb.UniqueWithoutIndexConstraint)(nil), | |||
(*scpb.ColumnNotNull)(nil), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should have a filter in the helpers for non-index-backed constraints?
// "too late" (i.e. when column transitions to ABSENT) is that the | ||
// constraint would still be visible and enforced while the column is not writable. | ||
// This can cause concurrent WRITE to see and attempt to uphold the constraint, | ||
// but it wouldn't be able to find the column. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation, I get why it needs to be in the same stage. Any reason why it's the not-null before the column and not the other way round? Not that it matters, just curious.
// - Delete `is_23_1_cluster_version_active` field from protobuf. | ||
// This plan will guarantee compatibility in all possible mixed version states | ||
// as well as restoring from a old backup. | ||
bool deprecated_is_nullable = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can deprecate fields in protobuf with [deprecated=true]
. Also add // Deprecated.
in the comment block above for a pretty strike-through visual effect in GoLand.
// V24_1: | ||
// - Delete `is_23_1_cluster_version_active` field from protobuf. | ||
// This plan will guarantee compatibility in all possible mixed version states | ||
// as well as restoring from a old backup. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments such as these don't typically age well. I recommend you use language which is more non-committal and less subject to current assumptions as to what the future will look like. You're far better off filing tickets in github to track future work.
// This plan will guarantee compatibility in all possible mixed version states | ||
// as well as restoring from a old backup. | ||
bool deprecated_is_nullable = 5; | ||
bool is_23_1_cluster_version_active = 11; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider renaming this to is_element_created_in_23_1_or_later
? This field is a neat solution to the problem at hand, which I'd find mildly distasteful if it weren't for the fact that we need to migrate to a new model for ColumnType in this release anyway as per #92605
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative to consider, which might age better: define a
message ElementCreationMetadata {
bool in_23_1_or_later = 1;
}
and embed it into ColumnType
. Then in scdecomp provide a helper which generates a scpb.ElementCreationMetadata
instance based on the cluster settings. This way this logic can be re-used and adapted for later versions and for other elements as well.
Another alternative: have this metadata in the TargetMetadata
message instead.
// - Start deprecating `is_nullable` (rename it to `deprecated_is_nullable`); | ||
// - Introduce `is_23_1_cluster_version_active`; | ||
// - Add a version gate in the builder for `ADD COLUMN` (see addColumn function) | ||
// to set `is_23_1_cluster_version_active` if cluster version V23_1 is active. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't scdecomp
be doing that as well?
5444e27
to
643edf9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ajwerner @postamar Thank you both for the feedback. I've addressed them all. I will hold off merging until #95631 is in, but in the meantime, I'd like to hear any other comments/feedback you still have for this PR.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @ajwerner and @postamar)
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_alter_column_set_not_null.go
line 1 at r4 (raw file):
Previously, postamar (Marius Posta) wrote…
s/2022/2023
Done.
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_alter_column_set_not_null.go
line 51 at r4 (raw file):
Previously, postamar (Marius Posta) wrote…
nit: Should this be in
alter_table_add_constraint.go
instead? I realize there's no actual ADD CONSTRAINT syntax for NOT NULL, so you be the judge.
I'd like to keep as is bc 1. it's less boilerplate code added (all the nested min supported version control for add constraints), and 2. we don't have SET NOT NULL NOT VALID
as other constraints.
pkg/sql/schemachanger/scdecomp/decomp.go
line 449 at r4 (raw file):
Previously, ajwerner wrote…
detritus?
done
Code quote:
ColumnID: col.GetID(),
//ConstraintID: tbl.GetNextConstraintID(),
pkg/sql/schemachanger/scexec/scmutationexec/helpers.go
line 222 at r4 (raw file):
Previously, ajwerner wrote…
nit: this subtraction is getting silly, and nothing seems to return an error. We can use generics now. What if you do:
func addMutation[M any](f func(M, descpb.DescriptorMutation_Direction), m M, dir descpb.DescriptoMutation_Direction) { f(tbl, m, dir) tbl.NextMutationID-- }
Then you can call this with:
addMutation(tbl.AddNotNullMutation, ck, descpb.DescriptorMutation_ADD)
This is a good idea! Done
pkg/sql/schemachanger/scpb/elements.proto
line 209 at r3 (raw file):
Previously, postamar (Marius Posta) wrote…
Comments such as these don't typically age well. I recommend you use language which is more non-committal and less subject to current assumptions as to what the future will look like. You're far better off filing tickets in github to track future work.
done, filed #95893
pkg/sql/schemachanger/scpb/elements.proto
line 210 at r3 (raw file):
Previously, postamar (Marius Posta) wrote…
You can deprecate fields in protobuf with
[deprecated=true]
. Also add// Deprecated.
in the comment block above for a pretty strike-through visual effect in GoLand.
done
pkg/sql/schemachanger/scpb/elements.proto
line 211 at r3 (raw file):
message ElementCreationMetadata {
bool in_23_1_or_later = 1;
}
I like this idea. I did some plumbing so that we have the cluster version inscdecomp
.
pkg/sql/schemachanger/scpb/elements.proto
line 196 at r4 (raw file):
Previously, postamar (Marius Posta) wrote…
Shouldn't
scdecomp
be doing that as well?
it's okay bc we have a minSupportedVersion on elements and any elements not supported in current cluster version will be filtered out. In fact, I think this makes it easier to think about: In the builder, we use the version gate to only set this newly added field is_23_1_cluster_version_active
. We still decomp and add/drop newly introduced elements (in addition to what we've already been doing) while rest assured that it will be filtered out anyway if they're not supported.
pkg/sql/schemachanger/scplan/internal/rules/current/dep_drop_column.go
line 115 at r3 (raw file):
Previously, postamar (Marius Posta) wrote…
Thanks for the explanation, I get why it needs to be in the same stage. Any reason why it's the not-null before the column and not the other way round? Not that it matters, just curious.
I think that'd be fine. All we need here is same-stage.
pkg/sql/schemachanger/scplan/internal/rules/current/op_drop.go
line 175 at r3 (raw file):
Previously, postamar (Marius Posta) wrote…
Perhaps we should have a filter in the helpers for non-index-backed constraints?
We do! IsSupportedNonIndexBackedConstraint
Code quote:
constraint.Type(
(*scpb.CheckConstraint)(nil),
(*scpb.ForeignKeyConstraint)(nil),
(*scpb.UniqueWithoutIndexConstraint)(nil),
(*scpb.ColumnNotNull)(nil),
pkg/sql/schemachanger/scplan/internal/rules/release_22_2/testdata/deprules
line 2123 at r2 (raw file):
Previously, postamar (Marius Posta) wrote…
None of the test output in the release_22_2 package should change, ever. Also, I don't think that foreign key constraints are featured in 22.2 so I filed an issue: #95849
yeah, we are aware of this and thanks for filing the issue.
pkg/sql/schemachanger/testdata/end_to_end/alter_table_alter_column_set_not_null
line 7 at r4 (raw file):
Previously, ajwerner wrote…
can you add some statements during this which attempt to write null values and fail at the appropriate stages?
Done!
pkg/sql/catalog/tabledesc/column.go
line 37 at r1 (raw file):
Previously, ajwerner wrote…
this is detritus, right?
Done.
816d36c
to
4bac39b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_column.go
Outdated
Show resolved
Hide resolved
4093917
to
d86e09e
Compare
This commit introduce a new element protobuf `ColumnNotNull`, which will be used in later commit to re-implement how we handle NOT NULL constraint of a column.
This commit uses the newly introduced element `ColumnNotNull` and re-implement logic handling NOT NULL constraint of a column in the declarative schema changer. The key idea is to treat NOT NULL as a functionally equivalent CHECK constraint, (col_name IS NOT NULL), and use existing validation logic for CHECK to validate the NOT NULL constraint. This commit is a implementation change for something we already support in the declarative schema changer, so the way we achieve compatibility in mixed version state is to properly version gate the builder and mutation execution. The detailed plan can be found in the comment inside `ColumnType` element.
d86e09e
to
74dd4a1
Compare
TFTR! bors r+ |
Build succeeded: |
This PR implements
ALTER TABLE t ALTER COLUMN c SET NOT NULL
in the declarative schema changer.The key change is to introduce the
ColumnNotNull
element for column NOT NULL constraint. Previously, this is recorded in a bool field in theColumnType
element. We will treatColumnNotNull
in a similar fashion asCheckConstraint
where we indeed uses a dummy check constraint for validation.The plan to address mixed version compatibility is laid out in comments in commit 2.
Epic: None