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

sql: Add IF NOT EXISTS modifier to ALTER TABLE ... ADD CONSTRAINT #71257

Merged
merged 1 commit into from
Nov 12, 2021

Conversation

postamar
Copy link
Contributor

@postamar postamar commented Oct 7, 2021

Previously, it was not possible to execute ALTER TABLE ... ADD
CONSTRAINT IF NOT EXISTS ... statements. This commit adds this IF NOT
EXISTS modifier to the ADD CONSTRAINT command of the ALTER TABLE
statement. When the modifier is present and the defined constraint name
already exists in the table, the statement no-ops instead of erroring.

Note that this syntax is not supported in PostgreSQL, however PostgreSQL
has transactional schema changes and PL/pgSQL which make it possible to
achieve the same thing. CockroachDB has none of that yet.

Fixes #53007.

Release note (sql change): added ALTER TABLE ... ADD CONSTRAINT IF NOT
EXISTS.

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@postamar postamar marked this pull request as ready for review October 7, 2021 16:44
@postamar postamar requested a review from a team October 7, 2021 16:44
@postamar
Copy link
Contributor Author

postamar commented Oct 7, 2021

@ekalinin I'm very sorry that I did not realize you already had a pull request up for this issue, #53095, and furthermore that you had recently intended to rebase it. It was right there in github, but I didn't notice until right now, my apologies.

I looked at your pull request and found it reassuring that we essentially took the same approach in implementing this. My only significant point of contention pertains to the handling the IF NOT EXISTS in the startExec method, pgcode. DuplicateObject is not the only error type that can be returned. Also, we'd probably have to check that the error is not marked as an assertion failure, and more generally (but perhaps that's just me) I'd rather not make too many assumptions on what and how the table descriptor validation works.

I also wasn't sure about changing the grammar for table_constraint, as that would allow IF NOT EXISTS modifiers in CREATE TABLE statements. These would have no effect, but it depends how strict we want to be. I don't have an opinion on this.

@ekalinin
Copy link
Contributor

ekalinin commented Oct 9, 2021

@ekalinin I'm very sorry that I did not realize you already had a pull request up for this issue, #53095, and furthermore that you had recently intended to rebase it. It was right there in github, but I didn't notice until right now, my apologies.

I looked at your pull request and found it reassuring that we essentially took the same approach in implementing this. My only significant point of contention pertains to the handling the IF NOT EXISTS in the startExec method, pgcode. DuplicateObject is not the only error type that can be returned. Also, we'd probably have to check that the error is not marked as an assertion failure, and more generally (but perhaps that's just me) I'd rather not make too many assumptions on what and how the table descriptor validation works.

I also wasn't sure about changing the grammar for table_constraint, as that would allow IF NOT EXISTS modifiers in CREATE TABLE statements. These would have no effect, but it depends how strict we want to be. I don't have an opinion on this.

Hey @postamar! I just closed my PR as i think your PR is better ;)
Thanks for notifying me.

@postamar postamar marked this pull request as draft October 14, 2021 14:13
@postamar
Copy link
Contributor Author

I'm converting this PR back to draft mode to rebase it. Now that #70604 has been merged to master, I've been able to find a few more subtle bugs.

@postamar postamar force-pushed the add-constraint-if-not-exists branch 3 times, most recently from ca916db to d59d9ea Compare October 14, 2021 19:37
@postamar postamar marked this pull request as ready for review October 14, 2021 20:56
@postamar postamar requested a review from a team as a code owner October 14, 2021 20:56
@postamar postamar requested a review from otan October 26, 2021 17:23
@postamar postamar force-pushed the add-constraint-if-not-exists branch 2 times, most recently from 20bdaca to 49f2c5e Compare October 26, 2021 20:38
@@ -2222,6 +2222,17 @@ alter_table_cmd:
ValidationBehavior: $3.validationBehavior(),
}
}
// ALTER TABLE <name> ADD CONSTRAINT IF NOT EXISTS ...
| ADD CONSTRAINT IF NOT EXISTS constraint_name constraint_elem opt_validate_behavior
Copy link
Contributor

