Skip to content

Commit 0c1980e

Browse files
author
James Cor
committed
implement double truncation and find correct hash comparison type
1 parent f485924 commit 0c1980e

File tree

5 files changed

+87
-6
lines changed

5 files changed

+87
-6
lines changed

enginetest/queries/join_queries.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,37 @@ var JoinScriptTests = []ScriptTest{
12001200
},
12011201
},
12021202
},
1203+
{
1204+
Name: "HashLookup with type int8 and string type conversions",
1205+
SetUpScript: []string{
1206+
"create table t1 (c1 boolean);",
1207+
"create table t2 (c2 varchar(500));",
1208+
"insert into t1 values (true), (false);",
1209+
"insert into t2 values ('abc'), ('def');", // will be converted to float64(0) and match false
1210+
"insert into t2 values ('1asdf');", // will be converted to '1' and match true
1211+
"insert into t2 values ('5five');", // will be converted to '5' and match nothing
1212+
},
1213+
Assertions: []ScriptTestAssertion{
1214+
{
1215+
// TODO: our warnings don't align with MySQL
1216+
Query: "select /*+ HASH_JOIN(t1, t2) */ * from t1 join t2 where c1 = c2 order by c1, c2;",
1217+
Expected: []sql.Row{
1218+
{0, "abc"},
1219+
{0, "def"},
1220+
{1, "1asdf"},
1221+
},
1222+
},
1223+
{
1224+
// TODO: our warnings don't align with MySQL
1225+
Query: "select /*+ INNER_JOIN(t1, t2) */ * from t1 join t2 where c1 = c2 order by c1, c2;",
1226+
Expected: []sql.Row{
1227+
{0, "abc"},
1228+
{0, "def"},
1229+
{1, "1asdf"},
1230+
},
1231+
},
1232+
},
1233+
},
12031234
}
12041235

