Repository navigation
sql: support EXCLUDE ALL NOT NULL CONSTRAINTS for MySQL sources - #39873
peterdukelarsen merged 3 commits into
Conversation
The option is parsed and gated for every source type but only the Postgres purifier applied it, so MySQL tables silently kept their NOT NULL constraints. Thread the flag through the MySQL purifier and mark every column nullable, matching the Postgres behavior. The unsupported source guard now also covers this option so other source types reject it instead of ignoring it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
QA LLM Review1. MEDIUM -- Retained primary-key columns lose their NULL guard, so a NULL id crash-loops clusterd
The new loop marks every column nullable in the stored DetailsThe NULL gets in when the source is behind while upstream briefly drops and restores the key. Reproduced on the PR's CI image. Upstream table ALTER TABLE t DROP PRIMARY KEY, MODIFY id INT NULL;
INSERT INTO t VALUES (NULL, 20);
UPDATE t SET id = 2 WHERE id IS NULL;
ALTER TABLE t MODIFY id INT NOT NULL, ADD PRIMARY KEY (id);After the replica is restored, clusterd panicked in Fix: leave retained primary-key columns as they are, so the stored desc matches what the planner declares. Making a PK column nullable upstream requires dropping the PK, which is already reported as if exclude_all_not_null_constraints {
+ let pk_cols: BTreeSet<&str> = parsed_table
+ .keys
+ .iter()
+ .filter(|k| k.is_primary)
+ .flat_map(|k| k.columns.iter().map(String::as_str))
+ .collect();
for c in &mut parsed_table.columns {
+ if pk_cols.contains(c.name.as_str()) {
+ continue;
+ }
if let Some(column_type) = &mut c.column_type {The Postgres path that this PR copies ( |
… NULL CONSTRAINTS The planner declares primary key columns NOT NULL in the RelationDesc regardless of the purified desc, so marking them nullable at purification disabled the decoder's NULL check for a column persist treats as non-nullable. MySQL schema verification compares keys by name and columns, so a key dropped and restored while the source was behind is not reported, and a NULL written in between panicked clusterd. The MySQL purifier now leaves columns of a retained primary key as they are. Adds the drop-and-restore scenario to the MySQL testdrive file. It panicked before this change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
00fc841 to
ed07b9b
Compare
| // in a column it treats as non-nullable, which panics. Schema | ||
| // verification compares keys by name and columns, so a key | ||
| // dropped and restored while the source was behind is not | ||
| // reported, and the NULL rows written in between do arrive. |
There was a problem hiding this comment.
the reason we need this to avoid panics for MySQL sources and not for Postgres sources is because of the detail that schemas are compared by name/column. Postgres has an OID that would differ and prevent this case.
There was a problem hiding this comment.
I vote to amend the comment.... with your comment!
|
I've addressed the LLM-review by leaving primary key not null constraints intact. |
martykulma
left a comment
There was a problem hiding this comment.
code looks good, some updates to the comments would be nice, but not blocking. thanks @peterdukelarsen!
| if pk_cols.contains(c.name.as_str()) { | ||
| continue; | ||
| } | ||
| if let Some(column_type) = &mut c.column_type { | ||
| column_type.nullable = true; | ||
| } |
There was a problem hiding this comment.
worth a comment to say why in PG we don't check PK cols, but here we do.
Closes SS-518
Motivation
#39680 added
EXCLUDE ALL NOT NULL CONSTRAINTStoCREATE TABLE .. FROM SOURCE. The parser, option plumbing, and feature flag gate are source-agnostic, but only the Postgres purifier applied it, so MySQL tables silently kept theirNOT NULLconstraints.Description
Thread the flag through the MySQL purifier and mark every column nullable, matching the Postgres behavior. The unsupported-source guard in
purify_create_table_from_sourcenow also covers this option, so source types without support reject it instead of ignoring it.Columns of a retained primary key stay NOT NULL. The planner declares them NOT NULL regardless of the purified desc, so marking them nullable disabled the decoder's NULL check for a column persist treats as non-nullable. MySQL schema verification compares keys by name and columns, so a key dropped and restored while the source was behind is not reported, and a NULL written in between panicked clusterd. Postgres compares keys by constraint OID, so a recreated key is reported before any such row is decoded, and its purifier is unchanged.
Verification
Extends
test/mysql-cdc/exclude-constraints.tdwith the same cases #39680 added for Postgres:SHOW CREATEroundtrip, index key, column nullability, the combination withEXCLUDE CONSTRAINTS ('PRIMARY'),DROP NOT NULLas a non-event, and PRIMARY KEY and UNIQUE drops still erroring with the hint. Also adds the drop-and-restore-while-paused scenario from the review, which panicked clusterd before the second commit.🤖 Generated with Claude Code