-
Notifications
You must be signed in to change notification settings - Fork 514
sql: support EXCLUDE CONSTRAINTS for SQL Server sources #38532
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| # Copyright Materialize, Inc. and contributors. All rights reserved. | ||
| # | ||
| # Use of this software is governed by the Business Source License | ||
| # included in the LICENSE file at the root of this repository. | ||
| # | ||
| # 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. | ||
|
|
||
| # | ||
| # EXCLUDE CONSTRAINTS / EXCLUDE ALL CONSTRAINTS: excluded upstream constraints | ||
| # are not recorded as Materialize keys, so dropping them upstream is a | ||
| # non-event instead of stalling the table. NOTE: SQL Server does not allow | ||
| # dropping a PRIMARY KEY while CDC is enabled, so the drop scenarios use UNIQUE | ||
| # constraints. Upstream ALTER COLUMN still errors the table regardless of these | ||
| # options, so EXCLUDE ALL CONSTRAINTS is checked for key recording and | ||
| # nullability only. | ||
| # | ||
|
|
||
| $ postgres-execute connection=postgres://mz_system:materialize@${testdrive.materialize-internal-sql-addr} | ||
| ALTER SYSTEM SET enable_exclude_constraints_option = true | ||
|
|
||
| $ sql-server-connect name=sql-server | ||
| server=tcp:sql-server,1433;IntegratedSecurity=true;TrustServerCertificate=true;User ID=${arg.default-sql-server-user};Password=${arg.default-sql-server-password};Database=test | ||
|
|
||
| > CREATE SECRET IF NOT EXISTS sql_server_pass AS '${arg.default-sql-server-password}' | ||
|
|
||
| > DROP CONNECTION IF EXISTS sql_server_test_27_connection CASCADE | ||
| > CREATE CONNECTION sql_server_test_27_connection TO SQL SERVER ( | ||
| HOST 'sql-server', | ||
| PORT 1433, | ||
| DATABASE test, | ||
| USER '${arg.default-sql-server-user}', | ||
| PASSWORD = SECRET sql_server_pass | ||
| ); | ||
|
|
||
| $ sql-server-execute name=sql-server | ||
| CREATE TABLE t27_uniq (id INT NOT NULL, wallet VARCHAR(64) NOT NULL, CONSTRAINT t27_pk PRIMARY KEY (id), CONSTRAINT t27_uq_wallet UNIQUE (wallet)); | ||
| INSERT INTO t27_uniq VALUES (1, 'a'), (2, 'b'); | ||
| EXEC sys.sp_cdc_enable_table @source_schema = 'dbo', @source_name = 't27_uniq', @role_name = 'SA', @supports_net_changes = 0; | ||
|
|
||
| CREATE TABLE t27_all (id INT NOT NULL, email VARCHAR(64) NOT NULL, CONSTRAINT t27_all_pk PRIMARY KEY (id)); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this named t27? where does the 27 come from, should it just say exclude constraints rather than a number?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [peter's bot] The number is this directory's convention: each numbered file prefixes its upstream tables with its own number so files sharing one SQL Server instance never collide (25-constraints.td uses t25_pk, 26-constraint-column-order.td uses t26_pk_order, 15-expose-progress.td uses t15_pk). Kept it for consistency. The MySQL and Postgres variants use descriptive names because those directories are not numbered. |
||
| INSERT INTO t27_all VALUES (1, 'a@b.c'); | ||
| EXEC sys.sp_cdc_enable_table @source_schema = 'dbo', @source_name = 't27_all', @role_name = 'SA', @supports_net_changes = 0; | ||
|
|
||
| CREATE TABLE t27_stall (id INT NOT NULL, wallet VARCHAR(64) NOT NULL, CONSTRAINT t27_stall_pk PRIMARY KEY (id), CONSTRAINT t27_stall_uq UNIQUE (wallet)); | ||
| INSERT INTO t27_stall VALUES (1, 'a'); | ||
| EXEC sys.sp_cdc_enable_table @source_schema = 'dbo', @source_name = 't27_stall', @role_name = 'SA', @supports_net_changes = 0; | ||
|
|
||
| > CREATE SOURCE t27_source FROM SQL SERVER CONNECTION sql_server_test_27_connection; | ||
|
|
||
| # | ||
| # Validation errors | ||
| # | ||
|
|
||
| ! CREATE TABLE t27_uniq FROM SOURCE t27_source (REFERENCE t27_uniq) | ||
| WITH (EXCLUDE CONSTRAINTS ('nope')); | ||
| contains:EXCLUDE CONSTRAINTS refers to constraints that do not exist on table dbo.t27_uniq | ||
|
|
||
| ! CREATE TABLE t27_uniq FROM SOURCE t27_source (REFERENCE t27_uniq) | ||
| WITH (EXCLUDE CONSTRAINTS ('t27_uq_wallet'), EXCLUDE ALL CONSTRAINTS); | ||
| contains:EXCLUDE ALL CONSTRAINTS cannot be combined with EXCLUDE CONSTRAINTS | ||
|
|
||
| # | ||
| # Excluding a named UNIQUE constraint: the PRIMARY KEY survives, the excluded | ||
| # constraint is not recorded as a key, and its later upstream drop is a | ||
| # non-event. | ||
| # | ||
|
|
||
| > CREATE TABLE t27_uniq FROM SOURCE t27_source (REFERENCE t27_uniq) | ||
| WITH (EXCLUDE CONSTRAINTS ('t27_uq_wallet')); | ||
|
|
||
| > SELECT * FROM t27_uniq; | ||
| 1 a | ||
| 2 b | ||
|
|
||
| > CREATE DEFAULT INDEX ON t27_uniq; | ||
| > SELECT key FROM (SHOW INDEXES ON t27_uniq); | ||
| {id} | ||
|
|
||
| > SELECT create_sql LIKE '%EXCLUDE CONSTRAINTS%t27_uq_wallet%' FROM (SHOW CREATE TABLE t27_uniq); | ||
| true | ||
|
|
||
| $ sql-server-execute name=sql-server | ||
| ALTER TABLE t27_uniq DROP CONSTRAINT t27_uq_wallet; | ||
| INSERT INTO t27_uniq VALUES (3, 'a'); | ||
|
|
||
| > SELECT * FROM t27_uniq; | ||
| 1 a | ||
| 2 b | ||
| 3 a | ||
|
|
||
| # | ||
| # EXCLUDE ALL CONSTRAINTS: no keys, every column nullable. | ||
| # | ||
|
|
||
| > CREATE TABLE t27_all FROM SOURCE t27_source (REFERENCE t27_all) | ||
| WITH (EXCLUDE ALL CONSTRAINTS); | ||
|
|
||
| > CREATE DEFAULT INDEX ON t27_all; | ||
| > SELECT key FROM (SHOW INDEXES ON t27_all); | ||
| {id,email} | ||
|
|
||
| > SELECT name, nullable FROM mz_columns WHERE id = (SELECT id FROM mz_tables WHERE name = 't27_all'); | ||
| id true | ||
| email true | ||
|
|
||
| > SELECT * FROM t27_all; | ||
| 1 a@b.c | ||
|
|
||
| # Replication still works on both tables. | ||
| $ sql-server-execute name=sql-server | ||
| INSERT INTO t27_uniq VALUES (4, 'c'); | ||
| INSERT INTO t27_all VALUES (2, 'd@e.f'); | ||
|
|
||
| > SELECT * FROM t27_uniq; | ||
| 1 a | ||
| 2 b | ||
| 3 a | ||
| 4 c | ||
|
|
||
| > SELECT * FROM t27_all; | ||
| 1 a@b.c | ||
| 2 d@e.f | ||
|
|
||
| # | ||
| # An empty exclusion list behaves as if the option were omitted. | ||
| # | ||
|
|
||
| > CREATE TABLE t27_uniq_empty FROM SOURCE t27_source (REFERENCE t27_uniq) | ||
| WITH (EXCLUDE CONSTRAINTS ()); | ||
|
|
||
| > CREATE DEFAULT INDEX ON t27_uniq_empty; | ||
| > SELECT key FROM (SHOW INDEXES ON t27_uniq_empty); | ||
| {id} | ||
|
|
||
| > DROP TABLE t27_uniq_empty; | ||
|
|
||
| # | ||
| # A non-excluded UNIQUE constraint dropped upstream still stalls the table, | ||
| # with an error that names the constraint and the recovery workflow. | ||
| # | ||
|
|
||
| > CREATE TABLE t27_stall FROM SOURCE t27_source (REFERENCE t27_stall); | ||
|
|
||
| > SELECT * FROM t27_stall; | ||
| 1 a | ||
|
|
||
| $ sql-server-execute name=sql-server | ||
| ALTER TABLE t27_stall DROP CONSTRAINT t27_stall_uq; | ||
| INSERT INTO t27_stall VALUES (2, 'a'); | ||
|
|
||
| ! SELECT * FROM t27_stall; | ||
| contains:incompatible schema change on dbo.t27_stall: UNIQUE constraint "t27_stall_uq" (wallet) was dropped upstream | ||
| hint:WITH (EXCLUDE CONSTRAINTS ('t27_stall_uq')) before the upstream drop | ||
|
|
||
| # Recovery: a replacement table snapshots the current upstream schema, which no | ||
| # longer has the constraint, so no exclusion is needed. Then drop the errored | ||
| # table. | ||
| > CREATE TABLE t27_stall_v2 FROM SOURCE t27_source (REFERENCE t27_stall); | ||
|
|
||
| > SELECT * FROM t27_stall_v2; | ||
| 1 a | ||
| 2 a | ||
|
|
||
| > DROP TABLE t27_stall; | ||
|
|
||
| # | ||
| # Feature flag: the options are rejected when disabled. | ||
| # | ||
|
|
||
| $ postgres-execute connection=postgres://mz_system:materialize@${testdrive.materialize-internal-sql-addr} | ||
| ALTER SYSTEM SET enable_exclude_constraints_option = false | ||
|
|
||
| ! CREATE TABLE t27_flagged FROM SOURCE t27_source (REFERENCE t27_uniq) | ||
| WITH (EXCLUDE ALL CONSTRAINTS); | ||
| contains:not available | ||
|
|
||
| $ postgres-execute connection=postgres://mz_system:materialize@${testdrive.materialize-internal-sql-addr} | ||
| ALTER SYSTEM SET enable_exclude_constraints_option = true | ||
|
|
||
| > DROP SOURCE t27_source CASCADE; | ||
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.
delete this comment
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.
[peter's bot] Deleted. The claim was also no longer true after this round: SQL Server schema changes are now checked structurally against the live upstream schema rather than by scanning the DDL text, so an upstream NOT NULL drop on an EXCLUDE ALL CONSTRAINTS table is a non-event. See the self-review commit.