fix: Add precheck for unsupport datatype cast (#42677)

issue: #42527

---------

Signed-off-by: Cai Zhang <cai.zhang@zilliz.com>
This commit is contained in:
cai.zhang 2025-06-12 21:14:36 +08:00 committed by GitHub
parent c9bc70f272
commit 4ca1a231ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 47 additions and 30 deletions

View File

@ -265,16 +265,19 @@ func (s *FillExpressionValueSuite) TestBinaryRange() {
func (s *FillExpressionValueSuite) TestBinaryArithOpEvalRange() {
s.Run("normal case", func() {
testcases := []testcase{
{`Int64Field + 5.5 == 10.5`, nil},
{`Int64Field + 5 == 10`, nil},
{`Int64Field - {offset} >= {target}`, map[string]*schemapb.TemplateValue{
"offset": generateTemplateValue(schemapb.DataType_Double, 3.5),
"target": generateTemplateValue(schemapb.DataType_Double, 11.5),
"offset": generateTemplateValue(schemapb.DataType_Int64, int64(3)),
"target": generateTemplateValue(schemapb.DataType_Int64, int64(11)),
}},
{`Int64Field * 3.5 <= {target}`, map[string]*schemapb.TemplateValue{
"target": generateTemplateValue(schemapb.DataType_Double, 11.5),
{`Int64Field * 3 <= {target}`, map[string]*schemapb.TemplateValue{
"target": generateTemplateValue(schemapb.DataType_Int64, int64(11)),
}},
{`Int64Field / {offset} > 11.5`, map[string]*schemapb.TemplateValue{
"offset": generateTemplateValue(schemapb.DataType_Double, 3.5),
{`Int64Field / {offset} > 11`, map[string]*schemapb.TemplateValue{
"offset": generateTemplateValue(schemapb.DataType_Int64, int64(3)),
}},
{`FloatField / {offset} > 11.3`, map[string]*schemapb.TemplateValue{
"offset": generateTemplateValue(schemapb.DataType_Double, 3.3),
}},
{`ArrayField[0] % {offset} < 11`, map[string]*schemapb.TemplateValue{
"offset": generateTemplateValue(schemapb.DataType_Int64, int64(3)),

View File

@ -274,8 +274,14 @@ func Power(a, b *planpb.GenericValue) *ExprWithType {
} else {
// aInt && bInt
// 2 ** (-1) = 0.5
ret.dataType = schemapb.DataType_Double
ret.expr.GetValueExpr().Value = NewFloat(math.Pow(float64(a.GetInt64Val()), float64(b.GetInt64Val())))
target := math.Pow(float64(a.GetInt64Val()), float64(b.GetInt64Val()))
if b.GetInt64Val() >= 0 && target <= math.MaxInt64 {
ret.dataType = schemapb.DataType_Int64
ret.expr.GetValueExpr().Value = NewInt(int64(target))
} else {
ret.dataType = schemapb.DataType_Double
ret.expr.GetValueExpr().Value = NewFloat(target)
}
}
return ret

View File

@ -215,8 +215,8 @@ func (v *ParserVisitor) VisitAddSub(ctx *parser.AddSubContext) interface{} {
} else if rightExpr.expr.GetIsTemplate() {
dataType = leftExpr.dataType
} else {
if !canArithmetic(leftExpr.dataType, getArrayElementType(leftExpr), rightExpr.dataType, getArrayElementType(rightExpr)) {
return fmt.Errorf("'%s' can only be used between integer or floating or json field expressions", arithNameMap[ctx.GetOp().GetTokenType()])
if err := canArithmetic(leftExpr.dataType, getArrayElementType(leftExpr), rightExpr.dataType, getArrayElementType(rightExpr), reverse); err != nil {
return fmt.Errorf("'%s' %s", arithNameMap[ctx.GetOp().GetTokenType()], err.Error())
}
dataType, err = calcDataType(leftExpr, rightExpr, reverse)
@ -305,8 +305,8 @@ func (v *ParserVisitor) VisitMulDivMod(ctx *parser.MulDivModContext) interface{}
} else if rightExpr.expr.GetIsTemplate() {
dataType = leftExpr.dataType
} else {
if !canArithmetic(leftExpr.dataType, getArrayElementType(leftExpr), rightExpr.dataType, getArrayElementType(rightExpr)) {
return fmt.Errorf("'%s' can only be used between integer or floating or json field expressions", arithNameMap[ctx.GetOp().GetTokenType()])
if err := canArithmetic(leftExpr.dataType, getArrayElementType(leftExpr), rightExpr.dataType, getArrayElementType(rightExpr), reverse); err != nil {
return fmt.Errorf("'%s' %s", arithNameMap[ctx.GetOp().GetTokenType()], err.Error())
}
if err = checkValidModArith(arithExprMap[ctx.GetOp().GetTokenType()], leftExpr.dataType, getArrayElementType(leftExpr), rightExpr.dataType, getArrayElementType(rightExpr)); err != nil {

View File

@ -422,11 +422,8 @@ func TestExpr_castValue(t *testing.T) {
exprStr := `Int64Field + 1.1 == 2.1`
expr, err := ParseExpr(helper, exprStr, nil)
assert.NoError(t, err, exprStr)
assert.NotNil(t, expr, exprStr)
assert.NotNil(t, expr.GetBinaryArithOpEvalRangeExpr())
assert.NotNil(t, expr.GetBinaryArithOpEvalRangeExpr().GetRightOperand().GetFloatVal())
assert.NotNil(t, expr.GetBinaryArithOpEvalRangeExpr().GetValue().GetFloatVal())
assert.Error(t, err, exprStr)
assert.Nil(t, expr, exprStr)
exprStr = `FloatField +1 == 2`
expr, err = ParseExpr(helper, exprStr, nil)
@ -458,6 +455,8 @@ func TestExpr_BinaryArith(t *testing.T) {
`ArrayField[0] % 19 >= 20`,
`JSONField + 15 == 16`,
`15 + JSONField == 16`,
`Int64Field + (2**3) > 0`,
`1 + FloatField > 100`,
}
for _, exprStr := range exprStrs {
assertValidExpr(t, helper, exprStr)
@ -467,6 +466,11 @@ func TestExpr_BinaryArith(t *testing.T) {
unsupported := []string{
`ArrayField + 15 == 16`,
`15 + ArrayField == 16`,
`Int64Field + 1.1 = 2.1`,
`Int64Field == 2.1`,
`Int64Field >= 2.1`,
`3 > Int64Field >= 2.1`,
`Int64Field + (2**-1) > 0`,
}
for _, exprStr := range unsupported {
assertInvalidExpr(t, helper, exprStr)
@ -1520,7 +1524,7 @@ func TestRandomSampleWithFilter(t *testing.T) {
`VarCharField IS NOT NULL && random_sample(0.01)`,
`11.0 < DoubleField < 12.0 && random_sample(0.01)`,
`1 < JSONField < 3 && random_sample(0.01)`,
`Int64Field + 1.1 == 2.1 && random_sample(0.01)`,
`Int64Field + 1 == 2 && random_sample(0.01)`,
`Int64Field % 10 != 9 && random_sample(0.01)`,
`A * 15 > 16 && random_sample(0.01)`,
`(Int16Field - 3 == 4) and (Int32Field * 5 != 6) && random_sample(0.01)`,

View File

@ -560,8 +560,9 @@ func convertEscapeSingle(literal string) (string, error) {
func canArithmeticDataType(left, right schemapb.DataType) bool {
switch left {
case schemapb.DataType_Int8, schemapb.DataType_Int16, schemapb.DataType_Int32, schemapb.DataType_Int64,
schemapb.DataType_Float, schemapb.DataType_Double:
case schemapb.DataType_Int8, schemapb.DataType_Int16, schemapb.DataType_Int32, schemapb.DataType_Int64:
return typeutil.IsIntegerType(right) || typeutil.IsJSONType(right)
case schemapb.DataType_Float, schemapb.DataType_Double:
return typeutil.IsArithmetic(right) || typeutil.IsJSONType(right)
case schemapb.DataType_JSON:
return typeutil.IsArithmetic(right)
@ -583,17 +584,20 @@ func canArithmeticDataType(left, right schemapb.DataType) bool {
// return canArithmeticDataType(left.dataType, getArrayElementType(right))
//}
func canArithmetic(left, leftElement, right, rightElement schemapb.DataType) bool {
if !typeutil.IsArrayType(left) && !typeutil.IsArrayType(right) {
return canArithmeticDataType(left, right)
}
if typeutil.IsArrayType(left) && typeutil.IsArrayType(right) {
return canArithmeticDataType(leftElement, rightElement)
}
func canArithmetic(left, leftElement, right, rightElement schemapb.DataType, reverse bool) error {
if typeutil.IsArrayType(left) {
return canArithmeticDataType(leftElement, right)
left = leftElement
}
return canArithmeticDataType(left, rightElement)
if typeutil.IsArrayType(right) {
right = rightElement
}
if reverse {
left, right = right, left
}
if !canArithmeticDataType(left, right) {
return fmt.Errorf("cannot perform arithmetic between %s field and %s", left.String(), right.String())
}
return nil
}
func canConvertToIntegerType(dataType, elementType schemapb.DataType) bool {