Skip to content

Commit 4ef59fc

Browse files
author
MrPresent-Han
committed
fix: query failed for int value on edge(#46075)
Signed-off-by: MrPresent-Han <[email protected]>
1 parent a4c1c5a commit 4ef59fc

File tree

4 files changed

+107
-2
lines changed

4 files changed

+107
-2
lines changed

internal/parser/planparserv2/parser_visitor.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ type ParserVisitorArgs struct {
2020
Timezone string
2121
}
2222

23+
// int64OverflowError is a special error type used to handle the case where
24+
// 9223372036854775808 (which exceeds int64 max) is used with unary minus
25+
// to represent -9223372036854775808 (int64 minimum value).
26+
// This happens because ANTLR parses -9223372036854775808 as Unary(SUB, Integer(9223372036854775808)),
27+
// causing the integer literal to exceed int64 range before the unary minus is applied.
28+
type int64OverflowError struct {
29+
literal string
30+
}
31+
32+
func (e *int64OverflowError) Error() string {
33+
return fmt.Sprintf("int64 overflow: %s", e.literal)
34+
}
35+
36+
func isInt64OverflowError(err error) bool {
37+
_, ok := err.(*int64OverflowError)
38+
return ok
39+
}
40+
2341
type ParserVisitor struct {
2442
parser.BasePlanVisitor
2543
schema *typeutil.SchemaHelper
@@ -108,6 +126,15 @@ func (v *ParserVisitor) VisitInteger(ctx *parser.IntegerContext) interface{} {
108126
literal := ctx.IntegerConstant().GetText()
109127
i, err := strconv.ParseInt(literal, 0, 64)
110128
if err != nil {
129+
// Special case: 9223372036854775808 is out of int64 range,
130+
// but -9223372036854775808 is valid (int64 minimum value).
131+
// This happens because ANTLR parses -9223372036854775808 as:
132+
// Unary(SUB, Integer(9223372036854775808))
133+
// The integer literal 9223372036854775808 exceeds int64 max (9223372036854775807)
134+
// before the unary minus is applied. We handle this in VisitUnary.
135+
if literal == "9223372036854775808" {
136+
return &int64OverflowError{literal: literal}
137+
}
111138
return err
112139
}
113140
return &ExprWithType{
@@ -950,6 +977,23 @@ func (v *ParserVisitor) VisitReverseRange(ctx *parser.ReverseRangeContext) inter
950977
func (v *ParserVisitor) VisitUnary(ctx *parser.UnaryContext) interface{} {
951978
child := ctx.Expr().Accept(v)
952979
if err := getError(child); err != nil {
980+
// Special case: handle -9223372036854775808
981+
// ANTLR parses -9223372036854775808 as Unary(SUB, Integer(9223372036854775808)).
982+
// The integer literal 9223372036854775808 exceeds int64 max, but when combined
983+
// with unary minus, it represents the valid int64 minimum value.
984+
if isInt64OverflowError(err) && ctx.GetOp().GetTokenType() == parser.PlanParserSUB {
985+
return &ExprWithType{
986+
dataType: schemapb.DataType_Int64,
987+
expr: &planpb.Expr{
988+
Expr: &planpb.Expr_ValueExpr{
989+
ValueExpr: &planpb.ValueExpr{
990+
Value: NewInt(math.MinInt64),
991+
},
992+
},
993+
},
994+
nodeDependent: true,
995+
}
996+
}
953997
return err
954998
}
955999

internal/parser/planparserv2/plan_parser_v2_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,9 @@ func TestCreateRetrievePlan(t *testing.T) {
912912
schema := newTestSchemaHelper(t)
913913
_, err := CreateRetrievePlan(schema, "Int64Field > 0", nil)
914914
assert.NoError(t, err)
915+
916+
_, err = CreateRetrievePlan(schema, "id > -9223372036854775808", nil)
917+
assert.NoError(t, err)
915918
}
916919

917920
func TestCreateSearchPlan(t *testing.T) {
@@ -1007,7 +1010,7 @@ func TestExpr_Invalid(t *testing.T) {
10071010
`"str" != false`,
10081011
`VarCharField != FloatField`,
10091012
`FloatField == VarCharField`,
1010-
`A == -9223372036854775808`,
1013+
`A == -9223372036854775809`,
10111014
// ---------------------- relational --------------------
10121015
//`not_in_schema < 1`, // maybe in json
10131016
//`1 <= not_in_schema`, // maybe in json

internal/proxy/task_query_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package proxy
1818
import (
1919
"context"
2020
"fmt"
21+
"math"
2122
"testing"
2223
"time"
2324

@@ -840,6 +841,61 @@ func TestTaskQuery_functions(t *testing.T) {
840841
assert.Equal(t, []int64{11}, result.GetFieldsData()[0].GetScalars().GetLongData().Data)
841842
})
842843
})
844+
845+
t.Run("test SelectMinPK with pk equals MaxInt64 and firstInt true", func(t *testing.T) {
846+
// Test case to cover pk == minIntPK && firstInt condition in SelectMinPK
847+
// When the first int64 PK equals math.MaxInt64 (which equals initial minIntPK),
848+
// and firstInt is true, the condition firstInt || pk < minIntPK evaluates to true
849+
// This ensures the branch is taken even when pk == minIntPK
850+
const (
851+
Dim = 8
852+
Int64FieldName = "Int64Field"
853+
FloatVectorFieldName = "FloatVectorField"
854+
Int64FieldID = common.StartOfUserFieldID + 1
855+
FloatVectorFieldID = common.StartOfUserFieldID + 2
856+
)
857+
maxInt64PK := int64(math.MaxInt64)
858+
FloatVector := []float32{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}
859+
fieldDataMaxPK := getFieldData(Int64FieldName, Int64FieldID, schemapb.DataType_Int64, []int64{maxInt64PK}, 1)
860+
vectorDataMaxPK := getFieldData(FloatVectorFieldName, FloatVectorFieldID, schemapb.DataType_FloatVector, FloatVector[0:8], Dim)
861+
862+
// Create result with MaxInt64 as the first PK to trigger pk == minIntPK && firstInt condition
863+
// Only the first result has data with pk = maxInt64PK
864+
rMaxPK := &internalpb.RetrieveResults{
865+
Ids: &schemapb.IDs{
866+
IdField: &schemapb.IDs_IntId{
867+
IntId: &schemapb.LongArray{
868+
Data: []int64{maxInt64PK},
869+
},
870+
},
871+
},
872+
FieldsData: []*schemapb.FieldData{fieldDataMaxPK, vectorDataMaxPK},
873+
HasMoreResult: false,
874+
}
875+
876+
// Create a second result with no data (empty result)
877+
rEmpty := &internalpb.RetrieveResults{
878+
Ids: &schemapb.IDs{
879+
IdField: &schemapb.IDs_IntId{
880+
IntId: &schemapb.LongArray{
881+
Data: []int64{},
882+
},
883+
},
884+
},
885+
FieldsData: []*schemapb.FieldData{},
886+
HasMoreResult: false,
887+
}
888+
889+
// Test with MaxInt64 first to ensure it's handled correctly when pk == minIntPK && firstInt
890+
// The reduced result should include the maxInt64PK result
891+
result, err := reduceRetrieveResults(context.Background(),
892+
[]*internalpb.RetrieveResults{rMaxPK, rEmpty},
893+
&queryParams{limit: typeutil.Unlimited})
894+
assert.NoError(t, err)
895+
assert.Equal(t, 2, len(result.GetFieldsData()))
896+
// Should include the maxInt64PK result
897+
assert.Equal(t, []int64{maxInt64PK}, result.GetFieldsData()[0].GetScalars().GetLongData().Data)
898+
})
843899
})
844900
}
845901

pkg/util/typeutil/schema.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2045,6 +2045,7 @@ func SelectMinPK[T ResultWithID](results []T, cursors []int64) (int, bool) {
20452045
minIntPK int64 = math.MaxInt64
20462046

20472047
firstStr = true
2048+
firstInt = true
20482049
minStrPK string
20492050
)
20502051
for i, cursor := range cursors {
@@ -2064,7 +2065,8 @@ func SelectMinPK[T ResultWithID](results []T, cursors []int64) (int, bool) {
20642065
sel = i
20652066
}
20662067
case int64:
2067-
if pk < minIntPK {
2068+
if firstInt || pk < minIntPK {
2069+
firstInt = false
20682070
minIntPK = pk
20692071
sel = i
20702072
}

0 commit comments

Comments
 (0)