Skip to content
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

Merged
merged 4 commits into from
Jan 27, 2023

Conversation

Xiang-Gu
Copy link
Contributor

@Xiang-Gu Xiang-Gu commented Jan 19, 2023

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 the ColumnType element. We will treat ColumnNotNull in a similar fashion as CheckConstraint 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

@cockroach-teamcity
Copy link
Member

This change is Reviewable

Copy link
Contributor

@ajwerner ajwerner left a 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?

@Xiang-Gu Xiang-Gu force-pushed the introduce-not-null-element branch 7 times, most recently from 9c628ff to c53bc23 Compare January 23, 2023 18:50
@Xiang-Gu Xiang-Gu force-pushed the introduce-not-null-element branch 6 times, most recently from 47ad22f to 5444e27 Compare January 24, 2023 01:28
@Xiang-Gu
Copy link
Contributor Author

@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

@Xiang-Gu
Copy link
Contributor Author

This is officially ready for review!

@Xiang-Gu Xiang-Gu marked this pull request as ready for review January 24, 2023 17:50
@Xiang-Gu Xiang-Gu requested a review from a team January 24, 2023 17:50
@Xiang-Gu Xiang-Gu requested a review from a team as a code owner January 24, 2023 17:50
@Xiang-Gu Xiang-Gu requested a review from yuzefovich January 24, 2023 17:50
@Xiang-Gu Xiang-Gu changed the title WIP: schemachanger: Implement ALTER COLUMN ... SET NOT NULL schemachanger: Implement ALTER COLUMN ... SET NOT NULL Jan 24, 2023
@yuzefovich yuzefovich removed request for a team and yuzefovich January 24, 2023 18:05
Copy link
Contributor

@ajwerner ajwerner left a 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: :shipit: 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?

@postamar
Copy link

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!

Copy link

@postamar postamar left a 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']

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

))
}
})
}

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.

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

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.

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;

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.

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;

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

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.

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?

@Xiang-Gu Xiang-Gu force-pushed the introduce-not-null-element branch from 5444e27 to 643edf9 Compare January 26, 2023 00:22
Copy link
Contributor Author

@Xiang-Gu Xiang-Gu left a 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: :shipit: 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 VALIDas 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 in scdecomp.


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.

@Xiang-Gu Xiang-Gu force-pushed the introduce-not-null-element branch 6 times, most recently from 816d36c to 4bac39b Compare January 27, 2023 18:35
Copy link
Contributor

@ajwerner ajwerner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Xiang-Gu Xiang-Gu force-pushed the introduce-not-null-element branch 2 times, most recently from 4093917 to d86e09e Compare January 27, 2023 20:25
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.
@Xiang-Gu Xiang-Gu force-pushed the introduce-not-null-element branch from d86e09e to 74dd4a1 Compare January 27, 2023 21:17
@Xiang-Gu
Copy link
Contributor Author

TFTR!

bors r+

@craig
Copy link
Contributor

craig bot commented Jan 27, 2023

Build succeeded:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants