Skip to content

Commit 97f03f3

Browse files
authored
Merge pull request #3094 from dolthub/elianddb/9496-decimal-foreign-keys
dolthub/dolt#9496 - Fix DECIMAL foreign key constraint validation to match MySQL behavior
2 parents bdc01a7 + 774726d commit 97f03f3

File tree

3 files changed

+72
-12
lines changed

3 files changed

+72
-12
lines changed

enginetest/queries/script_queries.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10508,6 +10508,8 @@ where
1050810508
SetUpScript: []string{
1050910509
"create table parent (d decimal(4, 2) primary key);",
1051010510
"insert into parent values (1.23), (45.67), (78.9);",
10511+
"create table parent_multi (d1 decimal(4,2), d2 decimal(3,1), primary key (d1, d2));",
10512+
"insert into parent_multi values (1.23, 4.5), (45.67, 78.9), (99.99, 0.1);",
1051110513
},
1051210514
Assertions: []ScriptTestAssertion{
1051310515
{
@@ -10528,43 +10530,60 @@ where
1052810530
{types.NewOkResult(3)},
1052910531
},
1053010532
},
10531-
1053210533
{
10533-
Skip: true,
10534+
Query: "insert into child_dec_4_2 values (99.99);",
10535+
ExpectedErr: sql.ErrForeignKeyChildViolation,
10536+
},
10537+
{
1053410538
Query: "create table child_dec_4_1 (d decimal(4,1), foreign key (d) references parent (d));",
1053510539
Expected: []sql.Row{
1053610540
{types.NewOkResult(0)},
1053710541
},
1053810542
},
1053910543
{
10540-
Skip: true,
1054110544
Query: "insert into child_dec_4_1 values (78.9);",
10542-
ExpectedErr: sql.ErrForeignKeyParentViolation,
10545+
ExpectedErr: sql.ErrForeignKeyChildViolation,
10546+
},
10547+
{
10548+
Query: "insert into child_dec_4_1 values (99.9);",
10549+
ExpectedErr: sql.ErrForeignKeyChildViolation,
1054310550
},
10544-
1054510551
{
10546-
Skip: true,
1054710552
Query: "create table child_dec_3_2 (d decimal(3,2), foreign key (d) references parent (d));",
1054810553
Expected: []sql.Row{
1054910554
{types.NewOkResult(0)},
1055010555
},
1055110556
},
1055210557
{
10553-
Skip: true,
10554-
Query: "insert into child_dec_3_2 values (1.23);",
10555-
ExpectedErr: sql.ErrForeignKeyParentViolation,
10558+
Query: "insert into child_dec_3_2 values (1.23);",
10559+
Expected: []sql.Row{
10560+
{types.NewOkResult(1)},
10561+
},
1055610562
},
1055710563
{
10558-
Skip: true,
1055910564
Query: "create table child_dec_65_30 (d decimal(65,30), foreign key (d) references parent (d));",
1056010565
Expected: []sql.Row{
1056110566
{types.NewOkResult(0)},
1056210567
},
1056310568
},
1056410569
{
10565-
Skip: true,
1056610570
Query: "insert into child_dec_65_30 values (1.23);",
10567-
ExpectedErr: sql.ErrForeignKeyParentViolation,
10571+
ExpectedErr: sql.ErrForeignKeyChildViolation,
10572+
},
10573+
{
10574+
Query: "create table child_multi_4_2_3_1 (d1 decimal(4,2), d2 decimal(3,1), foreign key (d1, d2) references parent_multi (d1, d2));",
10575+
Expected: []sql.Row{
10576+
{types.NewOkResult(0)},
10577+
}},
10578+
{
10579+
Query: "insert into child_multi_4_2_3_1 values (1.23, 4.5), (45.67, 78.9), (NULL, NULL);",
10580+
Expected: []sql.Row{
10581+
{types.NewOkResult(3)},
10582+
},
10583+
},
10584+
{
10585+
Query: "insert into child_multi_4_2_3_1 values (1.23, 9.9);",
10586+
ExpectedErr: sql.ErrForeignKeyChildViolation,
1056810587
},
1056910588
},
1057010589
},

sql/plan/alter_foreign_key.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,10 @@ func foreignKeyComparableTypes(ctx *sql.Context, type1 sql.Type, type2 sql.Type)
659659
// Enum types can reference each other in foreign keys regardless of their string values.
660660
// MySQL allows enum foreign keys to match based on underlying numeric values.
661661
return true
662+
case sqltypes.Decimal:
663+
// MySQL allows decimal foreign keys with different precision/scale
664+
// The foreign key constraint validation will handle the actual value comparison
665+
return true
662666
default:
663667
return false
664668
}

sql/plan/foreign_key_editor.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,10 @@ func (reference *ForeignKeyReferenceHandler) CheckReference(ctx *sql.Context, ro
512512
return err
513513
}
514514
if err == nil {
515+
// We have a parent row, but check for type-specific validation
516+
if validationErr := reference.validateDecimalConstraints(row); validationErr != nil {
517+
return validationErr
518+
}
515519
// We have a parent row so throw no error
516520
return nil
517521
}
@@ -539,6 +543,39 @@ func (reference *ForeignKeyReferenceHandler) CheckReference(ctx *sql.Context, ro
539543
reference.ForeignKey.ParentTable, reference.RowMapper.GetKeyString(row))
540544
}
541545

546+
// validateDecimalConstraints checks that decimal foreign key columns have compatible scales.
547+
func (reference *ForeignKeyReferenceHandler) validateDecimalConstraints(row sql.Row) error {
548+
if reference.RowMapper.Index == nil {
549+
return nil
550+
}
551+
indexColumnTypes := reference.RowMapper.Index.ColumnExpressionTypes()
552+
for parentIdx, parentCol := range indexColumnTypes {
553+
if parentIdx >= len(reference.RowMapper.IndexPositions) {
554+
break
555+
}
556+
parentType := parentCol.Type
557+
childColIdx := reference.RowMapper.IndexPositions[parentIdx]
558+
childType := reference.RowMapper.SourceSch[childColIdx].Type
559+
childDecimal, ok := childType.(sql.DecimalType)
560+
if !ok {
561+
continue
562+
}
563+
parentDecimal, ok := parentType.(sql.DecimalType)
564+
if !ok {
565+
continue
566+
}
567+
if childDecimal.Scale() != parentDecimal.Scale() {
568+
return sql.ErrForeignKeyChildViolation.New(
569+
reference.ForeignKey.Name,
570+
reference.ForeignKey.Table,
571+
reference.ForeignKey.ParentTable,
572+
reference.RowMapper.GetKeyString(row),
573+
)
574+
}
575+
}
576+
return nil
577+
}
578+
542579
// CheckTable checks that every row in the table has an index entry in the referenced table.
543580
func (reference *ForeignKeyReferenceHandler) CheckTable(ctx *sql.Context, tbl sql.ForeignKeyTable) error {
544581
partIter, err := tbl.Partitions(ctx)

0 commit comments

Comments
 (0)