Choose a reason for hiding this comment

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

What if instead of another parser case we added an opt_if_not_exist to the existing rule?

Copy link
Contributor

Choose a reason for hiding this comment

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

last time i tried something like that i got shift reduce'd to oblivion, but i didnt figure out why. you can try but i think that's why even in pg IF EXISTS / NOT EXISTS are always a separate clause.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The presence of IF NOT EXISTS makes the constraint_name mandatory instead of optional. There were several ways to deal with this awkwardness and because I don't have taste in these matters I picked the one which made for the smallest diff.

Copy link
Contributor

@otan otan left a comment

Choose a reason for hiding this comment

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

sorry had this in pending for ages

@@ -194,6 +194,12 @@ func (n *alterTableNode) startExec(params runParams) error {
return err
}
case *tree.AlterTableAddConstraint:
skip, err := validateConstraintNameIsNotUsed(n.tableDesc, t)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: pedantically:

if skip, err := validateConstraintNameIsNotUsed(n.tableDesc, t); err != nil {
   ...
} else if skip {
   ....
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -578,7 +578,7 @@ t CREATE TABLE public.t (
statement ok
CREATE INDEX i ON t (x);

statement error pq: constraint with name i already exists
statement error pgcode 42P07 an index named "i" already exists or is being dropped
Copy link
Contributor

Choose a reason for hiding this comment

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

i wonder if we should keep "constraint" here instead of "index" in the error message. seems more postgres-like

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My rationale for this was that since I'm returning pgcode.DuplicateRelation I might as well reference a relation. I don't know what the right thing to do is here and I'm not familiar with postgres. Should I change the message to s/constraint/index only, or should I also change the pgcode to something else? I'll defer to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm having looked into this further, it turns out that we sometimes return pgcode.DuplicateObject and a duplicate constraint name message, depending on which statements are executed.

For the sake of uniformity perhaps we should always return pgcode.DuplicateObject and not talk about indexes.

@@ -2222,6 +2222,17 @@ alter_table_cmd:
ValidationBehavior: $3.validationBehavior(),
}
}
// ALTER TABLE <name> ADD CONSTRAINT IF NOT EXISTS ...
| ADD CONSTRAINT IF NOT EXISTS constraint_name constraint_elem opt_validate_behavior
Copy link
Contributor

Choose a reason for hiding this comment

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

last time i tried something like that i got shift reduce'd to oblivion, but i didnt figure out why. you can try but i think that's why even in pg IF EXISTS / NOT EXISTS are always a separate clause.

pkg/sql/create_index.go Show resolved Hide resolved
@postamar postamar force-pushed the add-constraint-if-not-exists branch from 49f2c5e to b8bee29 Compare November 11, 2021 15:12
@blathers-crl blathers-crl bot requested a review from otan November 11, 2021 15:12
Previously, it was not possible to execute ALTER TABLE ... ADD
CONSTRAINT IF NOT EXISTS ... statements. This commit adds this IF NOT
EXISTS modifier to the ADD CONSTRAINT command of the ALTER TABLE
statement. When the modifier is present and the defined constraint name
already exists in the table, the statement no-ops instead of erroring.

Note that this syntax is not supported in PostgreSQL, however PostgreSQL
has transactional schema changes and PL/pgSQL which make it possible to
achieve the same thing. CockroachDB has none of that yet.

This commit also fixes some minor bugs or inconsistencies in the
handling of duplicate index names.

Fixes cockroachdb#53007.

Release note (sql change): added ALTER TABLE ... ADD CONSTRAINT IF NOT
EXISTS.
@postamar postamar force-pushed the add-constraint-if-not-exists branch from b8bee29 to e89093f Compare November 11, 2021 15:13
@postamar
Copy link
Contributor Author

Thanks for the review!

bors r+

@craig
Copy link
Contributor

craig bot commented Nov 12, 2021

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.

Add syntax for to use "IF NOT EXISTS" modifier to "ALTER TABLE add CONSTRAINT"
5 participants