@@ -16,6 +16,7 @@ package expression
16
16
17
17
import (
18
18
"fmt"
19
+ "github.com/dolthub/vitess/go/mysql"
19
20
20
21
"github.com/dolthub/go-mysql-server/sql"
21
22
"github.com/dolthub/go-mysql-server/sql/hash"
@@ -59,65 +60,120 @@ func NewInTuple(left sql.Expression, right sql.Expression) *InTuple {
59
60
return & InTuple {BinaryExpressionStub {left , right }}
60
61
}
61
62
63
+ // validateAndEvalRightTuple will evaluate the right tuple, check if leftType and the right Tuple are comparable,
64
+ // determine what type to use to compare the two sides, and indicate if right Tuple contains any NULL elements.
65
+ // The NULL handling for IN expressions is tricky. According to
66
+ // https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html#operator_in:
67
+ // To comply with the SQL standard, IN() returns NULL not only if the expression on the left hand side is NULL, but
68
+ // also if no match is found in the list and one of the expressions in the list is NULL.
69
+ func validateAndEvalRightTuple (ctx * sql.Context , lType sql.Type , right Tuple , row sql.Row ) ([]any , sql.Type , bool , error ) {
70
+ // If left is StringType and ANY of the right is NumberType, then we should use Double Type for comparison
71
+ // If left is NumberType and ANT of the left is StringType, then we should use Double Type for comparison
72
+ lColCount := types .NumColumns (lType )
73
+ lIsNumType := types .IsNumber (lType )
74
+ lIsStrType := types .IsText (lType )
75
+ var rHasNumType , rHasStrType , rHasNull bool
76
+ rVals := make ([]any , len (right ))
77
+ for i , el := range right {
78
+ rType := el .Type ()
79
+
80
+ // Nested tuples must have the same number of columns
81
+ rColCount := types .NumColumns (rType )
82
+ if rColCount != lColCount {
83
+ return nil , nil , false , sql .ErrInvalidOperandColumns .New (lColCount , types .NumColumns (el .Type ()))
84
+ }
85
+
86
+ if types .IsNumber (rType ) {
87
+ rHasNumType = true
88
+ } else if types .IsText (rType ) {
89
+ rHasStrType = true
90
+ }
91
+
92
+ // Null elements are not hashed into the Tuple Map
93
+ if types .IsNullType (rType ) {
94
+ rHasNull = true
95
+ continue
96
+ }
97
+ v , err := el .Eval (ctx , row )
98
+ if err != nil {
99
+ return nil , nil , false , err
100
+ }
101
+ if v == nil {
102
+ rHasNull = true
103
+ continue
104
+ }
105
+
106
+ rVals [i ] = v
107
+ }
108
+
109
+ var cmpType sql.Type
110
+ if (lIsStrType && rHasNumType ) || (lIsNumType && rHasStrType ) {
111
+ cmpType = types .Float64
112
+ } else if types .IsEnum (lType ) || types .IsSet (lType ) || types .IsText (lType ) {
113
+ cmpType = lType
114
+ } else {
115
+ cmpType = types .GetCompareType (lType , right [0 ].Type ())
116
+ }
117
+
118
+ return rVals , cmpType , rHasNull , nil
119
+ }
120
+
62
121
// Eval implements the Expression interface.
63
122
func (in * InTuple ) Eval (ctx * sql.Context , row sql.Row ) (interface {}, error ) {
64
- leftElems := types .NumColumns (in .Left ().Type ())
65
- originalLeft , err := in .Left ().Eval (ctx , row )
123
+ leftVal , err := in .Left ().Eval (ctx , row )
66
124
if err != nil {
67
125
return nil , err
68
126
}
69
- if originalLeft == nil {
127
+ if leftVal == nil {
70
128
return nil , nil
71
129
}
72
130
73
- // The NULL handling for IN expressions is tricky. According to
74
- // https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html#operator_in:
75
- // To comply with the SQL standard, IN() returns NULL not only if the expression on the left hand side is NULL, but
76
- // also if no match is found in the list and one of the expressions in the list is NULL.
77
- rightNull := false
78
-
79
- switch right := in .Right ().(type ) {
80
- case Tuple :
81
- for _ , el := range right {
82
- if types .NumColumns (el .Type ()) != leftElems {
83
- return nil , sql .ErrInvalidOperandColumns .New (leftElems , types .NumColumns (el .Type ()))
84
- }
85
- }
131
+ right , isTuple := in .Right ().(Tuple )
132
+ if ! isTuple {
133
+ return nil , ErrUnsupportedInOperand .New (right )
134
+ }
86
135
87
- leftLit := NewLiteral (originalLeft , in .Left ().Type ())
88
- for _ , el := range right {
89
- originalRight , err := el .Eval (ctx , row )
90
- if err != nil {
91
- return nil , err
92
- }
136
+ lType := in .Left ().Type ()
137
+ rVals , cmpType , rHasNull , err := validateAndEvalRightTuple (ctx , lType , right , row )
138
+ if err != nil {
139
+ return nil , err
140
+ }
93
141
94
- if ! rightNull && originalRight == nil {
95
- rightNull = true
96
- continue
97
- }
142
+ lv , _ , lErr := cmpType .Convert (ctx , leftVal )
143
+ if lErr != nil {
144
+ if ! sql .ErrTruncatedIncorrect .Is (lErr ) {
145
+ return nil , lErr
146
+ }
147
+ ctx .Warn (mysql .ERTruncatedWrongValue , "%s" , lErr .Error ())
148
+ }
98
149
99
- comp := newComparison ( leftLit , NewLiteral ( originalRight , el . Type ()))
100
- l , r , compareType , err := comp . castLeftAndRight ( ctx , originalLeft , originalRight )
101
- if err != nil {
102
- return nil , err
103
- }
104
- cmp , err := compareType . Compare ( ctx , l , r )
105
- if err != nil {
106
- return nil , err
150
+ for _ , rVal := range rVals {
151
+ if rVal == nil {
152
+ continue
153
+ }
154
+ rv , _ , rErr := cmpType . Convert ( ctx , rVal )
155
+ if rErr != nil {
156
+ if ! sql . ErrTruncatedIncorrect . Is ( rErr ) {
157
+ return nil , rErr
107
158
}
108
- if cmp == 0 {
109
- return true , nil
159
+ ctx .Warn (mysql .ERTruncatedWrongValue , "%s" , rErr .Error ())
160
+ }
161
+ cmp , cErr := cmpType .Compare (ctx , lv , rv )
162
+ if cErr != nil {
163
+ if ! sql .ErrTruncatedIncorrect .Is (cErr ) {
164
+ return nil , cErr
110
165
}
166
+ ctx .Warn (mysql .ERTruncatedWrongValue , "%s" , cErr .Error ())
111
167
}
112
-
113
- if rightNull {
114
- return nil , nil
168
+ if cmp == 0 {
169
+ return true , nil
115
170
}
116
-
117
- return false , nil
118
- default :
119
- return nil , ErrUnsupportedInOperand .New (right )
120
171
}
172
+ if rHasNull {
173
+ return nil , nil
174
+ }
175
+
176
+ return false , nil
121
177
}
122
178
123
179
// WithChildren implements the Expression interface.
@@ -191,60 +247,15 @@ func newInMap(ctx *sql.Context, lType sql.Type, right Tuple) (map[uint64]struct{
191
247
if len (right ) == 0 {
192
248
return nil , nil , false , nil
193
249
}
194
-
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
197
- lColumnCount := types .NumColumns (lType )
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
206
- rColumnCount := types .NumColumns (rType )
207
- if rColumnCount != lColumnCount {
208
- return nil , nil , false , sql .ErrInvalidOperandColumns .New (lColumnCount , rColumnCount )
209
- }
210
-
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
220
- continue
221
- }
222
- v , err := el .Eval (ctx , sql.Row {})
223
- if err != nil {
224
- return nil , nil , false , err
225
- }
226
- if v == nil {
227
- rHasNull = true
228
- continue
229
- }
230
-
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 ())
250
+ rVals , cmpType , rHasNull , err := validateAndEvalRightTuple (ctx , lType , right , nil )
251
+ if err != nil {
252
+ return nil , nil , false , err
241
253
}
242
-
243
254
elements := make (map [uint64 ]struct {})
244
255
for _ , v := range rVals {
245
- key , err := hash .HashOfSimple (ctx , v , cmpType )
246
- if err != nil {
247
- return nil , nil , false , err
256
+ key , hErr := hash .HashOfSimple (ctx , v , cmpType )
257
+ if hErr != nil {
258
+ return nil , nil , false , hErr
248
259
}
249
260
elements [key ] = struct {}{}
250
261
}
0 commit comments