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 TABLE .. VALIDATE CONSTRAINT .. #96648

Merged
merged 2 commits into from
Feb 11, 2023

Conversation

Xiang-Gu
Copy link
Contributor

@Xiang-Gu Xiang-Gu commented Feb 6, 2023

The main idea is to drop the unvalidated element and add a vanilla counterpart in the builder state.

Commit 2 involves some changes to allow both legacy and declarative schema changer handle
this stmt correctly: ALTER TABLE .. ADD/DROP CONSTRAINT .. [NOT VALID]; VALIDATE CONSTRAINT ..;
so this PR warrants a release note.

Epic: None
Release note (bug fix): Allow ALTER TABLE .. ADD/DROP CONSTRAINT .. [NOT VALID]; VALIDATE CONSTRAINT ..; to behave consistently with Postgres. Previously, the VALIDATE CONSTRAINT part will fail and cause the whole statement to fail.

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@Xiang-Gu Xiang-Gu force-pushed the implement-validate-constraint branch from 229a8e0 to aed8976 Compare February 6, 2023 19:09
@Xiang-Gu
Copy link
Contributor Author

Xiang-Gu commented Feb 6, 2023

This is ready for a look!

@Xiang-Gu Xiang-Gu changed the title WIP: schemachanger: implement ALTER TABLE .. VALIDATE CONSTRAINT .. schemachanger: implement ALTER TABLE .. VALIDATE CONSTRAINT .. Feb 6, 2023
@Xiang-Gu Xiang-Gu marked this pull request as ready for review February 6, 2023 20:13
@Xiang-Gu Xiang-Gu requested a review from a team February 6, 2023 20:13
@Xiang-Gu Xiang-Gu requested a review from a team as a code owner February 6, 2023 20:13
@Xiang-Gu Xiang-Gu requested a review from rytaft February 6, 2023 20:13
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.

Reviewed 15 of 15 files at r1, all commit messages.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @rytaft and @Xiang-Gu)


pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 44 at r1 (raw file):

	}
	if retrieveUniqueWithoutIndexConstraintElem(b, tbl.TableID, constraintID) != nil {
		return

Consider factoring out a helper here called getConstraintElementByID. Also, is there an issue if the constraint had been dropped? Like imagine you dropped the constraint and then validated it? Would you find it?

Code quote:

	if retrieveCheckConstraintElem(b, tbl.TableID, constraintID) != nil {
		return
	}
	if retrieveUniqueWithoutIndexConstraintElem(b, tbl.TableID, constraintID) != nil {
		return
	}
	if retrieveForeignKeyConstraintElem(b, tbl.TableID, constraintID) != nil {
		return
	}

pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 57 at r1 (raw file):

		uwiNotValidElem:    retrieveUniqueWithoutIndexConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
		fkNotValidElem:     retrieveForeignKeyConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
	}

nit: any reason to not just construct the spec inline? I feel like it's slightly more pleasant to not introduce the intermediate variable.

Code quote:

	spec := validateConstraintSpec{
		constraintNameElem: constraintNameElem,
		ckNotValidElem:     retrieveCheckConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
		uwiNotValidElem:    retrieveUniqueWithoutIndexConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
		fkNotValidElem:     retrieveForeignKeyConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
	}

pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 61 at r1 (raw file):

}

func retrieveCheckConstraintElem(

These smell, but I don't have anything better for you right now. Just a passing thought.

@Xiang-Gu Xiang-Gu force-pushed the implement-validate-constraint branch from aed8976 to af05a7f Compare February 7, 2023 20:20
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.

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner and @rytaft)


pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 44 at r1 (raw file):

Previously, ajwerner wrote…

Consider factoring out a helper here called getConstraintElementByID. Also, is there an issue if the constraint had been dropped? Like imagine you dropped the constraint and then validated it? Would you find it?

  1. Done. I instead created a helper where we determine whether or not we should validate the constraint.

  • if the constraint had just been dropped: the constraint name will be set to a placeholder and we will return an error from resolveConstraint, consistent with what the legacy schema changer does.
  • if the constraint had just been added: the logic will determine that this is something we should validate and thus it can proceed.

Code quote:

if retrieveUniqueWithoutIndexConstraintElem(b, tbl.TableID, constraintID) != nil {

pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 57 at r1 (raw file):

Previously, ajwerner wrote…

nit: any reason to not just construct the spec inline? I feel like it's slightly more pleasant to not introduce the intermediate variable.

nothing particularly special: I just feel like the resulting function signature doesn't look pretty
validateConstraint(b, tableID, constraintID, constraintNameElem)

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.

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @rytaft and @Xiang-Gu)


pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 44 at r1 (raw file):

Previously, Xiang-Gu (Xiang Gu) wrote…
  1. Done. I instead created a helper where we determine whether or not we should validate the constraint.

  • if the constraint had just been dropped: the constraint name will be set to a placeholder and we will return an error from resolveConstraint, consistent with what the legacy schema changer does.
  • if the constraint had just been added: the logic will determine that this is something we should validate and thus it can proceed.

