diff --git a/enginetest/queries/insert_queries.go b/enginetest/queries/insert_queries.go index 4e577cb3cd..0e898b4935 100644 --- a/enginetest/queries/insert_queries.go +++ b/enginetest/queries/insert_queries.go @@ -2836,7 +2836,7 @@ var InsertIgnoreScripts = []ScriptTest{ Assertions: []ScriptTestAssertion{ { Query: "insert into test_table values (1, 'invalid'), (2, 'comparative politics'), (3, null)", - ExpectedErr: types.ErrConvertingToEnum, // TODO: should be ErrDataTruncatedForColumn + ExpectedErr: types.ErrDataTruncatedForColumnAtRow, }, { Query: "insert ignore into test_table values (1, 'invalid'), (2, 'bye'), (3, null)", diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index 3d1efcfbef..be6a899ddb 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -1202,7 +1202,7 @@ CREATE TABLE tab3 ( { // enum values must match EXACTLY for case-sensitive collations Query: "INSERT INTO enumtest1 VALUES (10, 'ABC'), (11, 'aBc'), (12, 'xyz');", - ExpectedErrStr: "value ABC is not valid for this Enum", + ExpectedErrStr: "Data truncated for column 'e' at row 1", }, { Query: "SHOW CREATE TABLE enumtest1;", @@ -8053,11 +8053,11 @@ where Assertions: []ScriptTestAssertion{ { Query: "insert into t values (1, 500)", - ExpectedErrStr: "value 500 is not valid for this Enum", + ExpectedErrStr: "Data truncated for column 'e' at row 1", }, { Query: "insert into t values (1, -1)", - ExpectedErrStr: "value -1 is not valid for this Enum", + ExpectedErrStr: "Data truncated for column 'e' at row 1", }, }, }, @@ -9164,7 +9164,6 @@ where }, }, { - Skip: true, Name: "enums with foreign keys", Dialect: "mysql", SetUpScript: []string{ @@ -9207,7 +9206,7 @@ where }, { Query: "insert into child1 values (3);", - ExpectedErr: sql.ErrForeignKeyParentViolation, + ExpectedErr: sql.ErrForeignKeyChildViolation, }, { Query: "insert into child1 values ('x'), ('y');", @@ -9217,11 +9216,11 @@ where }, { Query: "insert into child1 values ('z');", - ExpectedErr: sql.ErrForeignKeyParentViolation, + ExpectedErr: sql.ErrForeignKeyChildViolation, }, { Query: "insert into child1 values ('a');", - ExpectedErrStr: "Data truncated for column 'e'", + ExpectedErrStr: "Data truncated for column 'e' at row 1", }, { Query: "select * from child1 order by e;", @@ -9247,7 +9246,7 @@ where }, { Query: "insert into child2 values (3);", - ExpectedErr: sql.ErrForeignKeyParentViolation, + ExpectedErr: sql.ErrForeignKeyChildViolation, }, { Query: "insert into child2 values ('c');", @@ -9257,14 +9256,14 @@ where }, { Query: "insert into child2 values ('a');", - ExpectedErr: sql.ErrForeignKeyParentViolation, + ExpectedErr: sql.ErrForeignKeyChildViolation, }, { Query: "select * from child2 order by e;", Expected: []sql.Row{ + {"b"}, {"c"}, {"c"}, - {"b"}, }, }, @@ -9282,7 +9281,7 @@ where }, { Query: "insert into child3 values (3);", - ExpectedErr: sql.ErrForeignKeyParentViolation, + ExpectedErr: sql.ErrForeignKeyChildViolation, }, { Query: "insert into child3 values ('x'), ('y');", @@ -9292,11 +9291,11 @@ where }, { Query: "insert into child3 values ('z');", - ExpectedErr: sql.ErrForeignKeyParentViolation, + ExpectedErr: sql.ErrForeignKeyChildViolation, }, { Query: "insert into child3 values ('a');", - ExpectedErr: sql.ErrForeignKeyParentViolation, + ExpectedErr: sql.ErrForeignKeyChildViolation, }, { Query: "select * from child3 order by e;", @@ -9322,7 +9321,7 @@ where }, { Query: "insert into child4 values (3);", - ExpectedErrStr: "Data truncated for column 'e'", + ExpectedErrStr: "Data truncated for column 'e' at row 1", }, { Query: "insert into child4 values ('q');", @@ -9332,7 +9331,7 @@ where }, { Query: "insert into child4 values ('a');", - ExpectedErrStr: "Data truncated for column 'e'", + ExpectedErrStr: "Data truncated for column 'e' at row 1", }, { Query: "select * from child4 order by e;", diff --git a/sql/plan/alter_foreign_key.go b/sql/plan/alter_foreign_key.go index 94e21638ec..c145f587c9 100644 --- a/sql/plan/alter_foreign_key.go +++ b/sql/plan/alter_foreign_key.go @@ -655,6 +655,10 @@ func foreignKeyComparableTypes(ctx *sql.Context, type1 sql.Type, type2 sql.Type) if type1String.Collation().CharacterSet() != type2String.Collation().CharacterSet() { return false } + case sqltypes.Enum: + // Enum types can reference each other in foreign keys regardless of their string values. + // MySQL allows enum foreign keys to match based on underlying numeric values. + return true default: return false } diff --git a/sql/rowexec/insert.go b/sql/rowexec/insert.go index 8130f2cc39..aba643ef98 100644 --- a/sql/rowexec/insert.go +++ b/sql/rowexec/insert.go @@ -49,6 +49,7 @@ type insertIter struct { firstGeneratedAutoIncRowIdx int deferredDefaults sql.FastIntSet + rowNumber int64 } func getInsertExpressions(values sql.Node) []sql.Expression { @@ -74,6 +75,9 @@ func (i *insertIter) Next(ctx *sql.Context) (returnRow sql.Row, returnErr error) return nil, i.ignoreOrClose(ctx, row, err) } + // Increment row number for error reporting (MySQL starts at 1) + i.rowNumber++ + // 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 // case the additional scope variables are prepended to the row. if len(row) > len(i.schema) { @@ -140,6 +144,8 @@ func (i *insertIter) Next(ctx *sql.Context) (returnRow sql.Row, returnErr error) cErr = types.ErrLengthBeyondLimit.New(row[idx], col.Name) } else if sql.ErrNotMatchingSRID.Is(cErr) { cErr = sql.ErrNotMatchingSRIDWithColName.New(col.Name, cErr) + } else if types.ErrConvertingToEnum.Is(cErr) { + cErr = types.ErrDataTruncatedForColumnAtRow.New(col.Name, i.rowNumber) } return nil, sql.NewWrappedInsertError(origRow, cErr) } diff --git a/sql/types/enum.go b/sql/types/enum.go index c01b0de0da..72c5be19a3 100644 --- a/sql/types/enum.go +++ b/sql/types/enum.go @@ -42,7 +42,8 @@ const ( var ( ErrConvertingToEnum = errors.NewKind("value %v is not valid for this Enum") - ErrDataTruncatedForColumn = errors.NewKind("Data truncated for column '%s'") + ErrDataTruncatedForColumn = errors.NewKind("Data truncated for column '%s'") + ErrDataTruncatedForColumnAtRow = errors.NewKind("Data truncated for column '%s' at row %d") enumValueType = reflect.TypeOf(uint16(0)) )