From 86819fd15e3d6022aeddf208fcfc826c91f5ad7e Mon Sep 17 00:00:00 2001 From: James Cor Date: Wed, 4 Sep 2024 11:54:51 -0700 Subject: [PATCH 1/4] determine tuple type for hash in comparator --- sql/expression/in.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/sql/expression/in.go b/sql/expression/in.go index d9c695fa42..811feba266 100644 --- a/sql/expression/in.go +++ b/sql/expression/in.go @@ -246,6 +246,29 @@ func newInMap(ctx *sql.Context, right Tuple, lType sql.Type) (map[uint64]sql.Exp return elements, hasNull, nil } +// getTupleCollation returns the collation to use for a tuple. +// If the tuple consists entirely of string types with the same collation, that collation is returned. +// Otherwise, Default collation is returned. +func getTupleCollation(tup types.TupleType) sql.CollationID { + coll := sql.Collation_Unspecified + for _, typ := range tup { + // if any element is not text, return default + if !types.IsTextOnly(typ) { + return sql.Collation_Default + } + // all text elements must have the same collation + c := typ.(sql.StringType).Collation() + if coll == sql.Collation_Unspecified { + coll = c + continue + } + if coll != c { + return sql.Collation_Default + } + } + return coll +} + func hashOfSimple(ctx *sql.Context, i interface{}, t sql.Type) (uint64, error) { if i == nil { return 0, nil @@ -265,6 +288,10 @@ func hashOfSimple(ctx *sql.Context, i interface{}, t sql.Type) (uint64, error) { str = converted.(string) } } else { + if types.IsTuple(t) { + coll = getTupleCollation(t.(types.TupleType)) + } + x, err := convertOrTruncate(ctx, i, t.Promote()) if err != nil { return 0, err From b4a7b774168a385d44664d653d265ae7c3ac43a7 Mon Sep 17 00:00:00 2001 From: James Cor Date: Wed, 4 Sep 2024 13:38:59 -0700 Subject: [PATCH 2/4] tests --- .../queries/information_schema_queries.go | 20 ++++++++++++++ enginetest/queries/script_queries.go | 27 +++++++++++++++++-- sql/expression/in.go | 18 +++++++++---- sql/expression/in_test.go | 17 ++++++++++++ 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/enginetest/queries/information_schema_queries.go b/enginetest/queries/information_schema_queries.go index 70a5258f8a..4b9f3aedc9 100644 --- a/enginetest/queries/information_schema_queries.go +++ b/enginetest/queries/information_schema_queries.go @@ -1775,6 +1775,26 @@ from information_schema.routines where routine_schema = 'mydb' and routine_type }, }, }, + { + Name: "information_schema.columns in expression uses info schema collation", + SetUpScript: []string{ + "create table TEST (COL int);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select table_schema, table_name, column_name table_comment from information_schema.columns where (table_name, column_name) in (('TEST', 'COL'));", + Expected: []sql.Row{ + {"mydb", "TEST", "COL"}, + }, + }, + { + Query: "select table_schema, table_name, column_name table_comment from information_schema.columns where (table_name, column_name) in (('test', 'col'));", + Expected: []sql.Row{ + {"mydb", "TEST", "COL"}, + }, + }, + }, + }, } var SkippedInfoSchemaScripts = []ScriptTest{ diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index 682ae79fd8..3ab723e893 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -7143,7 +7143,6 @@ where }, }, }, - { Name: "unix_timestamp script tests", SetUpScript: []string{ @@ -7174,7 +7173,6 @@ where }, }, }, - { Name: "name_const queries", SetUpScript: []string{ @@ -7258,6 +7256,31 @@ where }, }, }, + { + Name: "mismatched collation using hash in tuples", + SetUpScript: []string{ + "create table t (t1 text collate utf8mb4_0900_bin, t2 text collate utf8mb4_0900_ai_ci)", + "insert into t values ('ABC', 'DEF')", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select * from t where (t1, t2) in (('ABC', 'DEF'));", + Expected: []sql.Row{ + {"ABC", "DEF"}, + }, + }, + { + Query: "select * from t where (t1, t2) in (('ABC', 'def'));", + Expected: []sql.Row{ + {"ABC", "DEF"}, + }, + }, + { + Query: "select * from t where (t1, t2) in (('abc', 'DEF'));", + Expected: []sql.Row{}, + }, + }, + }, } var SpatialScriptTests = []ScriptTest{ diff --git a/sql/expression/in.go b/sql/expression/in.go index 811feba266..2161549895 100644 --- a/sql/expression/in.go +++ b/sql/expression/in.go @@ -276,7 +276,19 @@ func hashOfSimple(ctx *sql.Context, i interface{}, t sql.Type) (uint64, error) { var str string coll := sql.Collation_Default - if types.IsTextOnly(t) { + if types.IsTuple(t) { + tup := i.([]interface{}) + tupType := t.(types.TupleType) + hashes := make([]uint64, len(tup)) + for idx, v := range tup { + h, err := hashOfSimple(ctx, v, tupType[idx]) + if err != nil { + return 0, err + } + hashes[idx] = h + } + str = fmt.Sprintf("%v", hashes) + } else if types.IsTextOnly(t) { coll = t.(sql.StringType).Collation() if s, ok := i.(string); ok { str = s @@ -288,10 +300,6 @@ func hashOfSimple(ctx *sql.Context, i interface{}, t sql.Type) (uint64, error) { str = converted.(string) } } else { - if types.IsTuple(t) { - coll = getTupleCollation(t.(types.TupleType)) - } - x, err := convertOrTruncate(ctx, i, t.Promote()) if err != nil { return 0, err diff --git a/sql/expression/in_test.go b/sql/expression/in_test.go index 3f1267e72e..d3b0b41f61 100644 --- a/sql/expression/in_test.go +++ b/sql/expression/in_test.go @@ -414,6 +414,23 @@ func TestHashInTuple(t *testing.T) { nil, nil, }, + { + "heterogeneous collations with nested", + expression.NewTuple( + expression.NewLiteral("ABC", types.MustCreateString(sqltypes.VarChar, 20, sql.Collation_Default)), + expression.NewLiteral("def", types.MustCreateString(sqltypes.VarChar, 20, sql.Collation_utf8mb4_0900_ai_ci)), + ), + expression.NewTuple( + expression.NewTuple( + expression.NewLiteral("ABC", types.MustCreateString(sqltypes.VarChar, 20, sql.Collation_Default)), + expression.NewLiteral("DEF", types.MustCreateString(sqltypes.VarChar, 20, sql.Collation_Default)), + ), + ), + nil, + true, + nil, + nil, + }, { "left get field tuple is in right", expression.NewTuple( From 347a9529e57a6c85c45cf726a3398f9c1e72e1ab Mon Sep 17 00:00:00 2001 From: James Cor Date: Wed, 4 Sep 2024 13:39:22 -0700 Subject: [PATCH 3/4] remove unused --- sql/expression/in.go | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/sql/expression/in.go b/sql/expression/in.go index 2161549895..f648f9816f 100644 --- a/sql/expression/in.go +++ b/sql/expression/in.go @@ -246,29 +246,6 @@ func newInMap(ctx *sql.Context, right Tuple, lType sql.Type) (map[uint64]sql.Exp return elements, hasNull, nil } -// getTupleCollation returns the collation to use for a tuple. -// If the tuple consists entirely of string types with the same collation, that collation is returned. -// Otherwise, Default collation is returned. -func getTupleCollation(tup types.TupleType) sql.CollationID { - coll := sql.Collation_Unspecified - for _, typ := range tup { - // if any element is not text, return default - if !types.IsTextOnly(typ) { - return sql.Collation_Default - } - // all text elements must have the same collation - c := typ.(sql.StringType).Collation() - if coll == sql.Collation_Unspecified { - coll = c - continue - } - if coll != c { - return sql.Collation_Default - } - } - return coll -} - func hashOfSimple(ctx *sql.Context, i interface{}, t sql.Type) (uint64, error) { if i == nil { return 0, nil From 39cf0a81fd9efe8ddbaa1c13b6b778c2eba88feb Mon Sep 17 00:00:00 2001 From: jycor Date: Wed, 4 Sep 2024 20:41:35 +0000 Subject: [PATCH 4/4] [ga-format-pr] Run ./format_repo.sh to fix formatting --- enginetest/queries/script_queries.go | 6 +++--- sql/expression/in_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index 3ab723e893..476ebcbfad 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -7262,7 +7262,7 @@ where "create table t (t1 text collate utf8mb4_0900_bin, t2 text collate utf8mb4_0900_ai_ci)", "insert into t values ('ABC', 'DEF')", }, - Assertions: []ScriptTestAssertion{ + Assertions: []ScriptTestAssertion{ { Query: "select * from t where (t1, t2) in (('ABC', 'DEF'));", Expected: []sql.Row{ @@ -7276,10 +7276,10 @@ where }, }, { - Query: "select * from t where (t1, t2) in (('abc', 'DEF'));", + Query: "select * from t where (t1, t2) in (('abc', 'DEF'));", Expected: []sql.Row{}, }, - }, + }, }, } diff --git a/sql/expression/in_test.go b/sql/expression/in_test.go index d3b0b41f61..af3bef9592 100644 --- a/sql/expression/in_test.go +++ b/sql/expression/in_test.go @@ -417,8 +417,8 @@ func TestHashInTuple(t *testing.T) { { "heterogeneous collations with nested", expression.NewTuple( - expression.NewLiteral("ABC", types.MustCreateString(sqltypes.VarChar, 20, sql.Collation_Default)), - expression.NewLiteral("def", types.MustCreateString(sqltypes.VarChar, 20, sql.Collation_utf8mb4_0900_ai_ci)), + expression.NewLiteral("ABC", types.MustCreateString(sqltypes.VarChar, 20, sql.Collation_Default)), + expression.NewLiteral("def", types.MustCreateString(sqltypes.VarChar, 20, sql.Collation_utf8mb4_0900_ai_ci)), ), expression.NewTuple( expression.NewTuple(