-
-
Notifications
You must be signed in to change notification settings - Fork 238
dolthub/dolt#9496 - Fix DECIMAL foreign key constraint validation to match MySQL behavior #3094
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
Conversation
df4ab3e to
b35e54d
Compare
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.
I just decided to iteratively update error messages as I work on them. A mysql ver suffix seems to be the easiest way to differentiate error messages since it communicates where it comes from. This lessens the number of conflicts with existing test cases, and is targeted mainly to the issue at hand.
5b50c84 to
27c16cf
Compare
jycor
left a 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.
LGTM
enginetest/queries/script_queries.go
Outdated
| ExpectedErr: sql.ErrForeignKeyChildViolation, | ||
| }, | ||
| { | ||
| Query: "insert into child_dec_4_2 values (99.99);", |
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.
Small nitpick.
Move this test to be grouped up with other inserts on the same table
| // validateDecimalConstraints checks that decimal foreign key columns have compatible scales. | ||
| func (reference *ForeignKeyReferenceHandler) validateDecimalConstraints(row sql.Row) error { | ||
| if reference.RowMapper.Index == nil { | ||
| return nil | ||
| } | ||
|
|
||
| indexColumnTypes := reference.RowMapper.Index.ColumnExpressionTypes() | ||
| for i := range reference.ForeignKey.Columns { | ||
| if i >= len(indexColumnTypes) { | ||
| continue | ||
| } | ||
|
|
||
| childColIdx := reference.RowMapper.IndexPositions[i] | ||
| childType := reference.RowMapper.SourceSch[childColIdx].Type | ||
| parentType := indexColumnTypes[i].Type | ||
|
|
||
| // For decimal types, check scale compatibility (following existing pattern for type-specific validation) | ||
| if childDecimal, ok := childType.(sql.DecimalType); ok { | ||
| if parentDecimal, ok := parentType.(sql.DecimalType); ok { | ||
| if childDecimal.Scale() != parentDecimal.Scale() { | ||
| return sql.ErrForeignKeyChildViolation.New(reference.ForeignKey.Name, reference.ForeignKey.Table, | ||
| reference.ForeignKey.ParentTable, reference.RowMapper.GetKeyString(row)) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
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.
| // validateDecimalConstraints checks that decimal foreign key columns have compatible scales. | |
| func (reference *ForeignKeyReferenceHandler) validateDecimalConstraints(row sql.Row) error { | |
| if reference.RowMapper.Index == nil { | |
| return nil | |
| } | |
| indexColumnTypes := reference.RowMapper.Index.ColumnExpressionTypes() | |
| for i := range reference.ForeignKey.Columns { | |
| if i >= len(indexColumnTypes) { | |
| continue | |
| } | |
| childColIdx := reference.RowMapper.IndexPositions[i] | |
| childType := reference.RowMapper.SourceSch[childColIdx].Type | |
| parentType := indexColumnTypes[i].Type | |
| // For decimal types, check scale compatibility (following existing pattern for type-specific validation) | |
| if childDecimal, ok := childType.(sql.DecimalType); ok { | |
| if parentDecimal, ok := parentType.(sql.DecimalType); ok { | |
| if childDecimal.Scale() != parentDecimal.Scale() { | |
| return sql.ErrForeignKeyChildViolation.New(reference.ForeignKey.Name, reference.ForeignKey.Table, | |
| reference.ForeignKey.ParentTable, reference.RowMapper.GetKeyString(row)) | |
| } | |
| } | |
| } | |
| } | |
| return nil | |
| } | |
| // validateDecimalConstraints checks that decimal foreign key columns have compatible scales. | |
| func (reference *ForeignKeyReferenceHandler) validateDecimalConstraints(row sql.Row) error { | |
| if reference.RowMapper.Index == nil { | |
| return nil | |
| } | |
| indexColumnTypes := reference.RowMapper.Index.ColumnExpressionTypes() | |
| for parentIdx, parentCol := range indexColumnTypes { | |
| if parentIdx >= len(indexColumnTypes) { | |
| break | |
| } | |
| parentType := parentCol.Type | |
| childColIdx := reference.RowMapper.IndexPositions[parentIdx] | |
| childType := reference.RowMapper.SourceSch[childColIdx].Type | |
| childDecimal, ok := childType.(sql.DecimalType) | |
| if !ok { | |
| continue | |
| } | |
| parentDecimal, ok := parentType.(sql.DecimalType) | |
| if !ok { | |
| continue | |
| } | |
| if childDecimal.Scale() != parentDecimal.Scale() { | |
| return sql.ErrForeignKeyChildViolation.New( | |
| reference.ForeignKey.Name, | |
| reference.ForeignKey.Table, | |
| reference.ForeignKey.ParentTable, | |
| reference.RowMapper.GetKeyString(row), | |
| ) | |
| } | |
| } | |
| return nil | |
| } | |
| ExpectedErr: sql.ErrForeignKeyParentViolation, | ||
| Query: "insert into child_dec_3_2 values (1.23);", | ||
| Expected: []sql.Row{ | ||
| {types.NewOkResult(1)}, |
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.
nice catch
Allow foreign key references between DECIMAL columns with different precision/scale to match MySQL behavior. - Modified foreignKeyComparableTypes() to allow DECIMAL types with different precision/scale - Updated test expectations to reflect correct behavior with current decimal implementation - All foreign key tests pass Fixes dolthub/dolt#9496 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Added DECIMAL foreign key compatibility test suite to foreign_key_queries.go - Tests foreign key creation between DECIMAL columns with different precision/scale - Tests constraint validation with both valid and invalid inserts - Validates that the fix works for various DECIMAL type combinations
- Remove verbose comments and dead code - Reduce nesting in shouldRejectDecimalMatch function - Use existing patterns from codebase - Remove unnecessary vitess import - Simplify type checking logic 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Update ErrForeignKeyChildViolation error message to match MySQL format exactly - Include database name, table name, constraint name, and column names - Error now shows: "cannot add or update a child row: a foreign key constraint fails (`db`.`table`, CONSTRAINT `name` FOREIGN KEY (`cols`) REFERENCES `parent` (`parent_cols`))" - Maintains functional behavior while providing MySQL-compatible error messages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
e2ede1e to
774726d
Compare
Fixes dolthub/dolt#9496
Allow DECIMAL foreign key creation with different precision/scale but enforce strict constraint validation
MySQL allows DECIMAL foreign keys with different precision/scale but rejects constraint violations based on exact scale matching