@@ -154,7 +154,8 @@ func NewNotInTuple(left sql.Expression, right sql.Expression) sql.Expression {
154154// HashInTuple is an expression that checks an expression is inside a list of expressions using a hashmap.
155155type HashInTuple struct {
156156 in * InTuple
157- cmp map [uint64 ]sql.Expression
157+ cmp map [uint64 ]struct {}
158+ cmpType sql.Type
158159 hasNull bool
159160}
160161
@@ -169,90 +170,110 @@ func NewHashInTuple(ctx *sql.Context, left, right sql.Expression) (*HashInTuple,
169170 return nil , ErrUnsupportedInOperand .New (right )
170171 }
171172
172- cmp , hasNull , err := newInMap (ctx , rightTup , left .Type ())
173+ cmp , cmpType , hasNull , err := newInMap (ctx , left .Type (), rightTup )
173174 if err != nil {
174175 return nil , err
175176 }
176177
177- return & HashInTuple {in : NewInTuple (left , right ), cmp : cmp , hasNull : hasNull }, nil
178+ return & HashInTuple {
179+ in : NewInTuple (left , right ),
180+ cmp : cmp ,
181+ cmpType : cmpType ,
182+ hasNull : hasNull ,
183+ }, nil
178184}
179185
180186// newInMap hashes static expressions in the right child Tuple of a InTuple node
181- func newInMap (ctx * sql.Context , right Tuple , lType sql.Type ) (map [uint64 ]sql.Expression , bool , error ) {
187+ func newInMap (ctx * sql.Context , lType sql.Type , right Tuple ) (map [uint64 ]struct {}, sql.Type , bool , error ) {
182188 if lType == types .Null {
183- return nil , true , nil
189+ return nil , nil , true , nil
190+ }
191+ if len (right ) == 0 {
192+ return nil , nil , false , nil
184193 }
185194
186- elements := make ( map [ uint64 ]sql. Expression )
187- hasNull := false
195+ // If left is StringType and ANY of the right is NumberType, then we should use Double Type for comparison
196+ // If left is NumberType and ANT of the left is StringType, then we should use Double Type for comparison
188197 lColumnCount := types .NumColumns (lType )
189-
190- for _ , el := range right {
191- rType := el .Type ().Promote ()
198+ lIsNumType := types .IsNumber (lType )
199+ lIsStrType := types .IsText (lType )
200+ var rHasNumType , rHasStrType , rHasNull bool
201+ rVals := make ([]any , len (right ))
202+ for i , el := range right {
203+ rType := el .Type ()
204+
205+ // Nested tuples must have the same number of columns
192206 rColumnCount := types .NumColumns (rType )
193207 if rColumnCount != lColumnCount {
194- return nil , false , sql .ErrInvalidOperandColumns .New (lColumnCount , rColumnCount )
208+ return nil , nil , false , sql .ErrInvalidOperandColumns .New (lColumnCount , rColumnCount )
195209 }
196210
197- if rType == types .Null {
198- hasNull = true
211+ if types .IsNumber (rType ) {
212+ rHasNumType = true
213+ } else if types .IsText (rType ) {
214+ rHasStrType = true
215+ }
216+
217+ // Null elements are not hashed into the Tuple Map
218+ if types .IsNullType (rType ) {
219+ rHasNull = true
199220 continue
200221 }
201- i , err := el .Eval (ctx , sql.Row {})
222+ v , err := el .Eval (ctx , sql.Row {})
202223 if err != nil {
203- return nil , hasNull , err
224+ return nil , nil , false , err
204225 }
205- if i == nil {
206- hasNull = true
226+ if v == nil {
227+ rHasNull = true
207228 continue
208229 }
209230
210- var key uint64
211- if types .IsDecimal (rType ) || types .IsFloat (rType ) {
212- key , err = hash .HashOfSimple (ctx , i , rType )
213- } else {
214- key , err = hash .HashOfSimple (ctx , i , lType )
215- }
231+ rVals [i ] = v
232+ }
233+
234+ var cmpType sql.Type
235+ if (lIsStrType && rHasNumType ) || (lIsNumType && rHasStrType ) {
236+ cmpType = types .Float64
237+ } else if types .IsEnum (lType ) || types .IsSet (lType ) || types .IsText (lType ) {
238+ cmpType = lType
239+ } else {
240+ cmpType = types .GetCompareType (lType , right [0 ].Type ())
241+ }
242+
243+ elements := make (map [uint64 ]struct {})
244+ for _ , v := range rVals {
245+ key , err := hash .HashOfSimple (ctx , v , cmpType )
216246 if err != nil {
217- return nil , false , err
247+ return nil , nil , false , err
218248 }
219- elements [key ] = el
249+ elements [key ] = struct {}{}
220250 }
221-
222- return elements , hasNull , nil
251+ return elements , cmpType , rHasNull , nil
223252}
224253
225254// Eval implements the Expression interface.
226255func (hit * HashInTuple ) Eval (ctx * sql.Context , row sql.Row ) (interface {}, error ) {
227- leftElems := types .NumColumns (hit .in .Left ().Type ().Promote ())
228-
229256 leftVal , err := hit .in .Left ().Eval (ctx , row )
230257 if err != nil {
231258 return nil , err
232259 }
233-
234260 if leftVal == nil {
235261 return nil , nil
236262 }
237263
238- key , err := hash .HashOfSimple (ctx , leftVal , hit .in .Left ().Type ())
264+ // TODO: this needs to pick the same type as right... but there are multiple possibilities??
265+ key , err := hash .HashOfSimple (ctx , leftVal , hit .cmpType )
239266 if err != nil {
240267 return nil , err
241268 }
242269
243- right , ok := hit .cmp [key ]
244- if ! ok {
245- if hit .hasNull {
246- return nil , nil
247- }
248- return false , nil
270+ if _ , ok := hit .cmp [key ]; ok {
271+ return true , nil
249272 }
250-
251- if types .NumColumns (right .Type ().Promote ()) != leftElems {
252- return nil , sql .ErrInvalidOperandColumns .New (leftElems , types .NumColumns (right .Type ().Promote ()))
273+ if hit .hasNull {
274+ return nil , nil
253275 }
254-
255- return true , nil
276+ return false , nil
256277}
257278
258279func (hit * HashInTuple ) CollationCoercibility (ctx * sql.Context ) (collation sql.CollationID , coercibility byte ) {
0 commit comments