When you say the constraint name will be set to a placeholder, where does that happen?


pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 57 at r1 (raw file):

Previously, Xiang-Gu (Xiang Gu) wrote…

nothing particularly special: I just feel like the resulting function signature doesn't look pretty
validateConstraint(b, tableID, constraintID, constraintNameElem)

I was just suggesting:

validateConstraint(b, tbl.TableID, validateConstraintSpec{
	constraintNameElem: constraintNameElem,
	ckNotValidElem:     retrieveCheckConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
	uwiNotValidElem:    retrieveUniqueWithoutIndexConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
	fkNotValidElem:     retrieveForeignKeyConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
})

@Xiang-Gu Xiang-Gu force-pushed the implement-validate-constraint branch from af05a7f to 74d4545 Compare February 7, 2023 21:01
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.

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner and @rytaft)


pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 44 at r1 (raw file):

Previously, ajwerner wrote…

When you say the constraint name will be set to a placeholder, where does that happen?

In the statement phase of that DROP CONSTRAINT, that not-valid constraint will be dropped as well as its name.


pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 57 at r1 (raw file):

Previously, ajwerner wrote…

I was just suggesting:

validateConstraint(b, tbl.TableID, validateConstraintSpec{
	constraintNameElem: constraintNameElem,
	ckNotValidElem:     retrieveCheckConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
	uwiNotValidElem:    retrieveUniqueWithoutIndexConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
	fkNotValidElem:     retrieveForeignKeyConstraintUnvalidatedElem(b, tbl.TableID, constraintID),
})

ah I see . done

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.

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @rytaft and @Xiang-Gu)


pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 44 at r1 (raw file):

Previously, Xiang-Gu (Xiang Gu) wrote…

In the statement phase of that DROP CONSTRAINT, that not-valid constraint will be dropped as well as its name.

So what happens if you do:

CREATE TABLE t (i INT PRIMARY KEY);
ALTER TABLE t ADD CONSTRAINT c CHECK (i > 0) NOT VALID;

and

ALTER TABLE t DROP CONSTRAINT c, VALIDATE CONSTRAINT c;

Or:

ALTER TABLE t DROP CONSTRAINT c, ADD CONSTRAINT c CHECK (i > 1) NOT VALID, VALIDATE CONSTRAINT c;

Does the first case error and the second case validate the newly added check constraint?

@Xiang-Gu Xiang-Gu force-pushed the implement-validate-constraint branch 2 times, most recently from ac9a18e to 872337a Compare February 8, 2023 23:04
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.

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner and @rytaft)


pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_validate_constraint.go line 44 at r1 (raw file):

Previously, ajwerner wrote…

So what happens if you do:

CREATE TABLE t (i INT PRIMARY KEY);
ALTER TABLE t ADD CONSTRAINT c CHECK (i > 0) NOT VALID;

and

ALTER TABLE t DROP CONSTRAINT c, VALIDATE CONSTRAINT c;

Or:

ALTER TABLE t DROP CONSTRAINT c, ADD CONSTRAINT c CHECK (i > 1) NOT VALID, VALIDATE CONSTRAINT c;

Does the first case error and the second case validate the newly added check constraint?

Yes, I have ensured that new schema changer handles both cases as expected.

I also tried to make legacy schema changer handles the first case correctly, but it's not easy to get the second case work properly (see #96831)

Copy link
Contributor

@chengxiong-ruan chengxiong-ruan left a comment

Choose a reason for hiding this comment

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

Nice work. just one request on test and a question on constraint ID increments.

ALTER TABLE t_96648 ADD CONSTRAINT check_i CHECK (i > 0), VALIDATE CONSTRAINT check_i;

statement ok
ALTER TABLE t_96648 ADD CONSTRAINT check_i1 CHECK (i > 0) NOT VALID, VALIDATE CONSTRAINT check_i1;
Copy link
Contributor

Choose a reason for hiding this comment

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

could you add some test that the validation fails after inserting some bad data with i <= 0?

@Xiang-Gu Xiang-Gu force-pushed the implement-validate-constraint branch from 872337a to cd583ed Compare February 10, 2023 20:49
The main idea is to drop the unvalidated element and add a vanilla
counterpart in the builder state.
@Xiang-Gu Xiang-Gu force-pushed the implement-validate-constraint branch from cd583ed to 9266fdc Compare February 10, 2023 21:00
@Xiang-Gu
Copy link
Contributor Author

TFTR!

bors r+

@craig
Copy link
Contributor

craig bot commented Feb 11, 2023

Build succeeded:

@craig craig bot merged commit c2bb35d into cockroachdb:master Feb 11, 2023
@Xiang-Gu Xiang-Gu deleted the implement-validate-constraint branch February 11, 2023 01:55
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