@@ -141,50 +141,20 @@ func (c *comparison) Compare(ctx *sql.Context, row sql.Row) (int, error) {
141
141
return c .Left ().Type ().Compare (ctx , left , right )
142
142
}
143
143
144
- // ENUM, SET, and TIME must be excluded when doing comparisons, as they're too restrictive to use as a comparison
145
- // base.
146
- //
147
- // The best overall method would be to assign type priority. For example, INT would have a higher priority than
148
- // TINYINT. This could then be combined with the origin of the value (table column, procedure param, etc.) to
149
- // determine the best type for any comparison (tie-breakers can be simple rules such as the current left preference).
150
- var compareType sql.Type
151
- collationPreference := sql .Collation_Default
152
- switch c .Left ().(type ) {
153
- case * GetField , * UserVar , * SystemVar , * ProcedureParam :
154
- compareType = c .Left ().Type ()
155
- if twc , ok := compareType .(sql.TypeWithCollation ); ok {
156
- collationPreference = twc .Collation ()
157
- }
158
- default :
159
- switch c .Right ().(type ) {
160
- case * GetField , * UserVar , * SystemVar , * ProcedureParam :
161
- compareType = c .Right ().Type ()
162
- if twc , ok := compareType .(sql.TypeWithCollation ); ok {
163
- collationPreference = twc .Collation ()
164
- }
165
- }
166
- }
167
- if compareType != nil {
168
- _ , isEnum := compareType .(sql.EnumType )
169
- _ , isSet := compareType .(sql.SetType )
170
- _ , isTime := compareType .(types.TimeType )
171
- if ! isEnum && ! isSet && ! isTime {
172
- compareType = nil
173
- }
144
+ l , r , compareType , err := c .castLeftAndRight (ctx , left , right )
145
+ if err != nil {
146
+ return 0 , err
174
147
}
175
- if compareType == nil {
176
- left , right , compareType , err = c .castLeftAndRight (ctx , left , right )
177
- if err != nil {
178
- return 0 , err
179
- }
148
+
149
+ // Set comparison relies on empty strings not being converted yet
150
+ if types .IsSet (compareType ) {
151
+ return compareType .Compare (ctx , left , right )
180
152
}
181
- if _ , isSet := compareType .(sql.SetType ); ! isSet && types .IsTextOnly (compareType ) {
182
- collationPreference , _ = c .CollationCoercibility (ctx )
183
- stringCompareType := compareType .(sql.StringType )
153
+ collationPreference , _ := c .CollationCoercibility (ctx )
154
+ if stringCompareType , ok := compareType .(sql.StringType ); ok && types .IsTextOnly (stringCompareType ) {
184
155
compareType = types .MustCreateString (stringCompareType .Type (), stringCompareType .Length (), collationPreference )
185
156
}
186
-
187
- return compareType .Compare (ctx , left , right )
157
+ return compareType .Compare (ctx , l , r )
188
158
}
189
159
190
160
func (c * comparison ) evalLeftAndRight (ctx * sql.Context , row sql.Row ) (interface {}, interface {}, error ) {
@@ -204,6 +174,49 @@ func (c *comparison) evalLeftAndRight(ctx *sql.Context, row sql.Row) (interface{
204
174
func (c * comparison ) castLeftAndRight (ctx * sql.Context , left , right interface {}) (interface {}, interface {}, sql.Type , error ) {
205
175
leftType := c .Left ().Type ()
206
176
rightType := c .Right ().Type ()
177
+
178
+ leftIsEnumOrSet := types .IsEnum (leftType ) || types .IsSet (leftType )
179
+ rightIsEnumOrSet := types .IsEnum (rightType ) || types .IsSet (rightType )
180
+ // Only convert if same Enum or Set
181
+ if leftIsEnumOrSet && rightIsEnumOrSet {
182
+ if types .TypesEqual (leftType , rightType ) {
183
+ return left , right , leftType , nil
184
+ }
185
+ } else {
186
+ // If right side is convertible to enum/set, convert. Otherwise, convert left side
187
+ if leftIsEnumOrSet && (types .IsText (rightType ) || types .IsNumber (rightType )) {
188
+ if r , inRange , err := leftType .Convert (ctx , right ); inRange && err == nil {
189
+ return left , r , leftType , nil
190
+ } else {
191
+ l , err := types .TypeAwareConversion (ctx , left , leftType , rightType )
192
+ if err != nil {
193
+ return nil , nil , nil , err
194
+ }
195
+ return l , right , rightType , nil
196
+ }
197
+ }
198
+ // If left side is convertible to enum/set, convert. Otherwise, convert right side
199
+ if rightIsEnumOrSet && (types .IsText (leftType ) || types .IsNumber (leftType )) {
200
+ if l , inRange , err := rightType .Convert (ctx , left ); inRange && err == nil {
201
+ return l , right , rightType , nil
202
+ } else {
203
+ r , err := types .TypeAwareConversion (ctx , right , rightType , leftType )
204
+ if err != nil {
205
+ return nil , nil , nil , err
206
+ }
207
+ return left , r , leftType , nil
208
+ }
209
+ }
210
+ }
211
+
212
+ if types .IsTimespan (leftType ) || types .IsTimespan (rightType ) {
213
+ if l , err := types .Time .ConvertToTimespan (left ); err == nil {
214
+ if r , err := types .Time .ConvertToTimespan (right ); err == nil {
215
+ return l , r , types .Time , nil
216
+ }
217
+ }
218
+ }
219
+
207
220
if types .IsTuple (leftType ) && types .IsTuple (rightType ) {
208
221
return left , right , c .Left ().Type (), nil
209
222
}
0 commit comments