mirror of
https://gitee.com/milvus-io/milvus.git
synced 2025-12-28 14:35:27 +08:00
related: #46075 Signed-off-by: MrPresent-Han <chun.han@gmail.com> Co-authored-by: MrPresent-Han <chun.han@gmail.com>
This commit is contained in:
parent
ab2e51b1c7
commit
d9f8e38d6a
@ -20,6 +20,24 @@ type ParserVisitorArgs struct {
|
||||
Timezone string
|
||||
}
|
||||
|
||||
// int64OverflowError is a special error type used to handle the case where
|
||||
// 9223372036854775808 (which exceeds int64 max) is used with unary minus
|
||||
// to represent -9223372036854775808 (int64 minimum value).
|
||||
// This happens because ANTLR parses -9223372036854775808 as Unary(SUB, Integer(9223372036854775808)),
|
||||
// causing the integer literal to exceed int64 range before the unary minus is applied.
|
||||
type int64OverflowError struct {
|
||||
literal string
|
||||
}
|
||||
|
||||
func (e *int64OverflowError) Error() string {
|
||||
return fmt.Sprintf("int64 overflow: %s", e.literal)
|
||||
}
|
||||
|
||||
func isInt64OverflowError(err error) bool {
|
||||
_, ok := err.(*int64OverflowError)
|
||||
return ok
|
||||
}
|
||||
|
||||
type ParserVisitor struct {
|
||||
parser.BasePlanVisitor
|
||||
schema *typeutil.SchemaHelper
|
||||
@ -108,6 +126,15 @@ func (v *ParserVisitor) VisitInteger(ctx *parser.IntegerContext) interface{} {
|
||||
literal := ctx.IntegerConstant().GetText()
|
||||
i, err := strconv.ParseInt(literal, 0, 64)
|
||||
if err != nil {
|
||||
// Special case: 9223372036854775808 is out of int64 range,
|
||||
// but -9223372036854775808 is valid (int64 minimum value).
|
||||
// This happens because ANTLR parses -9223372036854775808 as:
|
||||
// Unary(SUB, Integer(9223372036854775808))
|
||||
// The integer literal 9223372036854775808 exceeds int64 max (9223372036854775807)
|
||||
// before the unary minus is applied. We handle this in VisitUnary.
|
||||
if literal == "9223372036854775808" {
|
||||
return &int64OverflowError{literal: literal}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return &ExprWithType{
|
||||
@ -950,6 +977,23 @@ func (v *ParserVisitor) VisitReverseRange(ctx *parser.ReverseRangeContext) inter
|
||||
func (v *ParserVisitor) VisitUnary(ctx *parser.UnaryContext) interface{} {
|
||||
child := ctx.Expr().Accept(v)
|
||||
if err := getError(child); err != nil {
|
||||
// Special case: handle -9223372036854775808
|
||||
// ANTLR parses -9223372036854775808 as Unary(SUB, Integer(9223372036854775808)).
|
||||
// The integer literal 9223372036854775808 exceeds int64 max, but when combined
|
||||
// with unary minus, it represents the valid int64 minimum value.
|
||||
if isInt64OverflowError(err) && ctx.GetOp().GetTokenType() == parser.PlanParserSUB {
|
||||
return &ExprWithType{
|
||||
dataType: schemapb.DataType_Int64,
|
||||
expr: &planpb.Expr{
|
||||
Expr: &planpb.Expr_ValueExpr{
|
||||
ValueExpr: &planpb.ValueExpr{
|
||||
Value: NewInt(math.MinInt64),
|
||||
},
|
||||
},
|
||||
},
|
||||
nodeDependent: true,
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -912,6 +912,9 @@ func TestCreateRetrievePlan(t *testing.T) {
|
||||
schema := newTestSchemaHelper(t)
|
||||
_, err := CreateRetrievePlan(schema, "Int64Field > 0", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = CreateRetrievePlan(schema, "id > -9223372036854775808", nil)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCreateSearchPlan(t *testing.T) {
|
||||
@ -1007,7 +1010,7 @@ func TestExpr_Invalid(t *testing.T) {
|
||||
`"str" != false`,
|
||||
`VarCharField != FloatField`,
|
||||
`FloatField == VarCharField`,
|
||||
`A == -9223372036854775808`,
|
||||
`A == -9223372036854775809`,
|
||||
// ---------------------- relational --------------------
|
||||
//`not_in_schema < 1`, // maybe in json
|
||||
//`1 <= not_in_schema`, // maybe in json
|
||||
|
||||
@ -18,6 +18,7 @@ package proxy
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -840,6 +841,61 @@ func TestTaskQuery_functions(t *testing.T) {
|
||||
assert.Equal(t, []int64{11}, result.GetFieldsData()[0].GetScalars().GetLongData().Data)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("test SelectMinPK with pk equals MaxInt64 and firstInt true", func(t *testing.T) {
|
||||
// Test case to cover pk == minIntPK && firstInt condition in SelectMinPK
|
||||
// When the first int64 PK equals math.MaxInt64 (which equals initial minIntPK),
|
||||
// and firstInt is true, the condition firstInt || pk < minIntPK evaluates to true
|
||||
// This ensures the branch is taken even when pk == minIntPK
|
||||
const (
|
||||
Dim = 8
|
||||
Int64FieldName = "Int64Field"
|
||||
FloatVectorFieldName = "FloatVectorField"
|
||||
Int64FieldID = common.StartOfUserFieldID + 1
|
||||
FloatVectorFieldID = common.StartOfUserFieldID + 2
|
||||
)
|
||||
maxInt64PK := int64(math.MaxInt64)
|
||||
FloatVector := []float32{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}
|
||||
fieldDataMaxPK := getFieldData(Int64FieldName, Int64FieldID, schemapb.DataType_Int64, []int64{maxInt64PK}, 1)
|
||||
vectorDataMaxPK := getFieldData(FloatVectorFieldName, FloatVectorFieldID, schemapb.DataType_FloatVector, FloatVector[0:8], Dim)
|
||||
|
||||
// Create result with MaxInt64 as the first PK to trigger pk == minIntPK && firstInt condition
|
||||
// Only the first result has data with pk = maxInt64PK
|
||||
rMaxPK := &internalpb.RetrieveResults{
|
||||
Ids: &schemapb.IDs{
|
||||
IdField: &schemapb.IDs_IntId{
|
||||
IntId: &schemapb.LongArray{
|
||||
Data: []int64{maxInt64PK},
|
||||
},
|
||||
},
|
||||
},
|
||||
FieldsData: []*schemapb.FieldData{fieldDataMaxPK, vectorDataMaxPK},
|
||||
HasMoreResult: false,
|
||||
}
|
||||
|
||||
// Create a second result with no data (empty result)
|
||||
rEmpty := &internalpb.RetrieveResults{
|
||||
Ids: &schemapb.IDs{
|
||||
IdField: &schemapb.IDs_IntId{
|
||||
IntId: &schemapb.LongArray{
|
||||
Data: []int64{},
|
||||
},
|
||||
},
|
||||
},
|
||||
FieldsData: []*schemapb.FieldData{},
|
||||
HasMoreResult: false,
|
||||
}
|
||||
|
||||
// Test with MaxInt64 first to ensure it's handled correctly when pk == minIntPK && firstInt
|
||||
// The reduced result should include the maxInt64PK result
|
||||
result, err := reduceRetrieveResults(context.Background(),
|
||||
[]*internalpb.RetrieveResults{rMaxPK, rEmpty},
|
||||
&queryParams{limit: typeutil.Unlimited})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(result.GetFieldsData()))
|
||||
// Should include the maxInt64PK result
|
||||
assert.Equal(t, []int64{maxInt64PK}, result.GetFieldsData()[0].GetScalars().GetLongData().Data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -2045,6 +2045,7 @@ func SelectMinPK[T ResultWithID](results []T, cursors []int64) (int, bool) {
|
||||
minIntPK int64 = math.MaxInt64
|
||||
|
||||
firstStr = true
|
||||
firstInt = true
|
||||
minStrPK string
|
||||
)
|
||||
for i, cursor := range cursors {
|
||||
@ -2064,7 +2065,8 @@ func SelectMinPK[T ResultWithID](results []T, cursors []int64) (int, bool) {
|
||||
sel = i
|
||||
}
|
||||
case int64:
|
||||
if pk < minIntPK {
|
||||
if firstInt || pk < minIntPK {
|
||||
firstInt = false
|
||||
minIntPK = pk
|
||||
sel = i
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user