12051236
var LateralJoinScriptTests = []ScriptTest{

sql/errors.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,10 @@ var (
947947
ErrColumnSpecifiedTwice = errors.NewKind("column '%v' specified twice")
948948

949949
ErrEnumTypeTruncated = errors.NewKind("new enum type change truncates value")
950+
951+
// ErrDoubleTypeTruncated is thrown when trying to convert a bad value to a double
952+
// TODO: MySQL's exact error message is "Data truncated for column '%s' at row %d"
953+
ErrDoubleTypeTruncated = errors.NewKind("data truncated for '%v'")
950954
)
951955

952956
// CastSQLError returns a *mysql.SQLError with the error code and in some cases, also a SQL state, populated for the

sql/expression/convert.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,10 @@ func convertValue(ctx *sql.Context, val interface{}, castTo string, originType s
372372
}
373373
d, _, err := types.Float64.Convert(ctx, value)
374374
if err != nil {
375+
if sql.ErrDoubleTypeTruncated.Is(err) {
376+
ctx.Warn(1265, err.Error())
377+
return d, nil
378+
}
375379
return types.Float64.Zero(), nil
376380
}
377381
return d, nil

sql/plan/hash_lookup.go

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ import (
3434
// simply delegates to the child.
3535
func NewHashLookup(n sql.Node, rightEntryKey sql.Expression, leftProbeKey sql.Expression, joinType JoinType) *HashLookup {
3636
leftKeySch := hash.ExprsToSchema(leftProbeKey)
37+
compareType := GetCompareType(leftProbeKey.Type(), rightEntryKey.Type())
3738
return &HashLookup{
3839
UnaryNode: UnaryNode{n},
3940
RightEntryKey: rightEntryKey,
4041
LeftProbeKey: leftProbeKey,
42+
CompareType: compareType,
4143
Mutex: new(sync.Mutex),
4244
JoinType: joinType,
4345
leftKeySch: leftKeySch,
@@ -48,6 +50,7 @@ type HashLookup struct {
4850
UnaryNode
4951
RightEntryKey sql.Expression
5052
LeftProbeKey sql.Expression
53+
CompareType sql.Type
5154
Mutex *sync.Mutex
5255
Lookup *map[interface{}][]sql.Row
5356
leftKeySch sql.Schema
@@ -58,6 +61,45 @@ var _ sql.Node = (*HashLookup)(nil)
5861
var _ sql.Expressioner = (*HashLookup)(nil)
5962
var _ sql.CollationCoercible = (*HashLookup)(nil)
6063

64+
// GetCompareType returns the type to use when comparing values of types left and right.
65+
func GetCompareType(left, right sql.Type) sql.Type {
66+
// TODO: much of this logic is very similar to castLeftAndRight() from sql/expression/comparison.go
67+
if left.Equals(right) {
68+
return left
69+
}
70+
if types.IsTuple(left) && types.IsTuple(right) {
71+
return left
72+
}
73+
if types.IsTime(left) || types.IsTime(right) {
74+
return types.DatetimeMaxPrecision
75+
}
76+
if types.IsJSON(left) || types.IsJSON(right) {
77+
return types.JSON
78+
}
79+
if types.IsBinaryType(left) || types.IsBinaryType(right) {
80+
return types.LongBlob
81+
}
82+
if types.IsNumber(left) || types.IsNumber(right) {
83+
if types.IsDecimal(left) {
84+
return left
85+
}
86+
if types.IsDecimal(right) {
87+
return right
88+
}
89+
if types.IsFloat(left) || types.IsFloat(right) {
90+
return types.Float64
91+
}
92+
if types.IsSigned(left) && types.IsSigned(right) {
93+
return types.Int64
94+
}
95+
if types.IsUnsigned(left) && types.IsUnsigned(right) {
96+
return types.Uint64
97+
}
98+
return types.Float64
99+
}
100+
return types.LongText
101+
}
102+
61103
func (n *HashLookup) Expressions() []sql.Expression {
62104
return []sql.Expression{n.RightEntryKey, n.LeftProbeKey}
63105
}
@@ -113,7 +155,7 @@ func (n *HashLookup) CollationCoercibility(ctx *sql.Context) (collation sql.Coll
113155
return sql.GetCoercibility(ctx, n.Child)
114156
}
115157

116-
// Convert a tuple expression returning []interface{} into something comparable.
158+
// GetHashKey converts a tuple expression returning []interface{} into something comparable.
117159
// Fast paths a few smaller slices into fixed size arrays, puts everything else
118160
// through string serialization and a hash for now. It is OK to hash lossy here
119161
// as the join condition is still evaluated after the matching rows are returned.
@@ -122,13 +164,13 @@ func (n *HashLookup) GetHashKey(ctx *sql.Context, e sql.Expression, row sql.Row)
122164
if err != nil {
123165
return nil, err
124166
}
125-
typ := n.LeftProbeKey.Type()
126-
key, _, err = typ.Convert(ctx, key)
167+
key, _, err = n.CompareType.Convert(ctx, key)
127168
if types.ErrValueNotNil.Is(err) {
128169
// The LHS expression was NullType. This is allowed.
129170
return nil, nil
130171
}
131-
if err != nil {
172+
if err != nil && !sql.ErrDoubleTypeTruncated.Is(err) {
173+
// Truncated warning is already thrown elsewhere.
132174
return nil, err
133175
}
134176
if s, ok := key.([]interface{}); ok {
@@ -139,7 +181,7 @@ func (n *HashLookup) GetHashKey(ctx *sql.Context, e sql.Expression, row sql.Row)
139181
return string(k), nil
140182
}
141183

142-
return hash.HashOfSimple(ctx, key, typ)
184+
return hash.HashOfSimple(ctx, key, n.CompareType)
143185
}
144186

145187
func (n *HashLookup) Dispose() {

sql/types/number.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1543,7 +1543,7 @@ func convertToFloat64(t NumberTypeImpl_, v interface{}) (float64, error) {
15431543
// parse the first longest valid numbers
15441544
s := numre.FindString(v)
15451545
i, _ = strconv.ParseFloat(s, 64)
1546-
return i, sql.ErrInvalidValue.New(v, t.String())
1546+
return i, sql.ErrDoubleTypeTruncated.New(v)
15471547
}
15481548
return i, nil
15491549
case bool:

0 commit comments

Comments
 (0)