Skip to content

Commit 2b528c5

Browse files
author
James Cor
committed
refactor in tuple logic
1 parent 6c641e9 commit 2b528c5

File tree

1 file changed

+105
-94
lines changed

1 file changed

+105
-94
lines changed

sql/expression/in.go

Lines changed: 105 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package expression
1616

1717
import (
1818
"fmt"
19+
"github.com/dolthub/vitess/go/mysql"
1920

2021
"github.com/dolthub/go-mysql-server/sql"
2122
"github.com/dolthub/go-mysql-server/sql/hash"
@@ -59,65 +60,120 @@ func NewInTuple(left sql.Expression, right sql.Expression) *InTuple {
5960
return &InTuple{BinaryExpressionStub{left, right}}
6061
}
6162

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+
62121
// Eval implements the Expression interface.
63122
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)
66124
if err != nil {
67125
return nil, err
68126
}
69-
if originalLeft == nil {
127+
if leftVal == nil {
70128
return nil, nil
71129
}
72130

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+
}
86135

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+
}
93141

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+
}
98149

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
107158
}
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
110165
}
166+
ctx.Warn(mysql.ERTruncatedWrongValue, "%s", cErr.Error())
111167
}
112-
113-
if rightNull {
114-
return nil, nil
168+
if cmp == 0 {
169+
return true, nil
115170
}
116-
117-
return false, nil
118-
default:
119-
return nil, ErrUnsupportedInOperand.New(right)
120171
}
172+
if rHasNull {
173+
return nil, nil
174+
}
175+
176+
return false, nil
121177
}
122178

123179
// WithChildren implements the Expression interface.
@@ -191,60 +247,15 @@ func newInMap(ctx *sql.Context, lType sql.Type, right Tuple) (map[uint64]struct{
191247
if len(right) == 0 {
192248
return nil, nil, false, nil
193249
}
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
241253
}
242-
243254
elements := make(map[uint64]struct{})
244255
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
248259
}
249260
elements[key] = struct{}{}
250261
}

0 commit comments

Comments
 (0)