@@ -34,10 +34,12 @@ import (
3434// simply delegates to the child.
3535func 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,46 @@ var _ sql.Node = (*HashLookup)(nil)
5861var _ sql.Expressioner = (* HashLookup )(nil )
5962var _ 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+ // consider consolidating
68+ if left .Equals (right ) {
69+ return left
70+ }
71+ if types .IsTuple (left ) && types .IsTuple (right ) {
72+ return left
73+ }
74+ if types .IsTime (left ) || types .IsTime (right ) {
75+ return types .DatetimeMaxPrecision
76+ }
77+ if types .IsJSON (left ) || types .IsJSON (right ) {
78+ return types .JSON
79+ }
80+ if types .IsBinaryType (left ) || types .IsBinaryType (right ) {
81+ return types .LongBlob
82+ }
83+ if types .IsNumber (left ) || types .IsNumber (right ) {
84+ if types .IsDecimal (left ) {
85+ return left
86+ }
87+ if types .IsDecimal (right ) {
88+ return right
89+ }
90+ if types .IsFloat (left ) || types .IsFloat (right ) {
91+ return types .Float64
92+ }
93+ if types .IsSigned (left ) && types .IsSigned (right ) {
94+ return types .Int64
95+ }
96+ if types .IsUnsigned (left ) && types .IsUnsigned (right ) {
97+ return types .Uint64
98+ }
99+ return types .Float64
100+ }
101+ return types .LongText
102+ }
103+
61104func (n * HashLookup ) Expressions () []sql.Expression {
62105 return []sql.Expression {n .RightEntryKey , n .LeftProbeKey }
63106}
@@ -113,7 +156,7 @@ func (n *HashLookup) CollationCoercibility(ctx *sql.Context) (collation sql.Coll
113156 return sql .GetCoercibility (ctx , n .Child )
114157}
115158
116- // Convert a tuple expression returning []interface{} into something comparable.
159+ // GetHashKey converts a tuple expression returning []interface{} into something comparable.
117160// Fast paths a few smaller slices into fixed size arrays, puts everything else
118161// through string serialization and a hash for now. It is OK to hash lossy here
119162// as the join condition is still evaluated after the matching rows are returned.
@@ -122,13 +165,13 @@ func (n *HashLookup) GetHashKey(ctx *sql.Context, e sql.Expression, row sql.Row)
122165 if err != nil {
123166 return nil , err
124167 }
125- typ := n .LeftProbeKey .Type ()
126- key , _ , err = typ .Convert (ctx , key )
168+ key , _ , err = n .CompareType .Convert (ctx , key )
127169 if types .ErrValueNotNil .Is (err ) {
128170 // The LHS expression was NullType. This is allowed.
129171 return nil , nil
130172 }
131- if err != nil {
173+ if err != nil && ! sql .ErrTruncatedIncorrect .Is (err ) {
174+ // Truncated warning is already thrown elsewhere.
132175 return nil , err
133176 }
134177 if s , ok := key .([]interface {}); ok {
@@ -139,7 +182,7 @@ func (n *HashLookup) GetHashKey(ctx *sql.Context, e sql.Expression, row sql.Row)
139182 return string (k ), nil
140183 }
141184
142- return hash .HashOfSimple (ctx , key , typ )
185+ return hash .HashOfSimple (ctx , key , n . CompareType )
143186}
144187
145188func (n * HashLookup ) Dispose () {
0 commit comments