Skip to content

Commit c5c4097

Browse files
authored
Merge pull request #3074 from dolthub/elianddb/9424-enum-foreign-keys
dolthub/dolt#9424 - Fix enum foreign key constraints to match MySQL behavior
2 parents 53f1886 + 511dd90 commit c5c4097

File tree

5 files changed

+27
-17
lines changed

5 files changed

+27
-17
lines changed

enginetest/queries/insert_queries.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2836,7 +2836,7 @@ var InsertIgnoreScripts = []ScriptTest{
28362836
Assertions: []ScriptTestAssertion{
28372837
{
28382838
Query: "insert into test_table values (1, 'invalid'), (2, 'comparative politics'), (3, null)",
2839-
ExpectedErr: types.ErrConvertingToEnum, // TODO: should be ErrDataTruncatedForColumn
2839+
ExpectedErr: types.ErrDataTruncatedForColumnAtRow,
28402840
},
28412841
{
28422842
Query: "insert ignore into test_table values (1, 'invalid'), (2, 'bye'), (3, null)",

enginetest/queries/script_queries.go

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,7 @@ CREATE TABLE tab3 (
12021202
{
12031203
// enum values must match EXACTLY for case-sensitive collations
12041204
Query: "INSERT INTO enumtest1 VALUES (10, 'ABC'), (11, 'aBc'), (12, 'xyz');",
1205-
ExpectedErrStr: "value ABC is not valid for this Enum",
1205+
ExpectedErrStr: "Data truncated for column 'e' at row 1",
12061206
},
12071207
{
12081208
Query: "SHOW CREATE TABLE enumtest1;",
@@ -8053,11 +8053,11 @@ where
80538053
Assertions: []ScriptTestAssertion{
80548054
{
80558055
Query: "insert into t values (1, 500)",
8056-
ExpectedErrStr: "value 500 is not valid for this Enum",
8056+
ExpectedErrStr: "Data truncated for column 'e' at row 1",
80578057
},
80588058
{
80598059
Query: "insert into t values (1, -1)",
8060-
ExpectedErrStr: "value -1 is not valid for this Enum",
8060+
ExpectedErrStr: "Data truncated for column 'e' at row 1",
80618061
},
80628062
},
80638063
},
@@ -9164,7 +9164,6 @@ where
91649164
},
91659165
},
91669166
{
9167-
Skip: true,
91689167
Name: "enums with foreign keys",
91699168
Dialect: "mysql",
91709169
SetUpScript: []string{
@@ -9207,7 +9206,7 @@ where
92079206
},
92089207
{
92099208
Query: "insert into child1 values (3);",
9210-
ExpectedErr: sql.ErrForeignKeyParentViolation,
9209+
ExpectedErr: sql.ErrForeignKeyChildViolation,
92119210
},
92129211
{
92139212
Query: "insert into child1 values ('x'), ('y');",
@@ -9217,11 +9216,11 @@ where
92179216
},
92189217
{
92199218
Query: "insert into child1 values ('z');",
9220-
ExpectedErr: sql.ErrForeignKeyParentViolation,
9219+
ExpectedErr: sql.ErrForeignKeyChildViolation,
92219220
},
92229221
{
92239222
Query: "insert into child1 values ('a');",
9224-
ExpectedErrStr: "Data truncated for column 'e'",
9223+
ExpectedErrStr: "Data truncated for column 'e' at row 1",
92259224
},
92269225
{
92279226
Query: "select * from child1 order by e;",
@@ -9247,7 +9246,7 @@ where
92479246
},
92489247
{
92499248
Query: "insert into child2 values (3);",
9250-
ExpectedErr: sql.ErrForeignKeyParentViolation,
9249+
ExpectedErr: sql.ErrForeignKeyChildViolation,
92519250
},
92529251
{
92539252
Query: "insert into child2 values ('c');",
@@ -9257,14 +9256,14 @@ where
92579256
},
92589257
{
92599258
Query: "insert into child2 values ('a');",
9260-
ExpectedErr: sql.ErrForeignKeyParentViolation,
9259+
ExpectedErr: sql.ErrForeignKeyChildViolation,
92619260
},
92629261
{
92639262
Query: "select * from child2 order by e;",
92649263
Expected: []sql.Row{
9264+
{"b"},
92659265
{"c"},
92669266
{"c"},
9267-
{"b"},
92689267
},
92699268
},
92709269

@@ -9282,7 +9281,7 @@ where
92829281
},
92839282
{
92849283
Query: "insert into child3 values (3);",
9285-
ExpectedErr: sql.ErrForeignKeyParentViolation,
9284+
ExpectedErr: sql.ErrForeignKeyChildViolation,
92869285
},
92879286
{
92889287
Query: "insert into child3 values ('x'), ('y');",
@@ -9292,11 +9291,11 @@ where
92929291
},
92939292
{
92949293
Query: "insert into child3 values ('z');",
9295-
ExpectedErr: sql.ErrForeignKeyParentViolation,
9294+
ExpectedErr: sql.ErrForeignKeyChildViolation,
92969295
},
92979296
{
92989297
Query: "insert into child3 values ('a');",
9299-
ExpectedErr: sql.ErrForeignKeyParentViolation,
9298+
ExpectedErr: sql.ErrForeignKeyChildViolation,
93009299
},
93019300
{
93029301
Query: "select * from child3 order by e;",
@@ -9322,7 +9321,7 @@ where
93229321
},
93239322
{
93249323
Query: "insert into child4 values (3);",
9325-
ExpectedErrStr: "Data truncated for column 'e'",
9324+
ExpectedErrStr: "Data truncated for column 'e' at row 1",
93269325
},
93279326
{
93289327
Query: "insert into child4 values ('q');",
@@ -9332,7 +9331,7 @@ where
93329331
},
93339332
{
93349333
Query: "insert into child4 values ('a');",
9335-
ExpectedErrStr: "Data truncated for column 'e'",
9334+
ExpectedErrStr: "Data truncated for column 'e' at row 1",
93369335
},
93379336
{
93389337
Query: "select * from child4 order by e;",

sql/plan/alter_foreign_key.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,10 @@ func foreignKeyComparableTypes(ctx *sql.Context, type1 sql.Type, type2 sql.Type)
655655
if type1String.Collation().CharacterSet() != type2String.Collation().CharacterSet() {
656656
return false
657657
}
658+
case sqltypes.Enum:
659+
// Enum types can reference each other in foreign keys regardless of their string values.
660+
// MySQL allows enum foreign keys to match based on underlying numeric values.
661+
return true
658662
default:
659663
return false
660664
}

sql/rowexec/insert.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type insertIter struct {
4949
firstGeneratedAutoIncRowIdx int
5050

5151
deferredDefaults sql.FastIntSet
52+
rowNumber int64
5253
}
5354

5455
func getInsertExpressions(values sql.Node) []sql.Expression {
@@ -74,6 +75,9 @@ func (i *insertIter) Next(ctx *sql.Context) (returnRow sql.Row, returnErr error)
7475
return nil, i.ignoreOrClose(ctx, row, err)
7576
}
7677

78+
// Increment row number for error reporting (MySQL starts at 1)
79+
i.rowNumber++
80+
7781
// Prune the row down to the size of the schema. It can be larger in the case of running with an outer scope, in which
7882
// case the additional scope variables are prepended to the row.
7983
if len(row) > len(i.schema) {
@@ -140,6 +144,8 @@ func (i *insertIter) Next(ctx *sql.Context) (returnRow sql.Row, returnErr error)
140144
cErr = types.ErrLengthBeyondLimit.New(row[idx], col.Name)
141145
} else if sql.ErrNotMatchingSRID.Is(cErr) {
142146
cErr = sql.ErrNotMatchingSRIDWithColName.New(col.Name, cErr)
147+
} else if types.ErrConvertingToEnum.Is(cErr) {
148+
cErr = types.ErrDataTruncatedForColumnAtRow.New(col.Name, i.rowNumber)
143149
}
144150
return nil, sql.NewWrappedInsertError(origRow, cErr)
145151
}

sql/types/enum.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ const (
4242
var (
4343
ErrConvertingToEnum = errors.NewKind("value %v is not valid for this Enum")
4444

45-
ErrDataTruncatedForColumn = errors.NewKind("Data truncated for column '%s'")
45+
ErrDataTruncatedForColumn = errors.NewKind("Data truncated for column '%s'")
46+
ErrDataTruncatedForColumnAtRow = errors.NewKind("Data truncated for column '%s' at row %d")
4647

4748
enumValueType = reflect.TypeOf(uint16(0))
4849
)

0 commit comments

Comments
 (0)