@@ -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,45 @@ 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+ 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+
61103func (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
145187func (n * HashLookup ) Dispose () {
0 commit comments