From 4d6c57affe95c39494c45239bb91dab8b9518e40 Mon Sep 17 00:00:00 2001 From: "cai.zhang" Date: Fri, 13 Jun 2025 18:18:37 +0800 Subject: [PATCH] fix: [2.5] Add precheck for unsupport datatype cast (#42678) issue: #42527 master pr: #42677 --------- Signed-off-by: Cai Zhang --- .../fill_expression_value_test.go | 17 +++++++----- internal/parser/planparserv2/operators.go | 10 +++++-- .../parser/planparserv2/parser_visitor.go | 8 +++--- .../planparserv2/plan_parser_v2_test.go | 14 ++++++---- internal/parser/planparserv2/utils.go | 26 +++++++++++-------- 5 files changed, 46 insertions(+), 29 deletions(-) diff --git a/internal/parser/planparserv2/fill_expression_value_test.go b/internal/parser/planparserv2/fill_expression_value_test.go index 63b0e5bc07..1407b86249 100644 --- a/internal/parser/planparserv2/fill_expression_value_test.go +++ b/internal/parser/planparserv2/fill_expression_value_test.go @@ -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)), diff --git a/internal/parser/planparserv2/operators.go b/internal/parser/planparserv2/operators.go index dc1080e328..a6f4e2476f 100644 --- a/internal/parser/planparserv2/operators.go +++ b/internal/parser/planparserv2/operators.go @@ -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 diff --git a/internal/parser/planparserv2/parser_visitor.go b/internal/parser/planparserv2/parser_visitor.go index 4c7721e62c..1524f01299 100644 --- a/internal/parser/planparserv2/parser_visitor.go +++ b/internal/parser/planparserv2/parser_visitor.go @@ -210,8 +210,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) @@ -300,8 +300,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 { diff --git a/internal/parser/planparserv2/plan_parser_v2_test.go b/internal/parser/planparserv2/plan_parser_v2_test.go index 307550d633..ef0861e3c7 100644 --- a/internal/parser/planparserv2/plan_parser_v2_test.go +++ b/internal/parser/planparserv2/plan_parser_v2_test.go @@ -347,11 +347,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) @@ -383,6 +380,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) @@ -392,6 +391,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) diff --git a/internal/parser/planparserv2/utils.go b/internal/parser/planparserv2/utils.go index 621f2fa8fd..fbf1a2967d 100644 --- a/internal/parser/planparserv2/utils.go +++ b/internal/parser/planparserv2/utils.go @@ -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 {