package planparserv2

import (
	"fmt"
	"math/rand"
	"strings"
	"sync"
	"testing"

	"github.com/antlr4-go/antlr/v4"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"google.golang.org/protobuf/proto"

	"github.com/milvus-io/milvus-proto/go-api/v3/commonpb"
	"github.com/milvus-io/milvus-proto/go-api/v3/schemapb"
	"github.com/milvus-io/milvus/internal/util/function/rerank"
	"github.com/milvus-io/milvus/pkg/v3/common"
	"github.com/milvus-io/milvus/pkg/v3/proto/planpb"
	"github.com/milvus-io/milvus/pkg/v3/util/merr"
	"github.com/milvus-io/milvus/pkg/v3/util/typeutil"
)

func newTestSchema(EnableDynamicField bool) *schemapb.CollectionSchema {
	fields := []*schemapb.FieldSchema{
		{FieldID: 0, Name: "FieldID", IsPrimaryKey: false, Description: "field no.1", DataType: schemapb.DataType_Int64},
	}

	for name, value := range schemapb.DataType_value {
		dataType := schemapb.DataType(value)
		newField := &schemapb.FieldSchema{
			FieldID: int64(100 + value), Name: name + "Field", IsPrimaryKey: false, Description: "", DataType: dataType,
		}
		if dataType == schemapb.DataType_Array {
			newField.ElementType = schemapb.DataType_Int64
		}
		fields = append(fields, newField)
	}
	if EnableDynamicField {
		fields = append(fields, &schemapb.FieldSchema{
			FieldID: 130, Name: common.MetaFieldName, IsPrimaryKey: false, Description: "dynamic field", DataType: schemapb.DataType_JSON,
			IsDynamic: true,
		})
	}

	fields = append(fields, &schemapb.FieldSchema{
		FieldID: 131, Name: "StringArrayField", IsPrimaryKey: false, Description: "string array field",
		DataType:    schemapb.DataType_Array,
		ElementType: schemapb.DataType_VarChar,
	})

	structArrayField := &schemapb.StructArrayFieldSchema{
		FieldID: 132, Name: "struct_array", Fields: []*schemapb.FieldSchema{
			{
				FieldID: 133, Name: "struct_array[sub_str]", IsPrimaryKey: false, Description: "sub struct array field for string",
				DataType:    schemapb.DataType_Array,
				ElementType: schemapb.DataType_VarChar,
			},
			{
				FieldID: 134, Name: "struct_array[sub_int]", IsPrimaryKey: false, Description: "sub struct array field for int",
				DataType:    schemapb.DataType_Array,
				ElementType: schemapb.DataType_Int32,
			},
		},
	}

	return &schemapb.CollectionSchema{
		Name:               "test",
		Description:        "schema for test used",
		AutoID:             true,
		Fields:             fields,
		StructArrayFields:  []*schemapb.StructArrayFieldSchema{structArrayField},
		EnableDynamicField: EnableDynamicField,
	}
}

func enableMatch(schema *schemapb.CollectionSchema) {
	for _, field := range schema.Fields {
		if typeutil.IsStringType(field.DataType) {
			field.TypeParams = append(field.TypeParams, &commonpb.KeyValuePair{
				Key: "enable_match", Value: "True",
			})
		}
	}
}

func newTestSchemaHelper(t *testing.T) *typeutil.SchemaHelper {
	schema := newTestSchema(true)
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(t, err)
	return schemaHelper
}

func assertValidExpr(t *testing.T, helper *typeutil.SchemaHelper, exprStr string) {
	expr, err := ParseExpr(helper, exprStr, nil)
	assert.NoError(t, err, exprStr)
	// fmt.Printf("expr: %s\n", exprStr)
	assert.NotNil(t, expr, exprStr)
	ShowExpr(expr)
}

func assertInvalidExpr(t *testing.T, helper *typeutil.SchemaHelper, exprStr string) {
	_, err := ParseExpr(helper, exprStr, nil)
	assert.Error(t, err, exprStr)
}

func TestExpr_Term(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`BoolField in [true, false]`,
		`Int8Field in [1, 2]`,
		`Int16Field in [3, 4]`,
		`Int32Field in [5, 6]`,
		`Int64Field in [7, 8]`,
		`FloatField in [9.0, 10.0]`,
		`DoubleField in [11.0, 12.0]`,
		`StringField in ["str13", "str14"]`,
		`VarCharField in ["str15", "str16"]`,
		`FloatField in [1373, 115]`,
		`Int64Field in [17]`,
		`Int64Field in []`,
		`Int64Field not in []`,
		`JSONField["A"] in [1, 10]`,
		`JSONField["A"] in []`,
		`$meta["A"] in [1, 10]`,
		`$meta["A"] in []`,
		`A in [1, 10]`,
		`A in []`,
		`A in ["abc", "def"]`,
		`A in ["1", "2", "abc", "def"]`,
		`A in ["1", 2, "abc", 2.2]`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}
}

// assertNullLiteralRejected checks that the expression is rejected specifically
// because of the bare-NULL reserved-word guard, i.e. it returns the actionable
// message rather than the misleading "field NULL not exist" (issue #50882).
func assertNullLiteralRejected(t *testing.T, helper *typeutil.SchemaHelper, exprStr string) {
	_, err := ParseExpr(helper, exprStr, nil)
	if assert.Error(t, err, exprStr) {
		assert.Contains(t, err.Error(), "NULL literal is not supported in expressions", exprStr)
	}
}

func TestExpr_NullLiteral(t *testing.T) {
	// NULL is not a value literal. It is lexed as a bare identifier, so it must be
	// rejected wherever it appears in column/value position (issue #50882). The
	// behavior must be identical whether or not a dynamic field is present: without
	// one a bare NULL fails a field lookup; with one it would otherwise be silently
	// mistaken for a dynamic JSON key named "NULL". Use `<field> is null` /
	// `is not null` to compare against null instead.
	rejected := []string{
		// inside an `in [...]` value list (the exact shape from the issue)
		`Int64Field in [6560, NULL, 6722, -7856, -6757]`,
		`Int64Field in [null]`,
		`Int64Field in [NULL]`,
		`Int64Field not in [1, Null]`,
		`VarCharField in ["a", null, "b"]`,
		`(not (not ((Int64Field is not null) and (Int64Field in [1, NULL, 2]))))`,
		// NULL as the tested column on the left of `in`
		`NULL in [1, 2]`,
		// binary comparison, either side
		`Int64Field == NULL`,
		`NULL == Int64Field`,
		`NULL > 5`,
		`Int64Field != NULL`,
		// range comparison
		`1 < NULL < 5`,
		`Int64Field < NULL`,
		// logical / unary operands
		`NULL and Int64Field > 0`,
		`Int64Field > 0 or NULL`,
		`not NULL`,
		// function arguments
		`array_length(NULL) > 0`,
		`array_contains(NULL, 1)`,
		// `is null` / `is not null` with NULL as the target (nonsensical)
		`NULL is null`,
		`NULL is not null`,
		// NULL as a JSON / array subscript base — a separate lookup path
		// (getColumnInfoFromJSONIdentifier) that bypasses translateIdentifier
		`NULL["x"] == 1`,
		`NULL[0] > 1`,
		// case-insensitive: any casing of the reserved word is rejected
		`Int64Field == Null`,
		`Int64Field == nUlL`,
		`Int64Field == NuLL`,
	}

	// Valid expressions that must NOT be affected by the guard.
	validBoth := []string{
		// the real "is null" predicates the guard points users to
		`Int64Field is null`,
		`Int64Field is not null`,
		`JSONField["a"] is null`,
		// a JSON key literally named "null" stays reachable via quoting: the base
		// identifier is the field name, not "null"
		`JSONField["null"] == 1`,
		`JSONField['null'] == 1`,
		// the string literal "null" is a value, not an identifier
		`VarCharField == "null"`,
		`VarCharField in ["null", "NULL"]`,
	}
	// Only valid when the dynamic field exists.
	validDynamicOnly := []string{
		`$meta["null"] == 1`,
		`A == 1`, // sanity: an arbitrary dynamic key still resolves
	}

	for _, dynamic := range []bool{true, false} {
		t.Run(fmt.Sprintf("dynamic=%v", dynamic), func(t *testing.T) {
			helper, err := typeutil.CreateSchemaHelper(newTestSchema(dynamic))
			assert.NoError(t, err)

			for _, exprStr := range rejected {
				assertNullLiteralRejected(t, helper, exprStr)
			}
			for _, exprStr := range validBoth {
				assertValidExpr(t, helper, exprStr)
			}
			if dynamic {
				for _, exprStr := range validDynamicOnly {
					assertValidExpr(t, helper, exprStr)
				}
			}
		})
	}
}

// TestExpr_NullLiteral_LegacyNullField locks the schema-aware side of the
// bare-NULL guard: "null" only became a create-time keyword together with this
// guard, so a legacy collection may own a field literally named "null", and the
// bare identifier is the ONLY syntax that can reference a top-level scalar
// field (quoting like field["null"] reaches JSON sub-keys only). Such a field
// must stay queryable; the strict GetFieldFromName check makes it resolve while
// everything else keeps the reserved-word rejection (see errNullLiteral).
func TestExpr_NullLiteral_LegacyNullField(t *testing.T) {
	withNullField := func(dataType schemapb.DataType) *typeutil.SchemaHelper {
		schema := newTestSchema(true)
		schema.Fields = append(schema.Fields, &schemapb.FieldSchema{
			FieldID: 199, Name: "null", Description: "legacy field literally named null", DataType: dataType,
		})
		helper, err := typeutil.CreateSchemaHelper(schema)
		require.NoError(t, err)
		return helper
	}

	t.Run("scalar field named null", func(t *testing.T) {
		helper := withNullField(schemapb.DataType_Int64)

		// The bare identifier resolves to the declared field, as before the guard.
		assertValidExpr(t, helper, `null > 5`)
		assertValidExpr(t, helper, `null in [1, 2]`)
		assertValidExpr(t, helper, `Int64Field == null`)
		// The guard previously rejected these outright; schema-aware turns them
		// into valid "is the null-named field NULL?" predicates.
		assertValidExpr(t, helper, `null is null`)
		assertValidExpr(t, helper, `null is not null`)
		// Strict lookup is exact-case: a differently-cased NULL does not match the
		// declared field and keeps the reserved-word rejection (pre-guard it would
		// have been misparsed as the dynamic JSON key $meta["NULL"]).
		assertNullLiteralRejected(t, helper, `NULL > 5`)
		assertNullLiteralRejected(t, helper, `Int64Field == Null`)
	})

	t.Run("json field named null", func(t *testing.T) {
		helper := withNullField(schemapb.DataType_JSON)

		// The subscript base resolves via the same schema-aware guard in
		// getColumnInfoFromJSONIdentifier.
		assertValidExpr(t, helper, `null["x"] == 1`)
		assertValidExpr(t, helper, `null["x"] is null`)
		assertNullLiteralRejected(t, helper, `NULL["x"] == 1`)
	})
}

func TestExpr_Call(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	testcases := []struct {
		CallExpr     string
		FunctionName string
		ParameterNum int
	}{
		{`hello123()`, "hello123", 0},
		{`lt(Int32Field)`, "lt", 1},
		// test parens
		{`lt((((Int32Field))))`, "lt", 1},
		{`empty(VarCharField,)`, "empty", 1},
		{`f2(Int64Field)`, "f2", 1},
		{`f2(Int64Field, 4)`, "f2", 2},
		{`f3(JSON_FIELD["A"], Int32Field)`, "f3", 2},
		{`f5(3+3, Int32Field)`, "f5", 2},
	}
	for _, testcase := range testcases {
		expr, err := ParseExpr(helper, testcase.CallExpr, nil)
		assert.NoError(t, err, testcase)
		assert.Equal(t, testcase.FunctionName, expr.GetCallExpr().FunctionName, testcase)
		assert.Equal(t, testcase.ParameterNum, len(expr.GetCallExpr().FunctionParameters), testcase)
		ShowExpr(expr)
	}

	expr, err := ParseExpr(helper, "xxx(1+1, !true, f(10+10))", nil)
	assert.NoError(t, err)
	assert.Equal(t, "xxx", expr.GetCallExpr().FunctionName)
	assert.Equal(t, 3, len(expr.GetCallExpr().FunctionParameters))
	assert.Equal(t, int64(2), expr.GetCallExpr().GetFunctionParameters()[0].GetValueExpr().GetValue().GetInt64Val())
	assert.Equal(t, false, expr.GetCallExpr().GetFunctionParameters()[1].GetValueExpr().GetValue().GetBoolVal())
	assert.Equal(t, int64(20), expr.GetCallExpr().GetFunctionParameters()[2].GetCallExpr().GetFunctionParameters()[0].GetValueExpr().GetValue().GetInt64Val())

	expr, err = ParseExpr(helper, "ceil(pow(1.5*Int32Field,0.58))", nil)
	assert.Error(t, err)
	assert.Nil(t, expr)
}

func TestExpr_Compare(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`Int8Field < Int16Field`,
		`Int16Field <= Int32Field`,
		`Int32Field > Int64Field`,
		`Int64Field >= FloatField`,
		`FloatField == DoubleField`,
		`StringField != VarCharField`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}

	exprStrs = []string{
		`BoolField == false + true`,
		`StringField == "1" + "2"`,
		`BoolField == false - true`,
		`StringField == "1" - "2"`,
		`BoolField == false * true`,
		`StringField == "1" * "2"`,
		`BoolField == false / true`,
		`StringField == "1" / "2"`,
		`BoolField == false % true`,
		`StringField == "1" % "2"`,
	}

	for _, exprStr := range exprStrs {
		assertInvalidExpr(t, helper, exprStr)
	}
}

func TestExpr_UnaryRange(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`Int8Field < 0`,
		`Int16Field <= 1`,
		`Int32Field > 2`,
		`Int64Field >= 3`,
		`FloatField == 4.0`,
		`FloatField == 2`,
		`DoubleField != 5.0`,
		`StringField > "str6"`,
		`VarCharField <= "str7"`,
		`JSONField["A"] > 10`,
		`$meta["A"] > 10`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}
}

func TestExpr_Like(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	expr := `A like "8\\_0%"`
	plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	fmt.Println(plan)
	assert.Equal(t, planpb.OpType_PrefixMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, "8_0", plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	expr = `A like "8_\\_0%"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	fmt.Println(plan)
	assert.Equal(t, planpb.OpType_Match, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, `8_\_0%`, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	expr = `A like "8\\%-0%"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	fmt.Println(plan)
	assert.Equal(t, planpb.OpType_PrefixMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, `8%-0`, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	expr = `A like "abc"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_Equal, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, "abc", plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())
}

func TestExpr_RawString(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	check := func(expr string, wantOp planpb.OpType, wantVal string) {
		plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.NoError(t, err, expr)
		assert.NotNil(t, plan, expr)
		ur := plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr()
		assert.Equal(t, wantOp, ur.GetOp(), expr)
		assert.Equal(t, wantVal, ur.GetValue().GetStringVal(), expr)
	}

	// In a raw string the backslash is NOT a string-literal escape: the content
	// is taken verbatim (no \n/\b/\\ processing), so == sees exactly what's typed.
	check(`A == r"a\b"`, planpb.OpType_Equal, `a\b`)
	check(`A == r"a\nb"`, planpb.OpType_Equal, `a\nb`)
	check(`A == r"a\\b"`, planpb.OpType_Equal, `a\\b`)

	// LIKE: the raw content still passes through the LIKE escape layer (\% -> %),
	// exactly like BigQuery's r'\%', but needs far fewer backslashes because the
	// string-literal Unquote layer is gone.
	check(`A like r"abc%"`, planpb.OpType_PrefixMatch, `abc`)
	check(`A like r"%abc%"`, planpb.OpType_InnerMatch, `abc`)
	check(`A like r"\%"`, planpb.OpType_Equal, `%`) // literal %
	check(`A like r"a\_b%"`, planpb.OpType_PrefixMatch, `a_b`)
	check(`A like r"\\%"`, planpb.OpType_PrefixMatch, `\`) // prefix of one literal backslash
	check(`A like r"a\\b"`, planpb.OpType_Equal, `a\b`)    // literal a\b
	check(`A like r'\\%'`, planpb.OpType_PrefixMatch, `\`) // single-quoted raw string

	// uppercase R prefix works too
	check(`A like R"abc%"`, planpb.OpType_PrefixMatch, `abc`)
	// empty raw string
	check(`A == r""`, planpb.OpType_Equal, ``)
	// the opposite quote needs no escaping inside a raw string
	check(`A == r"a'b"`, planpb.OpType_Equal, `a'b`)
	check(`A == r'a"b'`, planpb.OpType_Equal, `a"b`)

	// IN list accepts raw strings, taken verbatim
	inPlan, err := CreateSearchPlan(helper, `A in [r"a\b", r"c\d"]`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	vals := inPlan.GetVectorAnns().GetPredicates().GetTermExpr().GetValues()
	assert.Len(t, vals, 2)
	assert.Equal(t, `a\b`, vals[0].GetStringVal())
	assert.Equal(t, `c\d`, vals[1].GetStringVal())

	// a raw string cannot end with an odd number of backslashes (unterminated)
	assertInvalidExpr(t, helper, `A == r"x\"`)

	// raw string works as a JSON path key; JSON keys are already verbatim, so a
	// raw key yields the same nested path as a normal key.
	jPlan, err := CreateSearchPlan(helper, `JSONField[r"a\b"] == 1`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	jNorm, err := CreateSearchPlan(helper, `JSONField["a\b"] == 1`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	assert.Equal(t, []string{`a\b`}, jPlan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath())
	assert.Equal(t,
		jNorm.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath(),
		jPlan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath())

	// Raw needs 2 backslashes where a normal literal needs 4 — same final operand.
	rawPlan, err := CreateSearchPlan(helper, `A like r"\\%"`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	normPlan, err := CreateSearchPlan(helper, `A like "\\\\%"`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	assert.Equal(t,
		normPlan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal(),
		rawPlan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	// Raw string in a regex (=~) reaches the engine verbatim (no escape pass).
	rePlan, err := CreateSearchPlan(helper, `A =~ r"\d+"`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	reUR := rePlan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr()
	assert.Equal(t, planpb.OpType_RegexMatch, reUR.GetOp())
	assert.Equal(t, `\d+`, reUR.GetValue().GetStringVal())

	// --- raw strings must stay VERBATIM even for content the parser otherwise
	// rewrites before/around the raw handling: CJK runes (convertHanToASCII runs
	// pre-lex) and \uXXXX escapes (decodeUnicode runs on JSON paths). Issue
	// #43864 follow-up — without the raw-span exemption these silently leak. ---

	// CJK in a raw value reaches the matcher verbatim, not as \uXXXX.
	check(`A == r"中文"`, planpb.OpType_Equal, `中文`)
	check(`A like r"中%"`, planpb.OpType_PrefixMatch, `中`)

	// CJK in a raw regex reaches the engine verbatim too.
	check(`A =~ r"中文"`, planpb.OpType_RegexMatch, `中文`)

	// A raw JSON key with a literal A is NOT unicode-decoded: the key is the
	// 6 verbatim bytes, not the decoded "A".
	jUni, err := CreateSearchPlan(helper, `JSONField[r"\u0041"] == 1`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	assert.Equal(t, []string{`\u0041`}, jUni.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath())

	// A raw JSON key with CJK reaches the plan verbatim (regression guard: today
	// this only works because decodeUnicode reverses convertHanToASCII; it must
	// keep working once both passes skip raw spans).
	jHan, err := CreateSearchPlan(helper, `JSONField[r"中"] == 1`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	assert.Equal(t, []string{`中`}, jHan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath())
	// A raw VALUE with a literal A stays verbatim too (the value path has no
	// unicode decoding either) — locks the value/key consistency.
	check(`A == r"\u0041"`, planpb.OpType_Equal, `\u0041`)

	// Mixed raw + normal keys in one JSON path: each segment is classified
	// independently — the raw segment is verbatim, the normal segment decodes.
	jMix1, err := CreateSearchPlan(helper, `JSONField[r"中"]["b"] == 1`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	assert.Equal(t, []string{`中`, `b`}, jMix1.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath())

	jMix2, err := CreateSearchPlan(helper, `JSONField["a"][r"\u0041"] == 1`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	assert.Equal(t, []string{`a`, `\u0041`}, jMix2.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath())

	// Single-quoted raw JSON key behaves the same as the double-quoted form.
	jSq, err := CreateSearchPlan(helper, `JSONField[r'\u0041'] == 1`, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.NoError(t, err)
	assert.Equal(t, []string{`\u0041`}, jSq.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath())
}

// TestExpr_RawString_LikeEscapeModel exercises the LIKE escape model (issue
// #43864) end-to-end through the raw-string literal r"...". Because a raw string
// drops the string-literal Unquote layer, a backslash reaches the LIKE pattern
// layer verbatim, so these read with the same single backslash the C++ canonical
// matcher (RegexQuery.cpp) uses — no doubled/quadrupled backslashes. Each case
// asserts the optimized op and the literal operand the executor must match
// verbatim.
func TestExpr_RawString_LikeEscapeModel(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	check := func(expr string, wantOp planpb.OpType, wantVal string) {
		plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.NoError(t, err, expr)
		assert.NotNil(t, plan, expr)
		ur := plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr()
		assert.Equal(t, wantOp, ur.GetOp(), expr)
		assert.Equal(t, wantVal, ur.GetValue().GetStringVal(), expr)
	}

	// An escaped wildcard is a LITERAL byte in the operand: the optimized op
	// carries a literal '%'/'_', which the C++ side matches verbatim and must NOT
	// re-interpret as a wildcard.
	check(`A like r"a\%bc"`, planpb.OpType_Equal, `a%bc`)
	check(`A like r"a\_bc"`, planpb.OpType_Equal, `a_bc`)
	check(`A like r"abc\%%"`, planpb.OpType_PrefixMatch, `abc%`)
	check(`A like r"%abc\%"`, planpb.OpType_PostfixMatch, `abc%`)
	check(`A like r"%abc\%%"`, planpb.OpType_InnerMatch, `abc%`)

	// A literal '\%' and an UNescaped '%' coexist: the literal lands in the
	// operand verbatim, while the bare '%' is the prefix/postfix/inner boundary
	// the C++ matcher expands to an ANY-length span.
	check(`A like r"abc\%def%"`, planpb.OpType_PrefixMatch, `abc%def`)
	check(`A like r"%abc\%def"`, planpb.OpType_PostfixMatch, `abc%def`)
	check(`A like r"%abc\%def%"`, planpb.OpType_InnerMatch, `abc%def`)

	// A backslash escapes ANY next byte, not only wildcards: r"\a" -> literal "a".
	check(`A like r"\a"`, planpb.OpType_Equal, `a`)

	// A raw "\\" collapses to one literal backslash at the pattern layer.
	check(`A like r"a\\b"`, planpb.OpType_Equal, `a\b`)
	check(`A like r"%a\\b%"`, planpb.OpType_InnerMatch, `a\b`)

	// A dangling trailing backslash cannot be written as a raw string at all — a
	// raw string may not end in an odd number of backslashes (it would not
	// terminate). So the unterminated raw form is a parse error, and the literal
	// trailing-backslash pattern can only be expressed via a normal string, where
	// it is not optimizable and falls back to OpType_Match (the C++ matcher then
	// raises ExprInvalid at execution).
	assertInvalidExpr(t, helper, `A like r"abc\"`)
	check(`A like "abc\\"`, planpb.OpType_Match, `abc\`)
}

func TestExpr_RegexMatch(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	// --- Regex-to-LIKE optimization tests ---

	// Pure literal "abc" stays RegexMatch. RE2's literal PartialMatch path is
	// faster than Milvus InnerMatch in current growing-segment benchmarks.
	expr := `A =~ "abc"`
	plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_RegexMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, "abc", plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	// "^abc" → PrefixMatch
	expr = `A =~ "^abc"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_PrefixMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, "abc", plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	// "abc$" → PostfixMatch
	expr = `A =~ "abc$"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_PostfixMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, "abc", plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	// "^abc$" → Equal
	expr = `A =~ "^abc$"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_Equal, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, "abc", plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	// Escaped metacharacter without anchors stays RegexMatch
	expr = `A =~ "file\\.txt"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_RegexMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, `file\.txt`, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	// --- Patterns that stay as RegexMatch ---

	// Regex with metacharacters → stays RegexMatch
	expr = `A =~ "a.*b"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_RegexMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, "a.*b", plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetValue().GetStringVal())

	// Character class → stays RegexMatch
	expr = `A =~ "[0-9]+"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_RegexMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())

	// Empty pattern → stays RegexMatch (matches everything)
	expr = `A =~ ""`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_RegexMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())

	// --- Patterns that MUST stay RegexMatch (never downgrade to LIKE) ---

	// Unicode property \p{...}
	expr = `A =~ "\\p{Han}+"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk: 0, MetricType: "", SearchParams: "", RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_RegexMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())

	// Named group (?P<name>...)
	expr = `A =~ "(?P<user>[a-z]+)@host"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk: 0, MetricType: "", SearchParams: "", RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_RegexMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())

	// Inline flag (?m)
	expr = `A =~ "(?m)^start"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk: 0, MetricType: "", SearchParams: "", RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_RegexMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())

	// Shorthand class \d
	expr = `A =~ "\\d{3}-\\d{4}"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk: 0, MetricType: "", SearchParams: "", RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	assert.Equal(t, planpb.OpType_RegexMatch, plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp())

	// --- Error cases ---

	// Invalid regex pattern — unclosed bracket
	expr = `A =~ "[unclosed"`
	assertInvalidExpr(t, helper, expr)

	// Non-string field — should error
	expr = `Int64Field =~ "abc"`
	assertInvalidExpr(t, helper, expr)

	// --- Negation ---

	// !~ with pure literal → NOT(RegexMatch)
	expr = `A !~ "abc"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	predicates := plan.GetVectorAnns().GetPredicates()
	assert.Equal(t, planpb.UnaryExpr_Not, predicates.GetUnaryExpr().GetOp())
	assert.Equal(t, planpb.OpType_RegexMatch, predicates.GetUnaryExpr().GetChild().GetUnaryRangeExpr().GetOp())
	assert.Equal(t, "abc", predicates.GetUnaryExpr().GetChild().GetUnaryRangeExpr().GetValue().GetStringVal())

	// !~ with metacharacters → NOT(RegexMatch)
	expr = `A !~ "a.*b"`
	plan, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	assert.NotNil(t, plan)
	predicates = plan.GetVectorAnns().GetPredicates()
	assert.Equal(t, planpb.UnaryExpr_Not, predicates.GetUnaryExpr().GetOp())
	assert.Equal(t, planpb.OpType_RegexMatch, predicates.GetUnaryExpr().GetChild().GetUnaryRangeExpr().GetOp())

	// --- JSON and other field types ---
	validExprs := []string{
		`JSONField["A"] =~ "abc"`,
		`VarCharField =~ "^prefix"`,
	}
	for _, exprStr := range validExprs {
		assertValidExpr(t, helper, exprStr)
	}

	// --- Comprehensive tryOptimizeRegexToLike edge cases ---

	// Helper to check op type for =~ expressions
	checkOp := func(t *testing.T, exprStr string, expectedOp planpb.OpType) {
		t.Helper()
		p, e := CreateSearchPlan(helper, exprStr, "FloatVectorField", &planpb.QueryInfo{
			Topk: 0, MetricType: "", SearchParams: "", RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, e, exprStr)
		assert.NotNil(t, p, exprStr)
		assert.Equal(t, expectedOp,
			p.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetOp(),
			"wrong OpType for: %s", exprStr)
	}
	checkVal := func(t *testing.T, exprStr string, expectedOp planpb.OpType, expectedVal string) {
		t.Helper()
		p, e := CreateSearchPlan(helper, exprStr, "FloatVectorField", &planpb.QueryInfo{
			Topk: 0, MetricType: "", SearchParams: "", RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, e, exprStr)
		assert.NotNil(t, p, exprStr)
		ure := p.GetVectorAnns().GetPredicates().GetUnaryRangeExpr()
		assert.Equal(t, expectedOp, ure.GetOp(), "wrong OpType for: %s", exprStr)
		assert.Equal(t, expectedVal, ure.GetValue().GetStringVal(), "wrong value for: %s", exprStr)
	}

	// Optimizable patterns
	checkVal(t, `A =~ "hello"`, planpb.OpType_RegexMatch, "hello")
	checkVal(t, `A =~ "^hello"`, planpb.OpType_PrefixMatch, "hello")
	checkVal(t, `A =~ "hello$"`, planpb.OpType_PostfixMatch, "hello")
	checkVal(t, `A =~ "^hello$"`, planpb.OpType_Equal, "hello")
	checkVal(t, `A =~ "^$"`, planpb.OpType_Equal, "")
	checkVal(t, `A =~ "hello world"`, planpb.OpType_RegexMatch, "hello world")
	checkVal(t, `A =~ "file\\.txt"`, planpb.OpType_RegexMatch, `file\.txt`)
	checkVal(t, `A =~ "^file\\.txt$"`, planpb.OpType_Equal, "file.txt")
	checkVal(t, `A =~ "a\\(b\\)"`, planpb.OpType_RegexMatch, `a\(b\)`)
	checkVal(t, `A =~ "price\\$10"`, planpb.OpType_RegexMatch, `price\$10`)
	checkVal(t, `A =~ "back\\\\slash"`, planpb.OpType_RegexMatch, `back\\slash`)

	// Non-optimizable: metacharacters → stay RegexMatch
	checkOp(t, `A =~ "a.*b"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "a.b"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "[a-z]"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "a+"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "a?"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "a{2}"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "(abc)"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "a|b"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ ""`, planpb.OpType_RegexMatch)

	// Non-optimizable: shorthand classes, control chars, special escapes
	checkOp(t, `A =~ "\\d+"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "\\w+"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "\\s"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "\\b"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "\\n"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "\\t"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "\\x41"`, planpb.OpType_RegexMatch)

	// Non-optimizable: Unicode property, named groups, inline flags
	checkOp(t, `A =~ "\\p{Han}"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "(?P<name>[a-z]+)"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "(?i)hello"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "(?m)^start"`, planpb.OpType_RegexMatch)
	checkOp(t, `A =~ "(?s)a.b"`, planpb.OpType_RegexMatch)

	// Edge: escaped $ at end should NOT be treated as anchor
	checkVal(t, `A =~ "price\\$"`, planpb.OpType_RegexMatch, `price\$`)
	checkVal(t, `A =~ "^price\\$"`, planpb.OpType_PrefixMatch, "price$")
}

func TestExpr_TextMatch(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`text_match(VarCharField, "query")`,
		`text_match(TextField, "query")`,
	}
	for _, exprStr := range exprStrs {
		assertInvalidExpr(t, helper, exprStr)
	}

	enableMatch(schema)
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}

	unsupported := []string{
		`text_match(not_exist, "query")`,
		`text_match(BoolField, "query")`,
	}
	for _, exprStr := range unsupported {
		assertInvalidExpr(t, helper, exprStr)
	}
}

func TestExpr_TextMatchFuzzy(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	// the field must enable match first, otherwise fuzzy match is rejected.
	assertInvalidExpr(t, helper, `text_match_fuzzy(VarCharField, "query", max_edit_distance=1)`)
	enableMatch(schema)

	for _, v := range []int64{0, 1, 2} {
		expr := fmt.Sprintf(`text_match_fuzzy(VarCharField, "query", max_edit_distance=%d)`, v)
		plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         10,
			MetricType:   "L2",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err, expr)
		assert.NotNil(t, plan)

		ure := plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr()
		assert.NotNil(t, ure)
		assert.Equal(t, planpb.OpType_TextMatchFuzzy, ure.GetOp())
		assert.Equal(t, "query", ure.GetValue().GetStringVal())
		extra := ure.GetExtraValues()
		assert.Equal(t, 1, len(extra))
		assert.Equal(t, v, extra[0].GetInt64Val())
	}

	{
		// a templated query is filled at plan time and the edit distance survives.
		expr := `text_match_fuzzy(VarCharField, {q}, max_edit_distance=2)`
		mv := map[string]*schemapb.TemplateValue{
			"q": generateTemplateValue(schemapb.DataType_VarChar, "hello"),
		}
		plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:       10,
			MetricType: "L2",
		}, mv, nil)
		assert.NoError(t, err, expr)
		ure := plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr()
		assert.NotNil(t, ure)
		assert.Equal(t, planpb.OpType_TextMatchFuzzy, ure.GetOp())
		assert.Equal(t, "hello", ure.GetValue().GetStringVal())
		extra := ure.GetExtraValues()
		assert.Equal(t, 1, len(extra))
		assert.Equal(t, int64(2), extra[0].GetInt64Val())
	}

	{
		expr := `text_match_fuzzy(VarCharField, "query", max_edit_distance=3)`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "max_edit_distance should be in [0, 2]")
	}

	{
		// the distance argument is required by the grammar.
		expr := `text_match_fuzzy(VarCharField, "query")`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
	}

	{
		expr := `text_match_fuzzy(VarCharField, "query", max_edit_distance=1.5)`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
	}

	{
		expr := `text_match_fuzzy(VarCharField, "query", max_edit_distance=9223372036854775808)`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "invalid max_edit_distance value")
	}

	unsupported := []string{
		`text_match_fuzzy(not_exist, "query", max_edit_distance=1)`,
		`text_match_fuzzy(BoolField, "query", max_edit_distance=1)`,
	}
	for _, exprStr := range unsupported {
		assertInvalidExpr(t, helper, exprStr)
	}
}

func TestExpr_TextMatchFuzzy_SoftKeyword(t *testing.T) {
	// A wrong option name is rejected: the option name is a soft keyword, so
	// only "max_edit_distance" (any case) is accepted.
	{
		schema := newTestSchema(true)
		enableMatch(schema)
		helper, err := typeutil.CreateSchemaHelper(schema)
		assert.NoError(t, err)
		expr := `text_match_fuzzy(VarCharField, "query", fuzziness=1)`
		_, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "expected max_edit_distance")
	}

	// The option name is case-insensitive.
	{
		schema := newTestSchema(true)
		enableMatch(schema)
		helper, err := typeutil.CreateSchemaHelper(schema)
		assert.NoError(t, err)
		expr := `text_match_fuzzy(VarCharField, "query", MAX_EDIT_DISTANCE=1)`
		plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk: 10, MetricType: "L2",
		}, nil, nil)
		assert.NoError(t, err, expr)
		assert.NotNil(t, plan)
	}

	// max_edit_distance is a soft keyword, not a reserved word: a scalar field
	// literally named "max_edit_distance" stays usable in an ordinary filter
	// (issue #51058 — hard-keywording it would break such collections).
	{
		schema := newTestSchema(false)
		schema.Fields = append(schema.Fields, &schemapb.FieldSchema{
			FieldID: 9999, Name: "max_edit_distance", DataType: schemapb.DataType_Int64,
		})
		helper, err := typeutil.CreateSchemaHelper(schema)
		assert.NoError(t, err)
		expr := `max_edit_distance > 1`
		plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk: 10, MetricType: "L2",
		}, nil, nil)
		assert.NoError(t, err, expr)
		assert.NotNil(t, plan)
	}
}

func TestExpr_TextMatch_MinShouldMatch(t *testing.T) {
	schema := newTestSchema(true)
	enableMatch(schema)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	for _, v := range []int64{1, 2, 1000} {
		expr := fmt.Sprintf(`text_match(VarCharField, "query", minimum_should_match=%d)`, v)
		plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         10,
			MetricType:   "L2",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err, expr)
		assert.NotNil(t, plan)

		predicates := plan.GetVectorAnns().GetPredicates()
		assert.NotNil(t, predicates)
		ure := predicates.GetUnaryRangeExpr()
		assert.NotNil(t, ure)
		assert.Equal(t, planpb.OpType_TextMatch, ure.GetOp())
		assert.Equal(t, "query", ure.GetValue().GetStringVal())
		extra := ure.GetExtraValues()
		assert.Equal(t, 1, len(extra))
		assert.Equal(t, v, extra[0].GetInt64Val())
	}

	{
		expr := `text_match(VarCharField, "query", minimum_should_match=0)`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "minimum_should_match should be >= 1")
	}

	{
		expr := `text_match(VarCharField, "query", minimum_should_match=1001)`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "minimum_should_match should be <= 1000")
	}

	{
		expr := `text_match(VarCharField, "query", minimum_should_match=1.5)`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
	}

	{
		expr := `text_match(VarCharField, "query", minimum_should_match={min})`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
		// grammar rejects placeholder before visitor; accept either parse error or visitor error
		errMsg := err.Error()
		assert.True(t, strings.Contains(errMsg, "mismatched input") || strings.Contains(errMsg, "minimum_should_match should be a const integer expression"), errMsg)
	}

	{
		expr := `text_match(VarCharField, "query", minimum_should_match=9223372036854775808)`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "invalid minimum_should_match value")
	}

	{
		expr := `text_match(VarCharField, "\中国")`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
	}

	{
		expr := `text_match(VarCharField, "query", minimum_should_match=9223372036854775808)`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "invalid minimum_should_match value")
	}

	{
		expr := `text_match(VarCharField, "\中国")`
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err)
	}
}

func TestExpr_TextMatch_MinShouldMatch_NilValue_Coverage(t *testing.T) {
	// This test is specifically to cover the error case in validateAndExtractMinShouldMatch
	// which handles the edge case where minShouldMatchExpr is an ExprWithType

	// Test case 1: ExprWithType with a ColumnExpr
	// This will make getValueExpr return nil
	exprWithColumnExpr := &ExprWithType{
		expr: &planpb.Expr{
			Expr: &planpb.Expr_ColumnExpr{
				ColumnExpr: &planpb.ColumnExpr{
					Info: &planpb.ColumnInfo{
						FieldId:  100,
						DataType: schemapb.DataType_Int64,
					},
				},
			},
		},
		dataType: schemapb.DataType_Int64,
	}

	_, err := validateAndExtractMinShouldMatch(exprWithColumnExpr)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "minimum_should_match should be a const integer expression")

	// Test case 2: ExprWithType with a ValueExpr but nil Value
	// This will make getValueExpr return a non-nil ValueExpr but GetValue() returns nil
	exprWithNilValue := &ExprWithType{
		expr: &planpb.Expr{
			Expr: &planpb.Expr_ValueExpr{
				ValueExpr: &planpb.ValueExpr{
					Value: nil,
				},
			},
		},
		dataType: schemapb.DataType_Int64,
	}

	_, err = validateAndExtractMinShouldMatch(exprWithNilValue)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "minimum_should_match should be a const integer expression")

	// Test case 3: Valid ExprWithType with proper value
	validExpr := &ExprWithType{
		expr: &planpb.Expr{
			Expr: &planpb.Expr_ValueExpr{
				ValueExpr: &planpb.ValueExpr{
					Value: NewInt(10),
				},
			},
		},
		dataType: schemapb.DataType_Int64,
	}

	extraVals, err := validateAndExtractMinShouldMatch(validExpr)
	assert.NoError(t, err)
	assert.NotNil(t, extraVals)
	assert.Equal(t, 1, len(extraVals))
	assert.Equal(t, int64(10), extraVals[0].GetInt64Val())
}

func TestExpr_TextMatch_MinShouldMatch_Omitted(t *testing.T) {
	schema := newTestSchema(true)
	enableMatch(schema)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	expr := `text_match(VarCharField, "query")`
	plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         10,
		MetricType:   "L2",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err)
	assert.NotNil(t, plan)

	predicates := plan.GetVectorAnns().GetPredicates()
	assert.NotNil(t, predicates)
	ure := predicates.GetUnaryRangeExpr()
	assert.NotNil(t, ure)
	assert.Equal(t, planpb.OpType_TextMatch, ure.GetOp())
	assert.Equal(t, "query", ure.GetValue().GetStringVal())
	// When omitted, ExtraValues should be empty
	assert.Equal(t, 0, len(ure.GetExtraValues()))
}

func TestExpr_TextMatch_MinShouldMatch_IntegerConstant(t *testing.T) {
	schema := newTestSchema(true)
	enableMatch(schema)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	expr := `text_match(VarCharField, "query", minimum_should_match=10)`
	plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         10,
		MetricType:   "L2",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err)
	assert.NotNil(t, plan)

	predicates := plan.GetVectorAnns().GetPredicates()
	assert.NotNil(t, predicates)
	ure := predicates.GetUnaryRangeExpr()
	assert.NotNil(t, ure)
	assert.Equal(t, planpb.OpType_TextMatch, ure.GetOp())
	assert.Equal(t, "query", ure.GetValue().GetStringVal())
	extra := ure.GetExtraValues()
	assert.Equal(t, 1, len(extra))
	assert.Equal(t, int64(10), extra[0].GetInt64Val())
}

func TestExpr_TextMatch_MinShouldMatch_NameTypos(t *testing.T) {
	schema := newTestSchema(true)
	enableMatch(schema)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	invalid := []string{
		`text_match(VarCharField, "q", minimum_shouldmatch=1)`,
		`text_match(VarCharField, "q", min_should_match=1)`,
		`text_match(VarCharField, "q", minimumShouldMatch=1)`,
		`text_match(VarCharField, "q", minimum-should-match=1)`,
		`text_match(VarCharField, "q", minimum_should_matchx=1)`,
	}
	for _, expr := range invalid {
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err, expr)
	}
}

func TestExpr_TextMatch_MinShouldMatch_InvalidValueTypes(t *testing.T) {
	schema := newTestSchema(true)
	enableMatch(schema)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	invalid := []string{
		`text_match(VarCharField, "q", minimum_should_match=10*10)`,
		`text_match(VarCharField, "q", minimum_should_match=nil)`,
		`text_match(VarCharField, "q", minimum_should_match=)`,
		`text_match(VarCharField, "q", minimum_should_match="10")`,
		`text_match(VarCharField, "q", minimum_should_match=true)`,
		`text_match(VarCharField, "q", minimum_should_match=a)`,
		`text_match(VarCharField, "q", minimum_should_match={min})`,
		`text_match(VarCharField, "q", minimum_should_match=1.0)`,
		`text_match(VarCharField, "q", minimum_should_match=-1)`,
	}
	for _, expr := range invalid {
		_, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
		assert.Error(t, err, expr)
	}
}

func TestExpr_TextMatch_MinShouldMatch_LeadingZerosAndOctal(t *testing.T) {
	schema := newTestSchema(true)
	enableMatch(schema)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)
	{
		expr := `text_match(VarCharField, "query", minimum_should_match=001)`
		plan, err := CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{Topk: 10}, nil, nil)
		assert.NoError(t, err)
		ure := plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr()
		extra := ure.GetExtraValues()
		assert.Equal(t, 1, len(extra))
		assert.Equal(t, int64(1), extra[0].GetInt64Val())
	}
}

func TestExpr_TextMatch_MinShouldMatch_DuplicateOption(t *testing.T) {
	schema := newTestSchema(true)
	enableMatch(schema)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	expr := `text_match(VarCharField, "query", minimum_should_match=2, minimum_should_match=3)`
	_, err = CreateSearchPlan(helper, expr, "FloatVectorField", &planpb.QueryInfo{}, nil, nil)
	assert.Error(t, err)
}

func TestExpr_PhraseMatch(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	enableMatch(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`phrase_match(VarCharField, "phrase")`,
		`phrase_match(TextField, "phrase")`,
		`phrase_match(StringField, "phrase")`,
		`phrase_match(StringField, "phrase", 1)`,
		`phrase_match(VarCharField, "phrase", 11223)`,
		`phrase_match(StringField, "phrase", 0)`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}

	unsupported := []string{
		`phrase_match(not_exist, "phrase")`,
		`phrase_match(BoolField, "phrase")`,
		`phrase_match(StringField, "phrase", -1)`,
	}
	for _, exprStr := range unsupported {
		assertInvalidExpr(t, helper, exprStr)
	}

	unsupported = []string{
		`phrase_match(StringField, "phrase", -1)`,
		`phrase_match(StringField, "phrase", a)`,
		`phrase_match(StringField, "phrase", -a)`,
		`phrase_match(StringField, "phrase", 4294967296)`,
	}
	errMsgs := []string{
		`"slop" should not be a negative interger. "slop" passed: -1`,
		`"slop" should be a const integer expression with "uint32" value. "slop" expression passed: a`,
		`"slop" should be a const integer expression with "uint32" value. "slop" expression passed: -a`,
		`"slop" exceeds the range of "uint32". "slop" expression passed: 4294967296`,
	}
	for i, exprStr := range unsupported {
		_, err := ParseExpr(helper, exprStr, nil)
		assert.True(t, strings.Contains(err.Error(), errMsgs[i]), fmt.Sprintf("Error expected: %v, actual %v", errMsgs[i], err.Error()))
	}
}

func TestExpr_TextField(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	invalidExprs := []string{
		`TextField == "query"`,
	}

	for _, exprStr := range invalidExprs {
		assertInvalidExpr(t, helper, exprStr)
	}
}

func TestExpr_IsNull(t *testing.T) {
	schema := newTestSchema(false)
	schema.EnableDynamicField = false
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`VarCharField is null`,
		`VarCharField IS NULL`,
		`ArrayField is null`,
		`StringArrayField IS NULL`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}

	unsupported := []string{
		`not_exist is null`,
		`FloatVectorField is null`,
		`BinaryVectorField is null`,
		`Float16VectorField is null`,
		`BFloat16VectorField is null`,
		`SparseFloatVectorField is null`,
		`Int8VectorField is null`,
		// issue #48904: array element access with IS NULL should be
		// rejected at parse time rather than raising an internal error
		// at execution time.
		`ArrayField[0] is null`,
		`ArrayField[1] IS NULL`,
		`StringArrayField[0] is null`,
	}
	for _, exprStr := range unsupported {
		assertInvalidExpr(t, helper, exprStr)
	}
}

func TestExpr_StructArrayParentIsNull(t *testing.T) {
	schema := newTestSchema(true)
	schema.StructArrayFields[0].Nullable = true
	helper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(t, err)

	expr, err := ParseExpr(helper, `struct_array is null`, nil)
	require.NoError(t, err)
	nullExpr := expr.GetNullExpr()
	require.NotNil(t, nullExpr)
	assert.Equal(t, planpb.NullExpr_IsNull, nullExpr.GetOp())
	assert.Equal(t, int64(133), nullExpr.GetColumnInfo().GetFieldId())
	assert.Equal(t, schemapb.DataType_Array, nullExpr.GetColumnInfo().GetDataType())
	assert.Equal(t, schemapb.DataType_VarChar, nullExpr.GetColumnInfo().GetElementType())
	assert.True(t, nullExpr.GetColumnInfo().GetNullable())

	expr, err = ParseExpr(helper, `struct_array is not null`, nil)
	require.NoError(t, err)
	nullExpr = expr.GetNullExpr()
	require.NotNil(t, nullExpr)
	assert.Equal(t, planpb.NullExpr_IsNotNull, nullExpr.GetOp())
	assert.Equal(t, int64(133), nullExpr.GetColumnInfo().GetFieldId())
	assert.Equal(t, schemapb.DataType_Array, nullExpr.GetColumnInfo().GetDataType())
	assert.True(t, nullExpr.GetColumnInfo().GetNullable())
}

func TestExpr_VectorArrayOnlyStructParentIsNull(t *testing.T) {
	schema := newTestSchema(false)
	schema.StructArrayFields = append(schema.StructArrayFields, &schemapb.StructArrayFieldSchema{
		FieldID:  10000,
		Name:     "vector_struct",
		Nullable: true,
		Fields: []*schemapb.FieldSchema{
			{
				FieldID:     10001,
				Name:        "vector_struct[embeddings]",
				DataType:    schemapb.DataType_ArrayOfVector,
				ElementType: schemapb.DataType_FloatVector,
				Nullable:    true,
				TypeParams: []*commonpb.KeyValuePair{
					{Key: common.DimKey, Value: "4"},
				},
			},
		},
	})
	helper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(t, err)

	for _, testcase := range []struct {
		expr string
		op   planpb.NullExpr_NullOp
	}{
		{expr: `vector_struct is null`, op: planpb.NullExpr_IsNull},
		{expr: `vector_struct is not null`, op: planpb.NullExpr_IsNotNull},
	} {
		expr, err := ParseExpr(helper, testcase.expr, nil)
		require.NoError(t, err, testcase.expr)
		nullExpr := expr.GetNullExpr()
		require.NotNil(t, nullExpr, testcase.expr)
		assert.Equal(t, testcase.op, nullExpr.GetOp(), testcase.expr)
		assert.Equal(t, int64(10001), nullExpr.GetColumnInfo().GetFieldId(), testcase.expr)
		assert.Equal(t, schemapb.DataType_ArrayOfVector, nullExpr.GetColumnInfo().GetDataType(), testcase.expr)
		assert.Equal(t, schemapb.DataType_FloatVector, nullExpr.GetColumnInfo().GetElementType(), testcase.expr)
		assert.True(t, nullExpr.GetColumnInfo().GetNullable(), testcase.expr)
	}
}

func TestExpr_IsNotNull(t *testing.T) {
	schema := newTestSchema(false)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`VarCharField is not null`,
		`VarCharField IS NOT NULL`,
		`ArrayField is not null`,
		`StringArrayField IS NOT NULL`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}

	unsupported := []string{
		`not_exist is not null`,
		`FloatVectorField is not null`,
		`BinaryVectorField is not null`,
		`Float16VectorField is not null`,
		`BFloat16VectorField is not null`,
		`SparseFloatVectorField is not null`,
		`Int8VectorField is not null`,
		// issue #48904: array element access with IS NOT NULL should be
		// rejected at parse time rather than raising an internal error
		// at execution time.
		`ArrayField[0] is not null`,
		`ArrayField[1] IS NOT NULL`,
		`StringArrayField[0] is not null`,
	}
	for _, exprStr := range unsupported {
		assertInvalidExpr(t, helper, exprStr)
	}
}

func TestExpr_BinaryRange(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`1 < Int8Field < 2`,
		`3 <= Int16Field < 4`,
		`5 <= Int32Field <= 6`,
		`7 < Int64Field <= 8`,
		`9.0 < FloatField < 10.0`,
		`11.0 < DoubleField < 12.0`,
		`"str13" < StringField < "str14"`,
		`"str15" < VarCharField < "str16"`,
		`17 < DoubleField < 18`,
		`10 < A < 25`,

		`2 > Int8Field > 1`,
		`4 >= Int16Field >= 3`,
		`6 >= Int32Field >= 5`,
		`8 >= Int64Field > 7`,
		`10.0 > FloatField > 9.0`,
		`12.0 > DoubleField > 11.0`,
		`"str14" > StringField > "str13"`,
		`"str16" > VarCharField > "str15"`,
		`18 > DoubleField > 17`,
		`100 > B > 14`,
		`1 < JSONField < 3`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}

	invalidExprs := []string{
		`1 < ArrayField < 3`,
		`1 < A+B < 3`,
	}

	for _, exprStr := range invalidExprs {
		assertInvalidExpr(t, helper, exprStr)
	}
}

func TestExpr_castValue(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStr := `Int64Field + 1.1 == 2.1`
	expr, err := ParseExpr(helper, exprStr, nil)
	assert.Error(t, err, exprStr)
	assert.Nil(t, expr, exprStr)

	exprStr = `FloatField +1 == 2`
	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())
}

func TestExpr_BinaryArith(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`Int64Field % 10 == 9`,
		`Int64Field % 10 != 9`,
		`FloatField + 1.1 == 2.1`,
		`A % 10 != 2`,
		`Int8Field + 1 < 2`,
		`Int16Field - 3 <= 4`,
		`Int32Field * 5 > 6`,
		`Int64Field / 7 >= 8`,
		`FloatField + 11 < 12`,
		`DoubleField - 13 <= 14`,
		`A * 15 > 16`,
		`JSONField['A'] / 17 >= 18`,
		`ArrayField[0] % 19 >= 20`,
		`JSONField + 15 == 16`,
		`15 + JSONField == 16`,
		`Int64Field + (2**3) > 0`,
		`1 + FloatField > 100`,
		// bitwise operators on integer fields
		`(Int64Field & 4) == 4`,
		`(Int64Field & 4) != 0`,
		`(Int32Field | 2) == 3`,
		`(Int32Field | 2) != 0`,
		`(Int64Field ^ 7) == 0`,
		`(Int64Field ^ 7) != 5`,
		`(Int8Field & 1) == 1`,
		`(Int16Field | 8) >= 8`,
		`(Int32Field ^ 15) < 16`,
		// bitwise on a JSON dynamic field is allowed: the value type is only
		// known at runtime, so the parser cannot reject it (the executor casts
		// to int64 and treats non-numeric / missing values as non-matching).
		`(JSONField["A"] & 4) == 4`,
		`(JSONField["B"] | 2) != 0`,
		// shift operators on integer / JSON / array-element fields
		`(Int64Field << 2) == 8`,
		`(Int64Field >> 1) >= 5`,
		`(Int32Field << 3) != 0`,
		`(JSONField["A"] << 1) == 4`,
		`(ArrayField[0] >> 2) < 4`,
		`Int64Field == (1 << 3)`,
		`Int64Field == (256 >> 2)`,
		// bitwise NOT: constant folds; over a field it is rewritten to (x ^ -1)
		`Int64Field == ~5`,
		`~5 == Int64Field`,
		`~Int64Field == 0`,
		`~Int32Field != 0`,
		`~JSONField["A"] == 0`,
		`~ArrayField[0] >= 0`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}

	// TODO: enable these after execution backend is ready.
	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`,
		`Int64Field / 0 == 1`,
		`Int64Field % 0 == 1`,
		`FloatField / 0 == 1`,
		`FloatField % 0 == 1`,
		// bitwise ops on non-integer types are invalid
		`(FloatField & 1) == 1`,
		`(DoubleField | 2) == 3`,
		`(FloatField ^ 4) == 0`,
		// folding a bitwise op over float literals is invalid (integer-only)
		`Int64Field == (1.5 & 1)`,
		`(2.5 | 1) == Int64Field`,
		// bitwise ops between two fields are unsupported, consistent with how
		// +, -, *, /, % reject field-to-field arithmetic (right operand must be
		// a constant in the BinaryArithOpEvalRange model).
		`(Int64Field & Int32Field) == 4`,
		`(Int64Field | Int32Field) != 0`,
		`(Int64Field ^ Int32Field) == 0`,
		// shifts on non-integer fields are invalid
		`(FloatField << 1) == 0`,
		`(DoubleField >> 1) == 0`,
		// a negative or too-large shift amount is rejected at plan time
		`(Int64Field << 64) == 0`,
		`(Int64Field << -1) == 0`,
		`(Int64Field >> 64) == 0`,
		`Int64Field == (1 << 64)`,
		`Int64Field == (1 >> 64)`,
		// folding a shift over non-integer literals is invalid (integer-only)
		`Int64Field == (1.5 << 1)`,
		`Int64Field == (1.5 >> 1)`,
		// shift between two fields is unsupported (right operand must be a constant)
		`(Int64Field << Int32Field) == 0`,
		// bitwise NOT on non-integer fields is invalid
		`~FloatField == 0`,
		`~DoubleField == 0`,
		`~BoolField == 0`,
		`~VarCharField == 0`,
		// folding ~ over a non-integer literal is invalid (integer-only)
		`Int64Field == ~1.5`,
		// nested arithmetic (two arith ops before the comparison) is unsupported,
		// consistent with the other arithmetic / bitwise operators
		`(Int64Field >> 2) * 2 == 4`,
		`(~Int64Field) + 1 == 0`,
	}
	for _, exprStr := range unsupported {
		assertInvalidExpr(t, helper, exprStr)
	}
}

// TestExpr_BitwiseArith asserts the generated plan structure for bitwise
// operators, not merely that the expression parses. A bitwise op over a field
// must fuse into a BinaryArithOpEvalRangeExpr carrying the matching ArithOpType,
// right_operand (the mask) and comparison value; a bitwise op over two integer
// literals must constant-fold into a plain comparison.
func TestExpr_BitwiseArith(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	type bitwiseCase struct {
		expr    string
		arithOp planpb.ArithOpType
		cmpOp   planpb.OpType
		mask    int64
		value   int64
	}
	cases := []bitwiseCase{
		{`(Int64Field & 4) == 4`, planpb.ArithOpType_BitAnd, planpb.OpType_Equal, 4, 4},
		{`(Int64Field & 6) != 0`, planpb.ArithOpType_BitAnd, planpb.OpType_NotEqual, 6, 0},
		{`(Int32Field | 2) == 3`, planpb.ArithOpType_BitOr, planpb.OpType_Equal, 2, 3},
		{`(Int32Field | 8) >= 8`, planpb.ArithOpType_BitOr, planpb.OpType_GreaterEqual, 8, 8},
		{`(Int64Field ^ 7) == 0`, planpb.ArithOpType_BitXor, planpb.OpType_Equal, 7, 0},
		{`(Int64Field ^ 7) > 5`, planpb.ArithOpType_BitXor, planpb.OpType_GreaterThan, 7, 5},
		{`(Int16Field & 1) < 1`, planpb.ArithOpType_BitAnd, planpb.OpType_LessThan, 1, 1},
		{`(Int8Field | 3) <= 7`, planpb.ArithOpType_BitOr, planpb.OpType_LessEqual, 3, 7},
		// reverse form (constant on the left) for the symmetric == / != ops
		{`4 == (Int64Field & 4)`, planpb.ArithOpType_BitAnd, planpb.OpType_Equal, 4, 4},
		{`0 != (Int32Field | 2)`, planpb.ArithOpType_BitOr, planpb.OpType_NotEqual, 2, 0},
		// shift operators fuse the same way, carrying the shift amount as the operand
		{`(Int64Field << 2) == 8`, planpb.ArithOpType_Shl, planpb.OpType_Equal, 2, 8},
		{`(Int64Field >> 1) >= 5`, planpb.ArithOpType_Shr, planpb.OpType_GreaterEqual, 1, 5},
		{`(Int32Field << 3) != 0`, planpb.ArithOpType_Shl, planpb.OpType_NotEqual, 3, 0},
		// ~field is rewritten to (field ^ -1); it fuses as BitXor with operand -1
		{`~Int64Field == 0`, planpb.ArithOpType_BitXor, planpb.OpType_Equal, -1, 0},
		{`~Int32Field != 5`, planpb.ArithOpType_BitXor, planpb.OpType_NotEqual, -1, 5},
	}
	for _, c := range cases {
		expr, err := ParseExpr(helper, c.expr, nil)
		assert.NoError(t, err, c.expr)
		bao := expr.GetBinaryArithOpEvalRangeExpr()
		assert.NotNil(t, bao, c.expr)
		if bao == nil {
			continue
		}
		assert.Equal(t, c.arithOp, bao.GetArithOp(), c.expr)
		assert.Equal(t, c.cmpOp, bao.GetOp(), c.expr)
		assert.Equal(t, c.mask, bao.GetRightOperand().GetInt64Val(), c.expr)
		assert.Equal(t, c.value, bao.GetValue().GetInt64Val(), c.expr)
	}

	// Constant folding: a bitwise op over two integer literals collapses to a
	// constant, so the comparison degrades to a plain UnaryRange on the field.
	type foldCase struct {
		expr     string
		expected int64
	}
	foldCases := []foldCase{
		{`Int64Field == (7 & 3)`, 3},     // 7 & 3 = 3
		{`Int64Field == (5 | 2)`, 7},     // 5 | 2 = 7
		{`Int64Field == (6 ^ 3)`, 5},     // 6 ^ 3 = 5
		{`Int64Field == (1 << 3)`, 8},    // 1 << 3 = 8
		{`Int64Field == (256 >> 2)`, 64}, // 256 >> 2 = 64
		{`Int64Field == ~5`, -6},         // ~5 = -6
	}
	for _, c := range foldCases {
		expr, err := ParseExpr(helper, c.expr, nil)
		assert.NoError(t, err, c.expr)
		ure := expr.GetUnaryRangeExpr()
		assert.NotNil(t, ure, c.expr)
		if ure == nil {
			continue
		}
		assert.Equal(t, planpb.OpType_Equal, ure.GetOp(), c.expr)
		assert.Equal(t, c.expected, ure.GetValue().GetInt64Val(), c.expr)
	}
}

func TestExpr_Value(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`1`,
		`2.0`,
		`true`,
		`false`,
		`"str"`,
		`3 > 2`,
	}
	for _, exprStr := range exprStrs {
		expr := handleExpr(helper, exprStr)
		assert.NotNil(t, getExpr(expr).expr, exprStr)
		// fmt.Printf("expr: %s\n", exprStr)
		// ShowExpr(getExpr(expr).expr)
	}
}

func TestExpr_Identifier(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`BoolField`,
		`Int8Field`,
		`Int16Field`,
		`Int32Field`,
		`Int64Field`,
		`FloatField`,
		`DoubleField`,
		`StringField`,
		`VarCharField`,
		`JSONField["A"]`,
		`$meta["A"]`,
	}
	for _, exprStr := range exprStrs {
		expr := handleExpr(helper, exprStr)
		assert.NotNil(t, getExpr(expr).expr, exprStr)

		// fmt.Printf("expr: %s\n", exprStr)
		// ShowExpr(getExpr(expr).expr)
	}
}

func TestExpr_Constant(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		// ------------------- arithmetic operations ----------------
		`1.0 + 2.0`,
		`1.0 + 2`,
		`1 + 2.0`,
		`1 + 2`,
		`1.0 - 2.0`,
		`1.0 - 2`,
		`1 - 2.0`,
		`1 - 2`,
		`1.0 * 2.0`,
		`1.0 * 2`,
		`1 * 2.0`,
		`1 * 2`,
		`1.0 / 2.0`,
		`1.0 / 2`,
		`1 / 2.0`,
		`1 / 2`,
		`1 % 2`,
		// ------------------- logical operations ----------------
		`true and false`,
		`true or false`,
		`!true`,
		`!false`,
		// ------------------- relational operations ----------------
		`"1" < "2"`,
		`1.0 < 2.0`,
		`1.0 < 2`,
		`1 < 2.0`,
		`1 < 2`,
		`"1" <= "2"`,
		`1.0 <= 2.0`,
		`1.0 <= 2`,
		`1 <= 2.0`,
		`1 <= 2`,
		`"1" > "2"`,
		`1.0 > 2.0`,
		`1.0 > 2`,
		`1 > 2.0`,
		`1 > 2`,
		`"1" >= "2"`,
		`1.0 >= 2.0`,
		`1.0 >= 2`,
		`1 >= 2.0`,
		`1 >= 2`,
		`"1" == "2"`,
		`1.0 == 2.0`,
		`1.0 == 2`,
		`1 == 2.0`,
		`1 == 2`,
		`true == false`,
		`"1" != "2"`,
		`1.0 != 2.0`,
		`1.0 != 2`,
		`1 != 2.0`,
		`1 != 2`,
		`true != false`,
	}
	for _, exprStr := range exprStrs {
		expr := handleExpr(helper, exprStr)
		assert.NotNil(t, getExpr(expr).expr, exprStr)

		// fmt.Printf("expr: %s\n", exprStr)
		// ShowExpr(getExpr(expr).expr)
	}
}

func TestExpr_Combinations(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`not (Int8Field + 1 == 2)`,
		`(Int16Field - 3 == 4) and (Int32Field * 5 != 6)`,
		`(Int16Field - 3 == 4) AND (Int32Field * 5 != 6)`,
		`(Int64Field / 7 != 8) or (Int64Field % 10 == 9)`,
		`(Int64Field / 7 != 8) OR (Int64Field % 10 == 9)`,
		`Int64Field > 0 && VarCharField > "0"`,
		`Int64Field < 0 && VarCharField < "0"`,
		`A > 50 or B < 40`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}
}

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) {
	schema := newTestSchemaHelper(t)
	_, err := CreateSearchPlan(schema, `$meta["A"] != 10`, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err)
}

func TestCreateFloat16SearchPlan(t *testing.T) {
	schema := newTestSchemaHelper(t)
	_, err := CreateSearchPlan(schema, `$meta["A"] != 10`, "Float16VectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err)
}

func TestCreateBFloat16earchPlan(t *testing.T) {
	schema := newTestSchemaHelper(t)
	_, err := CreateSearchPlan(schema, `$meta["A"] != 10`, "BFloat16VectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err)
}

func TestCreateSparseFloatVectorSearchPlan(t *testing.T) {
	schema := newTestSchemaHelper(t)
	_, err := CreateSearchPlan(schema, `$meta["A"] != 10`, "SparseFloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err)
}

func TestExpr_Invalid(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`invalid expression`,
		`"constant"`,
		// -------------- identifier not in schema --------------
		`not_in_schema`,
		// ------------------------ Add/Sub ---------------------
		`not_in_schema + 1`,
		`1 - not_in_schema`,
		`true + false`,
		`"str" + "text"`,
		`true + "str"`,
		`true - false`,
		`"str" - "text"`,
		`true - "str"`,
		`StringField + VarCharField`,
		`StringField - 2`,
		`2 + StringField`,
		// ------------------------ Mul/Div/Mod ---------------------
		`not_in_schema * 1`,
		`1 / not_in_schema`,
		`1 % not_in_schema`,
		`true * false`,
		`true / false`,
		`true % false`,
		`"str" * "text"`,
		`"str" / "text"`,
		`"str" % "text"`,
		`2 / 0`,
		`2 % 0`,
		`StringField % VarCharField`,
		`StringField * 2`,
		`2 / StringField`,
		//`JSONField / 2 == 1`,
		`2 % JSONField == 1`,
		`2 % Int64Field == 1`,
		`ArrayField / 2 == 1`,
		`2 / ArrayField == 1`,
		// ----------------------- ==/!= -------------------------
		//`not_in_schema != 1`, // maybe in json
		//`1 == not_in_schema`, // maybe in json
		`true == "str"`,
		`"str" != false`,
		`VarCharField != FloatField`,
		`FloatField == VarCharField`,
		`A == -9223372036854775809`,
		// ---------------------- relational --------------------
		//`not_in_schema < 1`, // maybe in json
		//`1 <= not_in_schema`, // maybe in json
		`true <= "str"`,
		`"str" >= false`,
		`VarCharField < FloatField`,
		`FloatField > VarCharField`,
		//`JSONField > 1`,
		//`1 < JSONField`,
		`ArrayField > 2`,
		`2 < ArrayField`,
		// https://github.com/milvus-io/milvus/issues/34139
		"\"Int64Field\" > 500 && \"Int64Field\" < 1000",
		"\"Int64Field\" == 500 || \"Int64Field\" != 1000",
		`"str" < 100`,
		`"str" <= 100`,
		`"str" > 100`,
		`"str" >= 100`,
		`"str" == 100`,
		`"str" != 100`,
		// ------------------------ like ------------------------
		`(VarCharField % 2) like "prefix%"`,
		`FloatField like "prefix%"`,
		//`value like "prefix%"`, // maybe in json
		// ------------------------ term ------------------------
		//`not_in_schema in [1, 2, 3]`, // maybe in json
		`1 in [1, 2, 3]`,
		`(Int8Field + 8) in [1, 2, 3]`,
		`Int8Field in [(true + 1)]`,
		`Int8Field in [Int16Field]`,
		`BoolField in [4.0]`,
		`VarCharField in [4.0]`,
		`Int32Field in [4.0]`,
		`FloatField in [5, 6.0, true]`,
		`1 in A`,
		// ----------------------- range -------------------------
		//`1 < not_in_schema < 2`, // maybe in json
		`1 < 3 < 2`,
		`1 < (Int8Field + Int16Field) < 2`,
		`(invalid_lower) < Int32Field < 2`,
		`1 < Int32Field < (invalid_upper)`,
		`(Int8Field) < Int32Field < 2`,
		`1 < Int32Field < (Int16Field)`,
		`1 < StringField < 2`,
		`1 < BoolField < 2`,
		`1.0 < Int32Field < 2.0`,
		`true < FloatField < false`,
		// `2 <= Int32Field <= 1`,
		`2 = Int32Field = 1`,
		`true = BoolField = false`,
		// ----------------------- unary ------------------------
		`-true`,
		`!"str"`,
		`!(not_in_schema)`,
		`-Int32Field`,
		`!(Int32Field)`,
		// ----------------------- or/and ------------------------
		`false or not_in_schema`,
		`"str" or false`,
		`BoolField OR false`,
		`Int32Field OR Int64Field`,
		`not_in_schema and true`,
		`"str" and false`,
		`Int32Field AND Int64Field`,
		// -------------------- unsupported ----------------------
		`1 ^ 2`,
		`1 & 2`,
		`1 ** 2`,
		`1 << 2`,
		`1 | 2`,
		// -------------------- cannot be independent ----------------------
		`BoolField`,
		`Int64Field > 100 and BoolField`,
		`!BoolField`,
		// -------------------- array ----------------------
		//`A == [1, 2, 3]`,
		`Int64Field == [1, 2, 3]`,
		`Int64Field > [1, 2, 3]`,
		`Int64Field + [1, 2, 3] == 10`,
		`Int64Field % [1, 2, 3] == 10`,
		`[1, 2, 3] < Int64Field < [4, 5, 6]`,
		`Int64Field["A"] == 123`,
		`[1,2,3] == [4,5,6]`,
		`[1,2,3] == 1`,
	}
	for _, exprStr := range exprStrs {
		_, err := ParseExpr(helper, exprStr, nil)
		assert.Error(t, err, exprStr)
	}
}

func TestCreateRetrievePlan_Invalid(t *testing.T) {
	t.Run("invalid expr", func(t *testing.T) {
		schema := newTestSchemaHelper(t)
		_, err := CreateRetrievePlan(schema, "invalid expression", nil)
		assert.Error(t, err)
	})
}

func TestCreateSearchPlan_Invalid(t *testing.T) {
	t.Run("invalid expr", func(t *testing.T) {
		schema := newTestSchemaHelper(t)
		_, err := CreateSearchPlan(schema, "invalid expression", "", nil, nil, nil)
		assert.Error(t, err)
	})

	t.Run("invalid vector field", func(t *testing.T) {
		schema := newTestSchemaHelper(t)
		_, err := CreateSearchPlan(schema, "Int64Field > 0", "not_exist", nil, nil, nil)
		assert.Error(t, err)
	})

	t.Run("not vector type", func(t *testing.T) {
		schema := newTestSchemaHelper(t)
		_, err := CreateSearchPlan(schema, "Int64Field > 0", "VarCharField", nil, nil, nil)
		assert.Error(t, err)
	})
}

var listenerCnt int

type errorListenerTest struct {
	antlr.DefaultErrorListener
}

func (l *errorListenerTest) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
	listenerCnt += 1
}

func (l *errorListenerTest) ReportAmbiguity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, exact bool, ambigAlts *antlr.BitSet, configs *antlr.ATNConfigSet) {
	listenerCnt += 1
}

func (l *errorListenerTest) Error() error {
	return nil
}

func Test_FixErrorListenerNotRemoved(t *testing.T) {
	schema := newTestSchema(true)
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	normal := "1 < Int32Field < (Int16Field)"
	for i := 0; i < 10; i++ {
		err := handleExpr(schemaHelper, normal)
		err1, ok := err.(error)
		assert.True(t, ok)
		assert.Error(t, err1)
	}
	assert.True(t, listenerCnt <= 10)
}

func Test_handleExpr(t *testing.T) {
	schema := newTestSchema(true)
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	ret1 := handleExpr(schemaHelper, "1 < Int32Field < (Int16Field)")
	err1, ok := ret1.(error)
	assert.True(t, ok)
	assert.Error(t, err1)
}

func Test_handleExpr_empty(t *testing.T) {
	schema := newTestSchema(true)
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	ret1 := handleExpr(schemaHelper, "")
	assert.True(t, isAlwaysTrueExpr(getExpr(ret1).expr))
	assert.Equal(t, schemapb.DataType_Bool, getExpr(ret1).dataType)
}

// test if handleExpr is thread-safe.
func Test_handleExpr_17126_26662(t *testing.T) {
	schema := newTestSchema(true)
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)
	normal := `VarCharField == "abcd\"defg"`

	n := 400
	var wg sync.WaitGroup
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			ret := handleExpr(schemaHelper, normal)
			_, ok := ret.(error)
			assert.False(t, ok)
		}()
	}
	wg.Wait()
}

func Test_JSONExpr(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error
	// search
	exprs := []string{
		`$meta["A"] > 90`,
		`JSONField["A"] > 90`,
		`A < 10`,
		`JSONField["A"] <= 5`,
		`$meta["A"] <= 5`,
		`$meta["A"] >= 95`,
		`$meta["A"] == 5`,
		`$meta["A"] != 95`,
		`$meta["A"] > 90 && $meta["B"] < 5`,
		`$meta["A"] > 95 || $meta["B"] < 5`,
		`A > 95 || $meta["B"] < 5`,
		`not ($meta["A"] == 95)`,
		`$meta["A"] in [90, 91, 95, 97]`,
		`$meta["A"] not in [90, 91, 95, 97]`,
		`$meta["C"]["0"] in [90, 91, 95, 97]`,
		`$meta["C"]["0"] not in [90, 91, 95, 97]`,
		`C["0"] not in [90, 91, 95, 97]`,
		`C[0] in [90, 91, 95, 97]`,
		`C["0"] > 90`,
		`C["0"] < 90`,
		`C["0"] == 90`,
		`10 < C["0"] < 90`,
		`100 > C["0"] > 90`,
		`0 <= $meta["A"] < 5`,
		`0 <= A < 5`,
		`$meta["A"] + 5 == 10`,
		`$meta["A"] > 10 + 5`,
		`100 - 5 < $meta["A"]`,
		`100 == $meta["A"] + 6`,
		`exists $meta["A"]`,
		`exists $meta["A"]["B"]["C"] `,
		`exists $meta["A"] || exists JSONField["A"]`,
		`exists $meta["A"] && exists JSONField["A"]`,
		`A["B"][0] > 100`,
		`$meta[0] > 100`,
		`A["\"\"B\"\""] > 10`,
		`A["[\"B\"]"] == "abc\"bbb\"cc"`,
		`A['B'] == "abc\"bbb\"cc"`,
		`A['B'] == 'abc"cba'`,
		`A['B'] == 'abc\"cba'`,
		`A == [1,2,3]`,
		`A + 1.2 == 3.3`,
		`A + 1 == 2`,
		`JSONField > 0`,
		`JSONField == 0`,
		`JSONField < 100`,
		`0 < JSONField < 100`,
		`20 > JSONField > 0`,
		`JSONField + 5 > 0`,
		`JSONField > 2 + 5`,
		`JSONField * 2 > 5`,
		`JSONField / 2 > 5`,
		`JSONField % 10 > 5`,
	}
	for _, expr = range exprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err)
	}
}

func Test_InvalidExprOnJSONField(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error
	// search
	exprs := []string{
		`exists $meta`,
		`exists JSONField`,
		`exists ArrayField`,
		`exists $meta["A"] > 10 `,
		`exists Int64Field`,
		`A[[""B""]] > 10`,
		`A["[""B""]"] > 10`,
		`A[[""B""]] > 10`,
		`A[B] > 10`,
		`A + B == 3.3`,
	}

	for _, expr = range exprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err, expr)
	}
}

func Test_InvalidExprWithoutJSONField(t *testing.T) {
	fields := []*schemapb.FieldSchema{
		{FieldID: 100, Name: "id", IsPrimaryKey: true, Description: "id", DataType: schemapb.DataType_Int64},
		{FieldID: 101, Name: "vector", IsPrimaryKey: false, Description: "vector", DataType: schemapb.DataType_FloatVector},
	}

	schema := &schemapb.CollectionSchema{
		Name:        "test",
		Description: "schema for test used",
		AutoID:      true,
		Fields:      fields,
	}
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(t, err)
	expr := ""

	exprs := []string{
		`A == 0`,
		`JSON["A"] > 0`,
		`A < 100`,
		`0 < JSON["A"] < 100`,
		`0 < A < 100`,
		`100 > JSON["A"] > 0`,
		`100 > A > 0`,
	}

	for _, expr = range exprs {
		_, err = CreateSearchPlan(schemaHelper, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err)
	}
}

func Test_InvalidExprWithMultipleJSONField(t *testing.T) {
	fields := []*schemapb.FieldSchema{
		{FieldID: 100, Name: "id", IsPrimaryKey: true, Description: "id", DataType: schemapb.DataType_Int64},
		{FieldID: 101, Name: "vector", IsPrimaryKey: false, Description: "vector", DataType: schemapb.DataType_FloatVector},
		{FieldID: 102, Name: "json1", IsPrimaryKey: false, Description: "json field 1", DataType: schemapb.DataType_JSON},
		{FieldID: 103, Name: "json2", IsPrimaryKey: false, Description: "json field 2", DataType: schemapb.DataType_JSON},
	}

	schema := &schemapb.CollectionSchema{
		Name:        "test",
		Description: "schema for test used",
		AutoID:      true,
		Fields:      fields,
	}
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(t, err)

	expr := ""
	exprs := []string{
		`A == 0`,
		`A in [1, 2, 3]`,
		`A not in [1, 2, 3]`,
		`"1" in A`,
		`"1" not in A`,
	}

	for _, expr = range exprs {
		_, err = CreateSearchPlan(schemaHelper, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err)
	}
}

func Test_exprWithSingleQuotes(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error
	exprs := []string{
		`'abc' < StringField < "def"`,
		`'ab"c' < StringField < "d'ef"`,
		`'ab\"c' < StringField < "d\'ef"`,
		`'ab\'c' < StringField < "d\"ef"`,
	}
	for _, expr = range exprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err)
	}

	invalidExprs := []string{
		`'abc'd' < StringField < "def"`,
		`'abc' < StringField < "def"g"`,
	}

	for _, expr = range invalidExprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err)
	}
}

func Test_JSONContains(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error
	exprs := []string{
		`json_contains(A, 10)`,
		`not json_contains(A, 10)`,
		`json_contains(A, 10.5)`,
		`not json_contains(A, 10.5)`,
		`json_contains(A, "10")`,
		`not json_contains(A, "10")`,
		`json_contains($meta["A"], 10)`,
		`not json_contains($meta["A"], 10)`,
		`json_contains(JSONField["x"], 5)`,
		`not json_contains(JSONField["x"], 5)`,
		`JSON_CONTAINS(JSONField["x"], 5)`,
		`json_Contains(JSONField, 5)`,
		`JSON_contains(JSONField, 5)`,
		`json_contains(A, [1,2,3])`,
		`array_contains(A, [1,2,3])`,
		`array_contains(ArrayField, 1)`,
		`json_contains(JSONField, 5)`,
		`json_contains($meta, 1)`,
	}
	for _, expr = range exprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err)
	}
}

func Test_InvalidJSONContains(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error
	exprs := []string{
		`json_contains(10, A)`,
		`json_contains(1, [1,2,3])`,
		`json_contains([1,2,3], 1)`,
		`json_contains([1,2,3], [1,2,3])`,
		`json_contains([1,2,3], [1,2])`,
		`json_contains(A, B)`,
		`not json_contains(A, B)`,
		`json_contains(A, B > 5)`,
		`json_contains(StringField, "a")`,
		`json_contains(A, StringField > 5)`,
		`json_contains(A)`,
		`json_contains(A, 5, C)`,
		`json_contains(ArrayField, "abc")`,
		`json_contains(ArrayField, [1,2])`,
	}
	for _, expr = range exprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err)
	}
}

func Test_isEmptyExpression(t *testing.T) {
	type args struct {
		s string
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			args: args{s: ""},
			want: true,
		},
		{
			args: args{s: "         "},
			want: true,
		},
		{
			args: args{s: "not empty"},
			want: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equalf(t, tt.want, isEmptyExpression(tt.args.s), "isEmptyExpression(%v)", tt.args.s)
		})
	}
}

func Test_EscapeString(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error
	exprs := []string{
		`A == "\"" || B == '\"'`,
		`A == "\n" || B == '\n'`,
		`A == "\367" || B == '\367'`,
		`A == "\3678" || B == '\3678'`,
		`A == "ab'c\'d" || B == 'abc"de\"'`,
		`A == "'" || B == '"'`,
		`A == "\'" || B == '\"' || C == '\''`,
		`A == "\\'" || B == '\\"' || C == '\''`,
		`A == "\\\'" || B == '\\\"' || C == '\\\''`,
		`A == "\\\\'" || B == '\\\\"' || C == '\\\''`,
		`A == "\\\\\'" || B == '\\\\\"' || C == '\\\\\''`,
		`A == "\\\\\\'" || B == '\\\\\\"' || C == '\\\\\''`,
		`str2 like 'abc\"def-%'`,
		`str2 like 'abc"def-%'`,
		`str4 like "abc\367-%"`,
		`str4 like "中国"`,
		`tag == '"blue"'`,
	}
	for _, expr = range exprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err)
	}

	invalidExprs := []string{
		`A == "ab
c" || B == 'ab
c'`,
		`A == "\423" || B == '\378'`,
		`A == "\中国"`,
	}
	for _, expr = range invalidExprs {
		plan, err := CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err)
		fmt.Println(plan)
	}
}

// todo add null test

func Test_JSONContainsAll(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error
	var plan *planpb.PlanNode

	exprs := []string{
		`json_contains_all(A, [1,2,3])`,
		`json_contains_all(A, [1,"2",3.0])`,
		`JSON_CONTAINS_ALL(A, [1,"2",3.0])`,
		`array_contains_all(ArrayField, [1,2,3])`,
		`array_contains_all(ArrayField, [1])`,
		`array_contains_all(ArrayField, [1,2,3])`,
	}
	for _, expr = range exprs {
		plan, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err)
		assert.NotNil(t, plan.GetVectorAnns().GetPredicates().GetJsonContainsExpr())
		assert.Equal(t, planpb.JSONContainsExpr_ContainsAll, plan.GetVectorAnns().GetPredicates().GetJsonContainsExpr().GetOp())
	}

	invalidExprs := []string{
		`JSON_CONTAINS_ALL(A, 1)`,
		`JSON_CONTAINS_ALL(A, [abc])`,
		`JSON_CONTAINS_ALL(A, [2>a])`,
		`JSON_CONTAINS_ALL(A, [2>>a])`,
		`JSON_CONTAINS_ALL(A[""], [1,2,3])`,
		`JSON_CONTAINS_ALL(Int64Field, [1,2,3])`,
		`JSON_CONTAINS_ALL(A, B)`,
		`JSON_CONTAINS_ALL(ArrayField, [[1,2,3]])`,
	}
	for _, expr = range invalidExprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err)
	}
}

func Test_JSONContainsStructFieldUsesSubFieldNullable(t *testing.T) {
	for _, testcase := range []struct {
		name           string
		parentNullable bool
		childNullable  bool
		wantNullable   bool
	}{
		{name: "child nullable", childNullable: true, wantNullable: true},
		{name: "parent nullable only", parentNullable: true, wantNullable: false},
	} {
		t.Run(testcase.name, func(t *testing.T) {
			schema := newTestSchema(true)
			schema.StructArrayFields[0].Nullable = testcase.parentNullable
			schema.StructArrayFields[0].Fields[1].Nullable = testcase.childNullable
			helper, err := typeutil.CreateSchemaHelper(schema)
			require.NoError(t, err)

			plan, err := CreateSearchPlan(helper, `json_contains_all(struct_array[sub_int], [])`, "FloatVectorField", &planpb.QueryInfo{
				Topk:         0,
				MetricType:   "",
				SearchParams: "",
				RoundDecimal: 0,
			}, nil, nil)
			require.NoError(t, err)

			jsonContainsExpr := plan.GetVectorAnns().GetPredicates().GetJsonContainsExpr()
			require.NotNil(t, jsonContainsExpr)
			columnInfo := jsonContainsExpr.GetColumnInfo()
			require.NotNil(t, columnInfo)
			assert.Equal(t, int64(134), columnInfo.GetFieldId())
			assert.Equal(t, schemapb.DataType_Array, columnInfo.GetDataType())
			assert.Equal(t, schemapb.DataType_Int32, columnInfo.GetElementType())
			assert.Equal(t, testcase.wantNullable, columnInfo.GetNullable())
		})
	}
}

func Test_JSONContainsAny(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error
	var plan *planpb.PlanNode

	exprs := []string{
		`json_contains_any(A, [1,2,3])`,
		`json_contains_any(A, [1,"2",3.0])`,
		`JSON_CONTAINS_ANY(A, [1,"2",3.0])`,
		`JSON_CONTAINS_ANY(ArrayField, [1,2,3])`,
	}
	for _, expr = range exprs {
		plan, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err)
		assert.NotNil(t, plan.GetVectorAnns().GetPredicates().GetJsonContainsExpr())
		assert.Equal(t, planpb.JSONContainsExpr_ContainsAny, plan.GetVectorAnns().GetPredicates().GetJsonContainsExpr().GetOp())
	}

	invalidExprs := []string{
		`JSON_CONTAINS_ANY(A, 1)`,
		`JSON_CONTAINS_ANY(A, [abc])`,
		`JSON_CONTAINS_ANY(A, [2>a])`,
		`JSON_CONTAINS_ANY(A, [2>>a])`,
		`JSON_CONTAINS_ANY(A[""], [1,2,3])`,
		`JSON_CONTAINS_ANY(Int64Field, [1,2,3])`,
		`JSON_CONTAINS_ANY(ArrayField, [[1,2,3]])`,
		`JSON_CONTAINS_ANY(A, B)`,
	}
	for _, expr = range invalidExprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err)
	}
}

func Test_ArrayExpr(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error

	exprs := []string{
		`ArrayField == [1,2,3,4]`,
		`ArrayField != [1,2,3,4]`,
		`[1,2,3,4] == ArrayField`,
		`ArrayField[0] == 1`,
		`ArrayField[0] > 1`,
		`1 < ArrayField[0] < 3`,
		`StringArrayField[0] == "abc"`,
		`StringArrayField[0] < "abc"`,
		`"abc" < StringArrayField[0] < "efg"`,
		`array_contains(ArrayField, 1)`,
		`not ARRAY_CONTAINS(ArrayField, 1)`,
		`array_contains_all(ArrayField, [1,2,3,4])`,
		`not ARRAY_CONTAINS_ALL(ArrayField, [1,2,3,4])`,
		`array_contains_any(ArrayField, [1,2,3,4])`,
		`not ARRAY_CONTAINS_ANY(ArrayField, [1,2,3,4])`,
		`array_contains(StringArrayField, "abc")`,
		`not ARRAY_CONTAINS(StringArrayField, "abc")`,
		`array_contains_all(StringArrayField, ["a", "b", "c", "d"])`,
		`not ARRAY_CONTAINS_ALL(StringArrayField, ["a", "b", "c", "d"])`,
		`array_contains_any(StringArrayField, ["a", "b", "c", "d"])`,
		`not ARRAY_CONTAINS_ANY(StringArrayField, ["a", "b", "c", "d"])`,
		`StringArrayField[0] like "abd%"`,
		`+ArrayField[0] == 1`,
		`ArrayField[0] % 3 == 1`,
		`ArrayField[0] + 3 == 1`,
		`ArrayField[0] in [1,2,3]`,
		`ArrayField[0] in []`,
		`0 < ArrayField[0] < 100`,
		`100 > ArrayField[0] > 0`,
		`ArrayField[0] > 1`,
		`ArrayField[0] == 1`,
		`ArrayField in []`,
	}
	for _, expr = range exprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err, expr)
	}

	unsupportedFieldComparisons := []string{
		`ArrayField == ArrayField`,
		`ArrayField != ArrayField`,
		`ArrayField == Int64Field`,
		`Int64Field != ArrayField`,
		`ArrayField[0] == Int64Field`,
		`ArrayField[0] < Int64Field`,
	}
	for _, expr = range unsupportedFieldComparisons {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		require.ErrorIs(t, err, merr.ErrQueryPlan, expr)
		assert.Contains(t, err.Error(), "field-to-field comparison involving ARRAY fields is not supported", expr)
	}

	invalidExprs := []string{
		`ArrayField == ["abc", "def"]`,
		`"abc" < ArrayField[0] < "def"`,
		`ArrayField[0] == "def"`,
		`ArrayField["0"] == 1`,
		`array_contains(ArrayField, "a")`,
		`array_contains(StringArrayField, 1)`,
		`array_contains_all(StringArrayField, ["abc", 123])`,
		`array_contains_any(StringArrayField, ["abc", 123])`,
		`StringArrayField like "abd%"`,
		`+ArrayField == 1`,
		`ArrayField % 3 == 1`,
		`ArrayField + 3 == 1`,
		`ArrayField in [1,2,3]`,
		`ArrayField[0] in [1, "abc",3.3]`,
		`0 < ArrayField < 100`,
		`100 > ArrayField > 0`,
		`ArrayField > 1`,
		`ArrayField == 1`,
		`ArrayField[] == 1`,
		`A[] == 1`,
		`ArrayField[0] + ArrayField[1] == 1`,
		`ArrayField == []`,
	}
	for _, expr = range invalidExprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err, expr)
	}
}

func Test_ArrayLength(t *testing.T) {
	schema := newTestSchemaHelper(t)
	expr := ""
	var err error

	exprs := []string{
		`array_length(ArrayField) == 10`,
		`array_length(A) != 10`,
		`array_length(StringArrayField) == 1`,
		`array_length(B) != 1`,
		`not (array_length(C[0]) == 1)`,
		`not (array_length(C["D"]) != 1)`,
		`array_length(StringArrayField) < 1`,
		`array_length(StringArrayField) <= 1`,
		`array_length(StringArrayField) > 5`,
		`array_length(StringArrayField) >= 5`,
		// struct array sub-field
		`array_length(struct_array) == 2`,
		`array_length(struct_array[sub_str]) == 3`,
		`array_length(struct_array[sub_int]) > 1`,
		`array_length(struct_array[sub_int]) <= 10`,
	}
	for _, expr = range exprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err, expr)
	}

	invalidExprs := []string{
		`array_length(a > b) == 0`,
		`array_length(a, b) == 1`,
		`array_length(A)`,
		`array_length("A") / 10 == 2`,
		`array_length(Int64Field) == 2`,
		`array_length(a-b) == 2`,
		`0 < array_length(a-b) < 2`,
		`0 < array_length(StringArrayField) < 1`,
		`100 > array_length(ArrayField) > 10`,
		`array_length(A) % 10 == 2`,
		`array_length(A) / 10 == 2`,
		`array_length(A) + 1  == 2`,
		`array_length(JSONField) + 1  == 2`,
		`array_length(A)  == 2.2`,
	}
	for _, expr = range invalidExprs {
		_, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.Error(t, err, expr)
	}
}

// Test randome sample with all other predicate expressions.
func TestRandomSampleWithFilter(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	enableMatch(schema)
	assert.NoError(t, err)

	exprStrs := []string{
		`random_sample(0.01)`,
		`random_sample(0.9999)`,
		`BoolField in [true, false] && random_sample(0.01)`,
		`Int8Field < Int16Field && random_sample(0.01)`,
		`Int8Field < Int16Field && Int16Field <= 1 && random_sample(0.01)`,
		`(Int8Field < Int16Field || Int16Field <= 1) && random_sample(0.01)`,
		`Int16Field <= 1 && random_sample(0.01)`,
		`VarCharField like "prefix%" && random_sample(0.01)`,
		`VarCharField is null && random_sample(0.01)`,
		`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 == 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)`,
	}
	for _, exprStr := range exprStrs {
		assertValidExpr(t, helper, exprStr)
	}

	exprStrsInvalid := []string{
		`random_sample(a)`,
		`random_sample(1)`,
		`random_sample(1.1)`,
		`random_sample(0)`,
		`random_sample(-1)`,
		`random_sample(0.01, Int8Field < Int16Field) + 1`,
		`random_sample(0.01, Int8Field < Int16Field) ** 2 == 1`,
		`not random_sample(0.01, Int8Field < Int16Field)`,
		`(Int16Field - 3 == 4) || random_sample(0.01)`,
		`random_sample(0.01) and (Int16Field - 3 == 4)`,
		`random_sample(0.01) or (Int16Field - 3 == 4)`,
		`random_sample(0.01, FloatField)`,
		`random_sample(0.01, 1.0 + 2.0)`,
		`random_sample(0.01, false)`,
		`random_sample(0.01, text_match(VarCharField, "query"))`,
		`random_sample(0.01, phrase_match(VarCharField, "query something"))`,
		`Int8Field < Int16Field || Int16Field <= 1 && random_sample(0.01)`,
	}
	for _, exprStr := range exprStrsInvalid {
		assertInvalidExpr(t, helper, exprStr)
	}
}

func Test_SegmentScorers(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// helper to build a boost segment scorer function
	makeBoostRanker := func(filter string, weight string) *schemapb.FunctionSchema {
		params := []*commonpb.KeyValuePair{
			{Key: rerank.WeightKey, Value: weight},
			{Key: "reranker", Value: rerank.BoostName},
		}
		if filter != "" {
			params = append(params, &commonpb.KeyValuePair{Key: rerank.FilterKey, Value: filter})
		}
		return &schemapb.FunctionSchema{
			Params: params,
		}
	}

	t.Run("ok - single boost scorer", func(t *testing.T) {
		fs := &schemapb.FunctionScore{
			Functions: []*schemapb.FunctionSchema{
				makeBoostRanker("Int64Field > 0", "1.5"),
			},
		}
		plan, err := CreateSearchPlan(schema, "", "FloatVectorField", &planpb.QueryInfo{GroupByFieldId: -1}, nil, fs)
		assert.NoError(t, err)
		assert.NotNil(t, plan)
		assert.Equal(t, 1, len(plan.Scorers))
		// filter should be parsed into Expr when provided
		assert.NotNil(t, plan.Scorers[0])
	})

	t.Run("ok - multiple boost scorers", func(t *testing.T) {
		fs := &schemapb.FunctionScore{
			Functions: []*schemapb.FunctionSchema{
				makeBoostRanker("Int64Field > 0", "1.0"),
				makeBoostRanker("", "2.0"),
			},
		}
		plan, err := CreateSearchPlan(schema, "", "FloatVectorField", &planpb.QueryInfo{GroupByFieldId: -1}, nil, fs)
		assert.NoError(t, err)
		assert.NotNil(t, plan)
		assert.Equal(t, 2, len(plan.Scorers))
	})

	t.Run("error - not segment scorer flag", func(t *testing.T) {
		fs := &schemapb.FunctionScore{
			Functions: []*schemapb.FunctionSchema{{Params: []*commonpb.KeyValuePair{{Key: "reranker", Value: "weighted"}}}},
		}
		plan, err := CreateSearchPlan(schema, "", "FloatVectorField", &planpb.QueryInfo{GroupByFieldId: -1}, nil, fs)
		assert.NoError(t, err)
		// not segment scorer means ignored
		assert.NotNil(t, plan)
		assert.Equal(t, 0, len(plan.Scorers))
	})

	t.Run("error - missing weight", func(t *testing.T) {
		// missing weight should cause CreateSearchScorer to fail
		fs := &schemapb.FunctionScore{
			Functions: []*schemapb.FunctionSchema{
				{Params: []*commonpb.KeyValuePair{
					{Key: "reranker", Value: rerank.BoostName},
					// no weight
				}},
			},
		}
		_, err := CreateSearchPlan(schema, "", "FloatVectorField", &planpb.QueryInfo{}, nil, fs)
		assert.Error(t, err)
	})

	t.Run("error - invalid weight format", func(t *testing.T) {
		fs := &schemapb.FunctionScore{
			Functions: []*schemapb.FunctionSchema{
				makeBoostRanker("", "invalid_float"),
			},
		}
		_, err := CreateSearchPlan(schema, "", "FloatVectorField", &planpb.QueryInfo{}, nil, fs)
		assert.Error(t, err)
	})

	t.Run("error - scorer with group_by", func(t *testing.T) {
		fs := &schemapb.FunctionScore{
			Functions: []*schemapb.FunctionSchema{
				makeBoostRanker("", "1.0"),
			},
		}
		_, err := CreateSearchPlan(schema, "", "FloatVectorField", &planpb.QueryInfo{GroupByFieldId: 100}, nil, fs)
		assert.Error(t, err)
	})

	t.Run("error - scorer with search_iterator_v2", func(t *testing.T) {
		fs := &schemapb.FunctionScore{
			Functions: []*schemapb.FunctionSchema{
				makeBoostRanker("", "1.0"),
			},
		}
		_, err := CreateSearchPlan(schema, "", "FloatVectorField", &planpb.QueryInfo{SearchIteratorV2Info: &planpb.SearchIteratorV2Info{}}, nil, fs)
		assert.Error(t, err)
	})
}

func TestConcurrency(t *testing.T) {
	schemaHelper := newTestSchemaHelper(t)

	wg := sync.WaitGroup{}
	wg.Add(10)

	for i := 0; i < 10; i++ {
		go func() {
			defer wg.Done()
			for j := 0; j < 10; j++ {
				r := handleExpr(schemaHelper, fmt.Sprintf("array_length(ArrayField) == %d", j))
				err := getError(r)
				assert.NoError(t, err)
			}
		}()
	}

	wg.Wait()
}

func BenchmarkPlanCache(b *testing.B) {
	schema := newTestSchema(true)
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(b, err)

	b.ResetTimer()

	b.Run("cached", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			r := handleExpr(schemaHelper, "array_length(ArrayField) == 10")
			err := getError(r)
			assert.NoError(b, err)
		}
	})

	b.Run("uncached", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			r := handleExpr(schemaHelper, fmt.Sprintf("array_length(ArrayField) == %d", i))
			err := getError(r)
			assert.NoError(b, err)
		}
	})
}

func randomChineseString(length int) string {
	min := 0x4e00
	max := 0x9fa5

	result := make([]rune, length)
	for i := 0; i < length; i++ {
		result[i] = rune(rand.Intn(max-min+1) + min)
	}

	return string(result)
}

func BenchmarkWithString(b *testing.B) {
	schema := newTestSchema(true)
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(b, err)

	expr := ""
	for i := 0; i < 100; i++ {
		expr += fmt.Sprintf(`"%s",`, randomChineseString(rand.Intn(100)))
	}
	expr = "StringField in [" + expr + "]"

	for i := 0; i < b.N; i++ {
		plan, err := CreateSearchPlan(schemaHelper, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(b, err)
		assert.NotNil(b, plan)
	}
}

func Test_convertHanToASCII(t *testing.T) {
	type testcase struct {
		source string
		target string
	}
	testcases := []testcase{
		{`A in ["中国"]`, `A in ["\u4e2d\u56fd"]`},
		{`A in ["\中国"]`, `A in ["\中国"]`},
		{`A in ["\\中国"]`, `A in ["\\\u4e2d\u56fd"]`},
	}

	for _, c := range testcases {
		assert.Equal(t, c.target, convertHanToASCII(c.source))
	}
}

func BenchmarkTemplateWithString(b *testing.B) {
	schema := newTestSchema(true)
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(b, err)

	elements := make([]interface{}, 100)
	for i := 0; i < 100; i++ {
		elements[i] = generateTemplateValue(schemapb.DataType_String, fmt.Sprintf(`"%s",`, randomChineseString(rand.Intn(100))))
	}
	expr := "StringField in {list}"

	mv := map[string]*schemapb.TemplateValue{
		"list": generateTemplateValue(schemapb.DataType_Array, elements),
	}

	for i := 0; i < b.N; i++ {
		plan, err := CreateSearchPlan(schemaHelper, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, mv, nil)
		assert.NoError(b, err)
		assert.NotNil(b, plan)
	}
}

func TestNestedPathWithChinese(t *testing.T) {
	schema := newTestSchemaHelper(t)

	expr := `A["姓名"] == "小明"`
	plan, err := CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	paths := plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath()
	assert.NotNil(t, paths)
	assert.Equal(t, 2, len(paths))
	assert.Equal(t, "A", paths[0])
	assert.Equal(t, "姓名", paths[1])

	expr = `A["年份"]["月份"] == "九月"`
	plan, err = CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	assert.NoError(t, err, expr)
	paths = plan.GetVectorAnns().GetPredicates().GetUnaryRangeExpr().GetColumnInfo().GetNestedPath()
	assert.NotNil(t, paths)
	assert.Equal(t, 3, len(paths))
	assert.Equal(t, "A", paths[0])
	assert.Equal(t, "年份", paths[1])
	assert.Equal(t, "月份", paths[2])
}

func Test_JSONPathNullExpr(t *testing.T) {
	schema := newTestSchemaHelper(t)

	exprPairs := [][]string{
		{`A["a"] is null`, `not exists A["a"]`},
		{`A["a"] is not null`, `exists A["a"]`},
		{`dyn_field is null`, `not exists dyn_field`},
		{`dyn_field is not null`, `exists dyn_field`},
	}

	for _, expr := range exprPairs {
		plan, err := CreateSearchPlan(schema, expr[0], "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err)
		assert.NotNil(t, plan)

		plan2, err := CreateSearchPlan(schema, expr[1], "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err)
		assert.NotNil(t, plan2)

		planStr, err := proto.Marshal(plan)
		assert.NoError(t, err)
		plan2Str, err := proto.Marshal(plan2)
		assert.NoError(t, err)
		assert.Equal(t, planStr, plan2Str)
	}
}

// ============================================================================
// GIS Functions Tests
// ============================================================================

func TestExpr_GISFunctions(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test valid GIS function expressions
	validExprs := []string{
		// ST_EQUALS tests
		`st_equals(GeometryField, "POINT(0 0)")`,
		`ST_EQUALS(GeometryField, "POINT(1.5 2.3)")`,
		`st_equals(GeometryField, "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))")`,
		`st_equals(GeometryField, "LINESTRING(0 0, 1 1, 2 2)")`,
		`st_equals(GeometryField, "MULTIPOINT((0 0), (1 1))")`,

		// ST_INTERSECTS tests
		`st_intersects(GeometryField, "POINT(0 0)")`,
		`ST_INTERSECTS(GeometryField, "POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))")`,
		`st_intersects(GeometryField, "LINESTRING(-1 -1, 1 1)")`,

		// ST_CONTAINS tests
		`st_contains(GeometryField, "POINT(0.5 0.5)")`,
		`ST_CONTAINS(GeometryField, "POLYGON((-1 -1, 1 -1, 1 1, -1 1, -1 -1))")`,

		// ST_WITHIN tests
		`st_within(GeometryField, "POLYGON((-2 -2, 2 -2, 2 2, -2 2, -2 -2))")`,
		`ST_WITHIN(GeometryField, "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))")`,

		// ST_TOUCHES tests
		`st_touches(GeometryField, "POINT(1 1)")`,
		`ST_TOUCHES(GeometryField, "LINESTRING(0 0, 1 0)")`,

		// ST_OVERLAPS tests
		`st_overlaps(GeometryField, "POLYGON((0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, 0.5 0.5))")`,
		`ST_OVERLAPS(GeometryField, "POLYGON((-0.5 -0.5, 0.5 -0.5, 0.5 0.5, -0.5 0.5, -0.5 -0.5))")`,

		// ST_CROSSES tests
		`st_crosses(GeometryField, "LINESTRING(-1 0, 1 0)")`,
		`ST_CROSSES(GeometryField, "LINESTRING(0 -1, 0 1)")`,

		// ST_DWITHIN tests
		`st_dwithin(GeometryField, "POINT(0 0)", 1.0)`,
		`ST_DWITHIN(GeometryField, "POINT(1 1)", 5)`,
		`st_dwithin(GeometryField, "POINT(2.5 3.7)", 10.5)`,
		`ST_DWITHIN(GeometryField, "POINT(0.5 0.5)", 2.0)`,
		`st_dwithin(GeometryField, "POINT(1.0 1.0)", 1)`,

		// ST_ISVALID tests
		`st_isvalid(GeometryField)`,
		`ST_ISVALID(GeometryField)`,

		// Case insensitive tests
		`St_Equals(GeometryField, "POINT(0 0)")`,
		`sT_iNtErSeCts(GeometryField, "POINT(1 1)")`,
		`St_DWithin(GeometryField, "POINT(0 0)", 5.0)`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}
}

func TestExpr_GISFunctionsInvalidExpressions(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test invalid GIS function expressions
	invalidExprs := []string{
		// Invalid field type
		`st_equals(Int64Field, "POINT(0 0)")`,
		`st_intersects(StringField, "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))")`,
		`st_dwithin(BoolField, "POINT(0 0)", 1.0)`,

		// Invalid WKT strings
		`st_equals(GeometryField, "INVALID WKT")`,
		`st_intersects(GeometryField, "POINT()")`,
		`st_contains(GeometryField, "POLYGON((0 0, 1 0))")`, // Unclosed polygon
		`st_within(GeometryField, "LINESTRING(0)")`,         // Incomplete linestring

		// Missing parameters
		`st_equals(GeometryField)`,
		`st_intersects()`,
		`st_dwithin(GeometryField, "POINT(0 0)")`,       // Missing distance parameter
		`st_contains(GeometryField, "POINT(0 0)", 1.0)`, // Extra parameter

		// Invalid distance parameter for ST_DWITHIN
		`st_dwithin(GeometryField, "POINT(0 0)", "abc")`, // String parameter
		`st_dwithin(GeometryField, "POINT(0 0)", "invalid")`,
		`st_dwithin(GeometryField, "POINT(0 0)", -1.0)`, // Negative distance
		`st_dwithin(GeometryField, "POINT(0 0)", true)`, // Boolean instead of number

		// Non-existent fields
		`st_equals(NonExistentField, "POINT(0 0)")`,
		`st_dwithin(UnknownGeometryField, "POINT(0 0)", 5.0)`,

		// ST_ISVALID invalid usage
		`st_isvalid(Int64Field)`,
		`st_isvalid()`,
		`st_isvalid(GeometryField, 1)`,
	}

	for _, expr := range invalidExprs {
		assertInvalidExpr(t, schema, expr)
	}
}

func TestExpr_GISFunctionsComplexExpressions(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test complex GIS expressions with logical operators
	complexExprs := []string{
		// AND combinations
		`st_equals(GeometryField, "POINT(0 0)") and st_intersects(GeometryField, "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))")`,
		`st_contains(GeometryField, "POINT(0.5 0.5)") AND st_within(GeometryField, "POLYGON((-1 -1, 1 -1, 1 1, -1 1, -1 -1))")`,
		`st_dwithin(GeometryField, "POINT(0 0)", 5.0) and Int64Field > 100`,
		`st_isvalid(GeometryField) and Int64Field > 0`,

		// OR combinations
		`st_equals(GeometryField, "POINT(0 0)") or st_equals(GeometryField, "POINT(1 1)")`,
		`st_intersects(GeometryField, "POINT(0 0)") OR st_touches(GeometryField, "POINT(1 1)")`,
		`st_dwithin(GeometryField, "POINT(0 0)", 1.0) or st_dwithin(GeometryField, "POINT(5 5)", 2.0)`,

		// NOT combinations
		`not st_equals(GeometryField, "POINT(0 0)")`,
		`!(st_intersects(GeometryField, "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))"))`,
		`not (st_dwithin(GeometryField, "POINT(0 0)", 1.0))`,
		`not st_isvalid(GeometryField)`,
		// Mixed with other field types
		`st_contains(GeometryField, "POINT(0 0)") and StringField == "test"`,
		`st_dwithin(GeometryField, "POINT(0 0)", 5.0) or Int32Field in [1, 2, 3]`,
		`st_within(GeometryField, "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))") and FloatField > 0.5`,

		// Nested expressions
		`(st_equals(GeometryField, "POINT(0 0)") and Int64Field > 0) or (st_intersects(GeometryField, "POINT(1 1)") and StringField != "")`,
		`st_dwithin(GeometryField, "POINT(0 0)", 5.0) and (Int32Field > 10 or BoolField == true)`,
	}

	for _, expr := range complexExprs {
		assertValidExpr(t, schema, expr)
	}
}

func TestExpr_GISFunctionsWithDifferentGeometryTypes(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test different WKT geometry types
	geometryTests := []struct {
		gisFunc     string
		geometryWKT string
		description string
	}{
		// Point geometries
		{"st_equals", "POINT(0 0)", "Simple point"},
		{"st_intersects", "POINT(1.5 2.3)", "Point with decimals"},
		{"st_dwithin", "POINT(-1 -1)", "Point with negative coordinates"},

		// LineString geometries
		{"st_intersects", "LINESTRING(0 0, 1 1)", "Simple linestring"},
		{"st_crosses", "LINESTRING(-1 0, 1 0)", "Horizontal linestring"},
		{"st_contains", "LINESTRING(0 0, 1 1, 2 2)", "Multi-segment linestring"},

		// Polygon geometries
		{"st_within", "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))", "Simple polygon"},
		{"st_overlaps", "POLYGON((-1 -1, 1 -1, 1 1, -1 1, -1 -1))", "Centered polygon"},
		{"st_contains", "POLYGON((0 0, 2 0, 2 2, 0 2, 0 0), (0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, 0.5 0.5))", "Polygon with hole"},

		// Multi geometries
		{"st_intersects", "MULTIPOINT((0 0), (1 1), (2 2))", "Multiple points"},
		{"st_crosses", "MULTILINESTRING((0 0, 1 0), (1 1, 2 1))", "Multiple linestrings"},
		{"st_overlaps", "MULTIPOLYGON(((0 0, 1 0, 1 1, 0 1, 0 0)), ((2 2, 3 2, 3 3, 2 3, 2 2)))", "Multiple polygons"},

		// Collection geometries
		{"st_intersects", "GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(1 1, 2 2))", "Mixed geometry collection"},
	}

	for _, test := range geometryTests {
		exprStr := fmt.Sprintf(`%s(GeometryField, "%s")`, test.gisFunc, test.geometryWKT)
		if test.gisFunc == "st_dwithin" {
			exprStr = fmt.Sprintf(`%s(GeometryField, "%s", 5.0)`, test.gisFunc, test.geometryWKT)
		}

		assertValidExpr(t, schema, exprStr)
	}
}

func TestExpr_GISFunctionsWithVariousDistances(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test ST_DWITHIN with various distance values
	distanceTests := []struct {
		distance    interface{}
		shouldPass  bool
		description string
	}{
		// Valid distances (including zero)
		{0, true, "Zero distance (integer)"},
		{0.0, true, "Zero distance (float)"},
		{1, true, "Integer distance"},
		{1.0, true, "Float distance"},
		{0.5, true, "Small decimal distance"},
		{1000.0, true, "Large distance"},
		{99999999.999, true, "Very large distance"},
		{0.000001, true, "Very small distance"},

		// Valid distance expressions as strings that should be parsed
		{"0", true, "String zero integer"},
		{"0.0", true, "String zero float"},
		{"1", true, "String integer"},
		{"1.5", true, "String float"},
	}

	for _, test := range distanceTests {
		var exprStr string
		switch v := test.distance.(type) {
		case int:
			exprStr = fmt.Sprintf(`st_dwithin(GeometryField, "POINT(0 0)", %d)`, v)
		case float64:
			exprStr = fmt.Sprintf(`st_dwithin(GeometryField, "POINT(0 0)", %g)`, v)
		case string:
			exprStr = fmt.Sprintf(`st_dwithin(GeometryField, "POINT(0 0)", %s)`, v)
		}

		if test.shouldPass {
			assertValidExpr(t, schema, exprStr)
		} else {
			assertInvalidExpr(t, schema, exprStr)
		}
	}
}

func TestExpr_GISFunctionsPlanGeneration(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test that GIS expressions can be used in search plans
	gisExprs := []string{
		`st_equals(GeometryField, "POINT(0 0)")`,
		`st_intersects(GeometryField, "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))")`,
		`st_dwithin(GeometryField, "POINT(0 0)", 5.0)`,
		`st_contains(GeometryField, "POINT(0.5 0.5)") and Int64Field > 100`,
		`st_within(GeometryField, "POLYGON((-1 -1, 1 -1, 1 1, -1 1, -1 -1))") or StringField == "test"`,
	}

	for _, expr := range gisExprs {
		plan, err := CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         10,
			MetricType:   "L2",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err, "Failed to create plan for expression: %s", expr)
		assert.NotNil(t, plan, "Plan should not be nil for expression: %s", expr)
		assert.NotNil(t, plan.GetVectorAnns(), "Vector annotations should not be nil for expression: %s", expr)

		if plan.GetVectorAnns().GetPredicates() != nil {
			// Verify that the plan contains GIS function filter expressions
			// This ensures that the GIS expressions are properly parsed and converted to plan nodes
			predicates := plan.GetVectorAnns().GetPredicates()
			assert.NotNil(t, predicates, "Predicates should not be nil for GIS expression: %s", expr)
		}
	}
}

func TestExpr_GISFunctionsWithJSONFields(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test invalid usage with JSON fields - GIS functions should only work with geometry fields
	invalidJSONGISExprs := []string{
		`st_equals(JSONField, "POINT(0 0)")`,
		`st_intersects($meta["geometry"], "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))")`,
		`st_dwithin(A, "POINT(0 0)", 5.0)`, // Dynamic field
		`st_contains(JSONField["geom"], "POINT(0.5 0.5)")`,
	}

	for _, expr := range invalidJSONGISExprs {
		assertInvalidExpr(t, schema, expr)
	}
}

func TestExpr_GISFunctionsZeroDistance(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test zero distance specifically for ST_DWITHIN
	zeroDistanceExprs := []string{
		// Integer zero
		`st_dwithin(GeometryField, "POINT(0 0)", 0)`,
		// Float zero
		`st_dwithin(GeometryField, "POINT(1 1)", 0.0)`,
		// Zero in complex expressions
		`st_dwithin(GeometryField, "POINT(0 0)", 0) and Int64Field > 10`,
	}

	for _, expr := range zeroDistanceExprs {
		assertValidExpr(t, schema, expr)
	}

	// Test that zero distance expressions can generate valid search plans
	for _, expr := range zeroDistanceExprs {
		plan, err := CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         10,
			MetricType:   "L2",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err, "Failed to create plan for zero distance expression: %s", expr)
		assert.NotNil(t, plan, "Plan should not be nil for zero distance expression: %s", expr)
	}

	// Test that negative distances are still invalid
	invalidNegativeExprs := []string{
		`st_dwithin(GeometryField, "POINT(0 0)", -1)`,
		`st_dwithin(GeometryField, "POINT(0 0)", -0.1)`,
		`st_dwithin(GeometryField, "POINT(0 0)", -100.5)`,
	}

	for _, expr := range invalidNegativeExprs {
		assertInvalidExpr(t, schema, expr)
	}
}

func TestExpr_GISFunctionsInvalidParameterTypes(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test various invalid parameter types for ST_DWITHIN distance parameter
	invalidTypeExprs := []string{
		// String parameters (should be rejected)
		`st_dwithin(GeometryField, "POINT(0 0)", "abc")`,
		`st_dwithin(GeometryField, "POINT(0 0)", "123")`,    // Numeric string
		`st_dwithin(GeometryField, "POINT(0 0)", "123.45")`, // Float string
		`st_dwithin(GeometryField, "POINT(0 0)", "0")`,      // Zero string
		`st_dwithin(GeometryField, "POINT(0 0)", "-5")`,     // Negative string

		// Boolean parameters (should be rejected)
		`st_dwithin(GeometryField, "POINT(0 0)", true)`,
		`st_dwithin(GeometryField, "POINT(0 0)", false)`,

		// Array/complex parameters (should be rejected)
		`st_dwithin(GeometryField, "POINT(0 0)", [1, 2, 3])`,
		`st_dwithin(GeometryField, "POINT(0 0)", GeometryField)`, // Field reference instead of literal
	}

	for _, expr := range invalidTypeExprs {
		assertInvalidExpr(t, schema, expr)
	}
}

func TestExpr_ElementFilter(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	// Valid expressions
	validExprs := []string{
		`element_filter(struct_array, 2 > $[sub_int] > 1)`,
		`element_filter(struct_array, $[sub_int] > 1)`,
		`element_filter(struct_array, $[sub_int] == 100)`,
		`element_filter(struct_array, $[sub_int] >= 0)`,
		`element_filter(struct_array, $[sub_int] <= 1000)`,
		`element_filter(struct_array, $[sub_int] != 0)`,

		`element_filter(struct_array, $[sub_str] == "1")`,
		`element_filter(struct_array, $[sub_str] != "")`,

		`element_filter(struct_array, $[sub_str] == "1" || $[sub_int] > 1)`,
		`element_filter(struct_array, $[sub_str] == "1" && $[sub_int] > 1)`,
		`element_filter(struct_array, $[sub_int] > 0 && $[sub_int] < 100)`,

		`element_filter(struct_array, ($[sub_int] > 0 && $[sub_int] < 100) || $[sub_str] == "default")`,
		`element_filter(struct_array, !($[sub_int] < 0))`,

		`Int64Field > 0 && element_filter(struct_array, $[sub_int] > 1)`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, helper, expr)
	}

	// Invalid expressions
	invalidExprs := []string{
		`element_filter(struct_array, element_filter(struct_array, $[sub_int] > 1))`,
		`element_filter(struct_array, $[sub_int] > 1 && element_filter(struct_array, $[sub_str] == "1"))`,

		`$[sub_int] > 1`,
		`Int64Field > 0 && $[sub_int] > 1`,

		`element_filter(struct_array, $[non_existent_field] > 1)`,
		`element_filter(non_existent_array, $[sub_int] > 1)`,

		`element_filter(struct_array)`, // missing element expression
		`element_filter()`,             // missing all parameters

		`element_filter(struct_array, $[sub_int] > 1) || element_filter(struct_array, $[sub_str] == "test")`,
		`element_filter(struct_array, $[sub_int] > 1) && Int64Field > 0`,

		`not element_filter(struct_array, $[sub_int] > 1)`,
		`!element_filter(struct_array, $[sub_int] > 1)`,
	}

	for _, expr := range invalidExprs {
		assertInvalidExpr(t, helper, expr)
	}
}

func TestExpr_Match(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	// Valid MATCH_ALL expressions
	validExprs := []string{
		// MATCH_ALL: all elements must match
		`MATCH_ALL(struct_array, $[sub_int] > 1)`,
		`MATCH_ALL(struct_array, $[sub_int] == 100)`,
		`MATCH_ALL(struct_array, $[sub_str] == "aaa")`,
		`MATCH_ALL(struct_array, $[sub_str] == "aaa" && $[sub_int] > 100)`,
		`MATCH_ALL(struct_array, $[sub_str] != "" || $[sub_int] >= 0)`,

		// MATCH_ANY: at least one element must match
		`MATCH_ANY(struct_array, $[sub_int] > 1)`,
		`MATCH_ANY(struct_array, $[sub_int] == 100)`,
		`MATCH_ANY(struct_array, $[sub_str] == "aaa")`,
		`MATCH_ANY(struct_array, $[sub_str] == "aaa" && $[sub_int] > 100)`,

		// MATCH_LEAST: at least N elements must match
		`MATCH_LEAST(struct_array, $[sub_int] > 1, threshold=3)`,
		`MATCH_LEAST(struct_array, $[sub_str] == "aaa", threshold=1)`,
		`MATCH_LEAST(struct_array, $[sub_str] == "aaa" && $[sub_int] > 100, threshold=2)`,

		// MATCH_MOST: at most N elements must match
		`MATCH_MOST(struct_array, $[sub_int] > 1, threshold=3)`,
		`MATCH_MOST(struct_array, $[sub_str] == "aaa", threshold=0)`,
		`MATCH_MOST(struct_array, $[sub_str] == "aaa" && $[sub_int] > 100, threshold=5)`,

		// MATCH_EXACT: exactly N elements must match
		`MATCH_EXACT(struct_array, $[sub_int] > 1, threshold=2)`,
		`MATCH_EXACT(struct_array, $[sub_str] == "aaa", threshold=0)`,
		`MATCH_EXACT(struct_array, $[sub_str] == "aaa" && $[sub_int] > 100, threshold=3)`,

		// Combined with other expressions (match must be last)
		`Int64Field > 0 && MATCH_ALL(struct_array, $[sub_int] > 1)`,
		`Int64Field > 0 && MATCH_ANY(struct_array, $[sub_str] == "test")`,
		`Int64Field > 0 && MATCH_LEAST(struct_array, $[sub_int] > 1, threshold=2)`,

		// Complex predicates
		`MATCH_ALL(struct_array, ($[sub_int] > 0 && $[sub_int] < 100) || $[sub_str] == "default")`,
		`MATCH_ANY(struct_array, !($[sub_int] < 0))`,

		// Case insensitivity
		`match_all(struct_array, $[sub_int] > 1)`,
		`match_any(struct_array, $[sub_int] > 1)`,
		`match_least(struct_array, $[sub_int] > 1, threshold=2)`,
		`match_most(struct_array, $[sub_int] > 1, threshold=2)`,
		`match_exact(struct_array, $[sub_int] > 1, threshold=2)`,

		// Multiple match expressions with logical operators
		`MATCH_ALL(struct_array, $[sub_int] > 1) || MATCH_ANY(struct_array, $[sub_str] == "test")`,
		`MATCH_ALL(struct_array, $[sub_int] > 1) && MATCH_ANY(struct_array, $[sub_str] == "test")`,
		`MATCH_ANY(struct_array, $[sub_int] > 1) || Int64Field > 0`,
		`MATCH_ALL(struct_array, $[sub_int] > 1) && Int64Field > 0`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, helper, expr)
	}

	// Test proto structure assertions
	t.Run("MatchAll_Proto", func(t *testing.T) {
		expr, err := ParseExpr(helper, `MATCH_ALL(struct_array, $[sub_int] > 1)`, nil)
		assert.NoError(t, err)
		assert.NotNil(t, expr.GetMatchExpr())
		assert.Equal(t, "struct_array", expr.GetMatchExpr().GetStructName())
		assert.Equal(t, planpb.MatchType_MatchAll, expr.GetMatchExpr().GetMatchType())
		assert.Equal(t, int64(0), expr.GetMatchExpr().GetCount())
	})

	t.Run("MatchAny_Proto", func(t *testing.T) {
		expr, err := ParseExpr(helper, `MATCH_ANY(struct_array, $[sub_str] == "aaa")`, nil)
		assert.NoError(t, err)
		assert.NotNil(t, expr.GetMatchExpr())
		assert.Equal(t, "struct_array", expr.GetMatchExpr().GetStructName())
		assert.Equal(t, planpb.MatchType_MatchAny, expr.GetMatchExpr().GetMatchType())
		assert.Equal(t, int64(0), expr.GetMatchExpr().GetCount())
	})

	t.Run("MatchLeast_Proto", func(t *testing.T) {
		expr, err := ParseExpr(helper, `MATCH_LEAST(struct_array, $[sub_int] > 1, threshold=3)`, nil)
		assert.NoError(t, err)
		assert.NotNil(t, expr.GetMatchExpr())
		assert.Equal(t, "struct_array", expr.GetMatchExpr().GetStructName())
		assert.Equal(t, planpb.MatchType_MatchLeast, expr.GetMatchExpr().GetMatchType())
		assert.Equal(t, int64(3), expr.GetMatchExpr().GetCount())
	})

	t.Run("MatchMost_Proto", func(t *testing.T) {
		expr, err := ParseExpr(helper, `MATCH_MOST(struct_array, $[sub_str] == "aaa", threshold=5)`, nil)
		assert.NoError(t, err)
		assert.NotNil(t, expr.GetMatchExpr())
		assert.Equal(t, "struct_array", expr.GetMatchExpr().GetStructName())
		assert.Equal(t, planpb.MatchType_MatchMost, expr.GetMatchExpr().GetMatchType())
		assert.Equal(t, int64(5), expr.GetMatchExpr().GetCount())
	})

	t.Run("MatchExact_Proto", func(t *testing.T) {
		expr, err := ParseExpr(helper, `MATCH_EXACT(struct_array, $[sub_int] == 100, threshold=2)`, nil)
		assert.NoError(t, err)
		assert.NotNil(t, expr.GetMatchExpr())
		assert.Equal(t, "struct_array", expr.GetMatchExpr().GetStructName())
		assert.Equal(t, planpb.MatchType_MatchExact, expr.GetMatchExpr().GetMatchType())
		assert.Equal(t, int64(2), expr.GetMatchExpr().GetCount())
	})

	// Invalid expressions
	invalidExprs := []string{
		// Nested match expressions not allowed
		`MATCH_ALL(struct_array, MATCH_ANY(struct_array, $[sub_int] > 1))`,
		`MATCH_ANY(struct_array, $[sub_int] > 1 && MATCH_ALL(struct_array, $[sub_str] == "1"))`,

		// MATCH predicates must be evaluated at element level
		`MATCH_ALL(struct_array, Int64Field > 0)`,
		`MATCH_ANY(struct_array, $[sub_int] > 1 && Int64Field > 0)`,
		`MATCH_LEAST(struct_array, Int64Field > 0, threshold=1)`,
		`MATCH_MOST(struct_array, Int64Field > 0, threshold=1)`,
		`MATCH_EXACT(struct_array, Int64Field > 0, threshold=1)`,
		`MATCH_ALL(struct_array, $[sub_int] > Int64Field)`,
		`MATCH_ALL(struct_array, true)`,
		`MATCH_ANY(struct_array, ($[sub_int] == 2) && (true || $[sub_int] == 1))`,
		`MATCH_ANY(struct_array, $[sub_int] == $[sub_int])`,

		// Raw element columns and function calls are not supported by C++ MATCH execution
		`MATCH_ANY(struct_array, $[sub_int])`,
		`MATCH_ANY(struct_array, empty($[sub_str]))`,

		// Contains executors still produce row-level bitmaps
		`MATCH_ANY(struct_array, array_contains($[sub_int], 1))`,
		`MATCH_ANY(struct_array, array_contains_any($[sub_int], [1, 2]))`,
		`MATCH_ANY(struct_array, array_contains_all($[sub_int], [1, 2]))`,

		// $[field] syntax outside match context
		`$[sub_int] > 1`,
		`Int64Field > 0 && $[sub_int] > 1`,

		// Non-existent fields
		`MATCH_ALL(struct_array, $[non_existent_field] > 1)`,
		`MATCH_ALL(non_existent_array, $[sub_int] > 1)`,

		// Missing parameters
		`MATCH_ALL(struct_array)`,
		`MATCH_ALL()`,
		`MATCH_ANY(struct_array)`,
		`MATCH_ANY()`,
		`MATCH_LEAST(struct_array, $[sub_int] > 1)`, // missing count
		`MATCH_MOST(struct_array, $[sub_int] > 1)`,  // missing count
		`MATCH_EXACT(struct_array, $[sub_int] > 1)`, // missing count

		// MATCH_ALL/MATCH_ANY should not have count parameter
		`MATCH_ALL(struct_array, $[sub_int] > 1, 3)`,
		`MATCH_ANY(struct_array, $[sub_int] > 1, 2)`,

		// Invalid count values
		`MATCH_LEAST(struct_array, $[sub_int] > 1, threshold=0)`,  // count must be positive for MATCH_LEAST
		`MATCH_LEAST(struct_array, $[sub_int] > 1, threshold=-1)`, // negative count
		`MATCH_MOST(struct_array, $[sub_int] > 1, threshold=-1)`,  // negative count
		`MATCH_EXACT(struct_array, $[sub_int] > 1, threshold=-1)`, // negative count
	}

	for _, expr := range invalidExprs {
		assertInvalidExpr(t, helper, expr)
	}
}

func TestExpr_ArrayContains(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	// Valid ArrayContains expressions
	validExprs := []string{
		`array_contains(struct_array[sub_int], 1)`,
		`array_contains(struct_array[sub_int], 1) && array_contains(struct_array[sub_int], 2)`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, helper, expr)
	}
}

func TestExpr_StructIndexField(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	assert.NoError(t, err)

	// Valid struct_arr[index][sub_field] expressions
	validExprs := []string{
		// comparison
		`struct_array[0][sub_int] > 50`,
		`struct_array[0][sub_int] == 100`,
		`struct_array[1][sub_str] == "apple"`,
		`struct_array[0][sub_str] != "banana"`,
		// range via &&
		`struct_array[0][sub_int] >= 30 && struct_array[0][sub_int] <= 70`,
		// IN / NOT IN
		`struct_array[0][sub_int] in [10, 20, 30, 40, 50]`,
		`struct_array[0][sub_int] not in [0, 1, 2, 3]`,
		`struct_array[0][sub_str] in ["apple", "banana"]`,
		`struct_array[0][sub_str] not in ["apple", "banana"]`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, helper, expr)
	}

	// Invalid expressions
	invalidExprs := []string{
		// non-existent parent or sub-field
		`non_existent[0][sub_int] > 50`,
		`struct_array[0][non_existent] > 50`,
		// unsupported form: sub-field first then index
		`struct_array[sub_int][0] > 50`,
		`struct_array[sub_str][0] == "apple"`,
		`struct_array[sub_int][0] in [10, 20, 30]`,
	}

	for _, expr := range invalidExprs {
		assertInvalidExpr(t, helper, expr)
	}
}

func TestExpr_StructIndexField_PlanShape(t *testing.T) {
	schema := newTestSchemaHelper(t)

	plan, err := CreateSearchPlan(schema, `struct_array[0][sub_int] == 100`, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	require.NoError(t, err)

	predicates := plan.GetVectorAnns().GetPredicates()
	require.NotNil(t, predicates)

	ure := predicates.GetUnaryRangeExpr()
	require.NotNil(t, ure)
	assert.Equal(t, planpb.OpType_Equal, ure.GetOp())
	assert.Equal(t, int64(100), ure.GetValue().GetInt64Val())

	columnInfo := ure.GetColumnInfo()
	require.NotNil(t, columnInfo)
	assert.Equal(t, int64(134), columnInfo.GetFieldId())
	assert.Equal(t, schemapb.DataType_Array, columnInfo.GetDataType())
	assert.Equal(t, schemapb.DataType_Int32, columnInfo.GetElementType())
	assert.Equal(t, []string{"0"}, columnInfo.GetNestedPath())

	ret := handleExpr(schema, `struct_array[1][sub_str] in ["apple", "banana"]`)
	ewt, ok := ret.(*ExprWithType)
	require.True(t, ok, "handleExpr should return *ExprWithType for term expression")

	te := ewt.expr.GetTermExpr()
	require.NotNil(t, te)
	assert.Len(t, te.GetValues(), 2)
	assert.Equal(t, "apple", te.GetValues()[0].GetStringVal())
	assert.Equal(t, "banana", te.GetValues()[1].GetStringVal())

	columnInfo = te.GetColumnInfo()
	require.NotNil(t, columnInfo)
	assert.Equal(t, int64(133), columnInfo.GetFieldId())
	assert.Equal(t, schemapb.DataType_Array, columnInfo.GetDataType())
	assert.Equal(t, schemapb.DataType_VarChar, columnInfo.GetElementType())
	assert.Equal(t, []string{"1"}, columnInfo.GetNestedPath())
}

func TestExpr_StructFieldArrayLength(t *testing.T) {
	schema := newTestSchemaHelper(t)

	ret := handleExpr(schema, `array_length(struct_array)`)
	ewt, ok := ret.(*ExprWithType)
	require.True(t, ok, "handleExpr should return *ExprWithType")
	assert.Equal(t, schemapb.DataType_Int64, ewt.dataType)

	bae := ewt.expr.GetBinaryArithExpr()
	require.NotNil(t, bae)
	assert.Equal(t, planpb.ArithOpType_ArrayLength, bae.GetOp())
	require.Nil(t, bae.GetRight())

	columnInfo := bae.GetLeft().GetColumnExpr().GetInfo()
	require.NotNil(t, columnInfo)
	assert.Equal(t, int64(133), columnInfo.GetFieldId())
	assert.Equal(t, schemapb.DataType_Array, columnInfo.GetDataType())
	assert.Equal(t, schemapb.DataType_VarChar, columnInfo.GetElementType())
	assert.Empty(t, columnInfo.GetNestedPath())

	ret = handleExpr(schema, `array_length(struct_array[sub_int])`)
	ewt, ok = ret.(*ExprWithType)
	require.True(t, ok, "handleExpr should return *ExprWithType")
	assert.Equal(t, schemapb.DataType_Int64, ewt.dataType)

	bae = ewt.expr.GetBinaryArithExpr()
	require.NotNil(t, bae)
	assert.Equal(t, planpb.ArithOpType_ArrayLength, bae.GetOp())
	require.Nil(t, bae.GetRight())

	columnInfo = bae.GetLeft().GetColumnExpr().GetInfo()
	require.NotNil(t, columnInfo)
	assert.Equal(t, int64(134), columnInfo.GetFieldId())
	assert.Equal(t, schemapb.DataType_Array, columnInfo.GetDataType())
	assert.Equal(t, schemapb.DataType_Int32, columnInfo.GetElementType())
	assert.Empty(t, columnInfo.GetNestedPath())

	validExprs := []string{
		`array_length(struct_array) == 10`,
		`array_length(struct_array[sub_int]) == 10`,
		`array_length(struct_array[sub_str]) > 0`,
	}
	for _, expr := range validExprs {
		_, err := CreateSearchPlan(schema, expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		assert.NoError(t, err, expr)
	}
}

func TestExpr_VectorArrayOnlyStructParentArrayLength(t *testing.T) {
	schema := newTestSchema(false)
	schema.StructArrayFields = append(schema.StructArrayFields, &schemapb.StructArrayFieldSchema{
		FieldID:  10000,
		Name:     "vector_struct",
		Nullable: true,
		Fields: []*schemapb.FieldSchema{
			{
				FieldID:     10001,
				Name:        "vector_struct[embeddings]",
				DataType:    schemapb.DataType_ArrayOfVector,
				ElementType: schemapb.DataType_FloatVector,
				Nullable:    true,
				TypeParams: []*commonpb.KeyValuePair{
					{Key: common.DimKey, Value: "4"},
				},
			},
		},
	})
	helper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(t, err)

	for _, expr := range []string{
		`array_length(vector_struct)`,
		`array_length(vector_struct[embeddings])`,
	} {
		ret := handleExpr(helper, expr)
		ewt, ok := ret.(*ExprWithType)
		require.True(t, ok, "handleExpr should return *ExprWithType")
		assert.Equal(t, schemapb.DataType_Int64, ewt.dataType)

		bae := ewt.expr.GetBinaryArithExpr()
		require.NotNil(t, bae)
		assert.Equal(t, planpb.ArithOpType_ArrayLength, bae.GetOp())

		columnInfo := bae.GetLeft().GetColumnExpr().GetInfo()
		require.NotNil(t, columnInfo)
		assert.Equal(t, int64(10001), columnInfo.GetFieldId())
		assert.Equal(t, schemapb.DataType_ArrayOfVector, columnInfo.GetDataType())
		assert.Equal(t, schemapb.DataType_FloatVector, columnInfo.GetElementType())
		assert.True(t, columnInfo.GetNullable())
	}

	_, err = CreateSearchPlan(helper, `array_length(vector_struct) == 2`, "FloatVectorField", &planpb.QueryInfo{
		Topk:         0,
		MetricType:   "",
		SearchParams: "",
		RoundDecimal: 0,
	}, nil, nil)
	require.NoError(t, err)
}

func TestExpr_StructIndexField_RangeForms(t *testing.T) {
	schema := newTestSchemaHelper(t)

	testCases := []struct {
		expr           string
		lower          int64
		upper          int64
		lowerInclusive bool
		upperInclusive bool
	}{
		{
			expr:           `1 < struct_array[0][sub_int] < 3`,
			lower:          1,
			upper:          3,
			lowerInclusive: false,
			upperInclusive: false,
		},
		{
			expr:           `0 <= struct_array[0][sub_int] <= 100`,
			lower:          0,
			upper:          100,
			lowerInclusive: true,
			upperInclusive: true,
		},
		{
			expr:           `100 > struct_array[0][sub_int] > 0`,
			lower:          0,
			upper:          100,
			lowerInclusive: false,
			upperInclusive: false,
		},
		{
			expr:           `100 >= struct_array[0][sub_int] >= 0`,
			lower:          0,
			upper:          100,
			lowerInclusive: true,
			upperInclusive: true,
		},
	}

	for _, tc := range testCases {
		plan, err := CreateSearchPlan(schema, tc.expr, "FloatVectorField", &planpb.QueryInfo{
			Topk:         0,
			MetricType:   "",
			SearchParams: "",
			RoundDecimal: 0,
		}, nil, nil)
		require.NoError(t, err, tc.expr)

		predicates := plan.GetVectorAnns().GetPredicates()
		require.NotNil(t, predicates, tc.expr)

		bre := predicates.GetBinaryRangeExpr()
		require.NotNil(t, bre, tc.expr)
		assert.Equal(t, tc.lower, bre.GetLowerValue().GetInt64Val(), tc.expr)
		assert.Equal(t, tc.upper, bre.GetUpperValue().GetInt64Val(), tc.expr)
		assert.Equal(t, tc.lowerInclusive, bre.GetLowerInclusive(), tc.expr)
		assert.Equal(t, tc.upperInclusive, bre.GetUpperInclusive(), tc.expr)

		columnInfo := bre.GetColumnInfo()
		require.NotNil(t, columnInfo, tc.expr)
		assert.Equal(t, int64(134), columnInfo.GetFieldId(), tc.expr)
		assert.Equal(t, schemapb.DataType_Array, columnInfo.GetDataType(), tc.expr)
		assert.Equal(t, schemapb.DataType_Int32, columnInfo.GetElementType(), tc.expr)
		assert.Equal(t, []string{"0"}, columnInfo.GetNestedPath(), tc.expr)
	}
}

// ============================================================================
// Timestamptz Expression Tests
// These tests cover VisitTimestamptzCompareForward and VisitTimestamptzCompareReverse
// which are used for optimized timestamptz comparisons with optional INTERVAL arithmetic
// ============================================================================

func newTestSchemaWithTimestamptz(t *testing.T) *typeutil.SchemaHelper {
	// Create schema with Timestamptz field for testing
	// The newTestSchema already includes all DataType values including Timestamptz
	schema := newTestSchema(true)
	schemaHelper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(t, err)
	return schemaHelper
}

func TestExpr_TimestamptzCompareForward(t *testing.T) {
	schema := newTestSchemaWithTimestamptz(t)

	// Test valid timestamptz forward comparisons (column op ISO value)
	// Format: TimestamptzField [+|- INTERVAL 'duration'] <op> ISO 'timestamp'
	// Note: ISO keyword is required before the timestamp string literal
	validExprs := []string{
		// Simple comparisons without INTERVAL (quick path)
		`TimestamptzField > ISO '2025-01-01T00:00:00Z'`,
		`TimestamptzField >= ISO '2025-01-01T00:00:00Z'`,
		`TimestamptzField < ISO '2025-12-31T23:59:59Z'`,
		`TimestamptzField <= ISO '2025-06-15T12:00:00Z'`,
		`TimestamptzField == ISO '2025-03-20T10:30:00Z'`,
		`TimestamptzField != ISO '2025-08-10T08:00:00Z'`,

		// Comparisons with INTERVAL (slow path with arithmetic)
		`TimestamptzField + INTERVAL 'P1D' > ISO '2025-01-01T00:00:00Z'`,
		`TimestamptzField - INTERVAL 'P1D' < ISO '2025-12-31T23:59:59Z'`,
		`TimestamptzField + INTERVAL 'PT1H' >= ISO '2025-06-15T12:00:00Z'`,
		`TimestamptzField - INTERVAL 'PT30M' <= ISO '2025-03-20T10:30:00Z'`,
		`TimestamptzField + INTERVAL 'P1Y' == ISO '2026-01-01T00:00:00Z'`,
		`TimestamptzField - INTERVAL 'P6M' != ISO '2024-06-01T00:00:00Z'`,

		// Complex INTERVAL durations
		`TimestamptzField + INTERVAL 'P1Y2M3D' > ISO '2025-01-01T00:00:00Z'`,
		`TimestamptzField + INTERVAL 'PT10H30M15S' < ISO '2025-12-31T23:59:59Z'`,
		`TimestamptzField - INTERVAL 'P1Y2M3DT4H5M6S' >= ISO '2024-01-01T00:00:00Z'`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}
}

func TestExpr_TimestamptzCompareReverse(t *testing.T) {
	schema := newTestSchemaWithTimestamptz(t)

	// Test valid timestamptz reverse comparisons (ISO value op column)
	// Format: ISO 'timestamp' <op> TimestamptzField [+|- INTERVAL 'duration']
	// Note: ISO keyword is required before the timestamp string
	// Note: Operator gets reversed internally (e.g., '>' becomes '<')
	validExprs := []string{
		// Simple reverse comparisons without INTERVAL (quick path)
		`ISO '2025-01-01T00:00:00Z' < TimestamptzField`,
		`ISO '2025-01-01T00:00:00Z' <= TimestamptzField`,
		`ISO '2025-12-31T23:59:59Z' > TimestamptzField`,
		`ISO '2025-06-15T12:00:00Z' >= TimestamptzField`,
		`ISO '2025-03-20T10:30:00Z' == TimestamptzField`,
		`ISO '2025-08-10T08:00:00Z' != TimestamptzField`,

		// Reverse comparisons with INTERVAL after field (slow path with arithmetic)
		`ISO '2025-01-01T00:00:00Z' < TimestamptzField + INTERVAL 'P1D'`,
		`ISO '2025-12-31T23:59:59Z' > TimestamptzField - INTERVAL 'P1D'`,
		`ISO '2025-06-15T12:00:00Z' <= TimestamptzField + INTERVAL 'PT1H'`,
		`ISO '2025-03-20T10:30:00Z' >= TimestamptzField - INTERVAL 'PT30M'`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}
}

func TestExpr_TimestamptzCompareInvalid(t *testing.T) {
	schema := newTestSchemaWithTimestamptz(t)

	// Test invalid timestamptz expressions
	// Note: ISO keyword is required for timestamptz comparisons
	invalidExprs := []string{
		// Invalid field type for timestamptz operations (non-timestamptz field with INTERVAL)
		`Int64Field + INTERVAL 'P1D' > ISO '2025-01-01T00:00:00Z'`,
		`VarCharField + INTERVAL 'P1D' < ISO '2025-01-01T00:00:00Z'`,

		// Invalid timestamp format with ISO
		`TimestamptzField > ISO 'invalid-timestamp'`,
		`TimestamptzField < ISO '2025-13-01T00:00:00Z'`, // Invalid month
		`TimestamptzField > ISO '2025-01-32T00:00:00Z'`, // Invalid day

		// Invalid interval format
		`TimestamptzField + INTERVAL 'invalid' > ISO '2025-01-01T00:00:00Z'`,
		`TimestamptzField + INTERVAL '1D' > ISO '2025-01-01T00:00:00Z'`, // Missing P prefix
	}

	for _, expr := range invalidExprs {
		assertInvalidExpr(t, schema, expr)
	}
}

// ============================================================================
// Power Expression Tests
// These tests cover VisitPower for constant power operations
// ============================================================================

func TestExpr_Power(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test valid power expressions with constants
	validExprs := []string{
		// Integer powers
		`2 ** 3 == 8`,
		`3 ** 2 == 9`,
		`10 ** 0 == 1`,

		// Float powers
		`2.0 ** 3.0 == 8.0`,
		`4.0 ** 0.5 > 1.0`,

		// Negative exponents
		`2 ** -1 == 0.5`,

		// Used in arithmetic expressions
		`Int64Field + (2 ** 3) > 0`,
		`Int64Field * (10 ** 2) < 1000`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}

	// Test invalid power expressions - power requires constant operands
	invalidExprs := []string{
		// Power with field operands (not allowed)
		`Int64Field ** 2 == 100`,
		`2 ** Int64Field == 8`,
		`Int64Field ** Int64Field == 1`,
	}

	for _, expr := range invalidExprs {
		assertInvalidExpr(t, schema, expr)
	}
}

// ============================================================================
// Error Handling Tests
// These tests cover the int64OverflowError type and error handling paths
// ============================================================================

func TestInt64OverflowError(t *testing.T) {
	// Test int64OverflowError.Error() method - covers the Error() method at 0% coverage
	err := &int64OverflowError{literal: "9223372036854775808"}
	assert.Contains(t, err.Error(), "int64 overflow")
	assert.Contains(t, err.Error(), "9223372036854775808")

	// Test isInt64OverflowError helper function
	assert.True(t, isInt64OverflowError(err))
	assert.False(t, isInt64OverflowError(fmt.Errorf("some other error")))
	assert.False(t, isInt64OverflowError(nil))
}

// ============================================================================
// reverseCompareOp Tests
// This function is used internally to reverse comparison operators
// ============================================================================

func Test_reverseCompareOp(t *testing.T) {
	// Test all comparison operator reversals
	// This covers the reverseCompareOp function at 0% coverage
	tests := []struct {
		input    planpb.OpType
		expected planpb.OpType
	}{
		{planpb.OpType_LessThan, planpb.OpType_GreaterThan},
		{planpb.OpType_LessEqual, planpb.OpType_GreaterEqual},
		{planpb.OpType_GreaterThan, planpb.OpType_LessThan},
		{planpb.OpType_GreaterEqual, planpb.OpType_LessEqual},
		{planpb.OpType_Equal, planpb.OpType_Equal},
		{planpb.OpType_NotEqual, planpb.OpType_NotEqual},
		{planpb.OpType_Invalid, planpb.OpType_Invalid},
		{planpb.OpType_PrefixMatch, planpb.OpType_Invalid}, // Unknown ops return Invalid
	}

	for _, tt := range tests {
		result := reverseCompareOp(tt.input)
		assert.Equal(t, tt.expected, result, "reverseCompareOp(%v)", tt.input)
	}
}

// ============================================================================
// Additional Coverage Tests for Edge Cases
// ============================================================================

func TestExpr_AdditionalEdgeCases(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test valid edge case expressions
	validExprs := []string{
		// Floating point edge cases
		`FloatField > 1e10`,
		`DoubleField < 1e-10`,
		`FloatField == 3.14159265358979`,

		// Boolean expressions
		`true == true`,
		`false != true`,

		// Empty string comparison
		`StringField == ""`,
		`VarCharField != ""`,

		// JSON with complex nested paths
		`JSONField["level1"]["level2"]["level3"] > 0`,

		// Array length operations
		`array_length(ArrayField) > 0`,
		`array_length(ArrayField) == 10`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}
}

func TestExpr_InvalidOperatorCombinations(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Test invalid operator combinations that should fail
	// These test the error paths in various Visit methods
	invalidExprs := []string{
		// Shift operations not supported
		`Int64Field << 2`,
		`Int64Field >> 2`,

		// Bitwise operations not supported
		`Int64Field & 0xFF`,
		`Int64Field | 0xFF`,
		`Int64Field ^ 0xFF`,

		// Type mismatches
		`"string" + 1`,
		`BoolField + 1`,
	}

	for _, expr := range invalidExprs {
		assertInvalidExpr(t, schema, expr)
	}
}

// TestExpr_VisitBooleanEdgeCases tests edge cases in VisitBoolean
// Boolean literals must be used in comparison expressions, not as standalone values
func TestExpr_VisitBooleanEdgeCases(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Valid boolean comparison expressions
	// Note: Standalone boolean values or fields are not valid filter expressions
	// They must be used in comparisons
	validExprs := []string{
		`true == true`,
		`false == false`,
		`true != false`,
		`BoolField == true`,
		`BoolField != false`,
		`BoolField == BoolField`,
		`not (BoolField == true)`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}
}

// TestExpr_VisitFloatingEdgeCases tests edge cases in VisitFloating
func TestExpr_VisitFloatingEdgeCases(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Valid floating point literal expressions
	validExprs := []string{
		`FloatField > 0.0`,
		`FloatField < 1.0e10`,
		`FloatField >= -1.0e-10`,
		`FloatField <= 3.14159265`,
		`DoubleField == 2.718281828`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}
}

// TestExpr_VisitRangeEdgeCases tests edge cases in VisitRange and VisitReverseRange
func TestExpr_VisitRangeEdgeCases(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Valid range expressions
	validExprs := []string{
		// Forward range: lower < field < upper
		`1 < Int64Field < 10`,
		`0.0 < FloatField < 1.0`,
		`"a" < StringField < "z"`,

		// Forward range with equal
		`1 <= Int64Field < 10`,
		`1 < Int64Field <= 10`,
		`1 <= Int64Field <= 10`,

		// Reverse range: upper > field > lower
		`10 > Int64Field > 1`,
		`1.0 > FloatField > 0.0`,
		`"z" > StringField > "a"`,

		// Reverse range with equal
		`10 >= Int64Field > 1`,
		`10 > Int64Field >= 1`,
		`10 >= Int64Field >= 1`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}

	// Invalid range expressions
	invalidExprs := []string{
		// Range on bool type is invalid
		`true < BoolField < false`,

		// Non-const bounds
		`Int64Field < Int32Field < Int64Field`,
	}

	for _, expr := range invalidExprs {
		assertInvalidExpr(t, schema, expr)
	}
}

// TestExpr_VisitUnaryEdgeCases tests edge cases in VisitUnary
// Unary operators (not/!) must produce boolean expressions for filter predicates
func TestExpr_VisitUnaryEdgeCases(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Valid unary expressions - must produce boolean filter predicates
	validExprs := []string{
		`not (Int64Field > 0)`,
		`!(Int64Field < 10)`,
		`not (BoolField == true)`,
		`not (true == false)`,
		`!(FloatField >= 1.0)`,
		// Unary negation used in comparison context
		`Int64Field > -1`,
		`Int64Field < -(-5)`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}
}

// TestExpr_ConstantFolding tests constant folding in arithmetic expressions
func TestExpr_ConstantFolding(t *testing.T) {
	schema := newTestSchemaHelper(t)

	// Expressions where constants can be folded
	validExprs := []string{
		// Add/Sub constant folding
		`Int64Field > (1 + 2)`,
		`Int64Field < (10 - 5)`,
		`Int64Field == (1 + 2 + 3)`,

		// Mul/Div/Mod constant folding
		`Int64Field > (2 * 3)`,
		`Int64Field < (10 / 2)`,
		`Int64Field == (10 % 3)`,

		// Mixed operations
		`Int64Field > (2 * 3 + 4)`,
		`Int64Field < (10 - 2 * 3)`,

		// Float constant folding
		`FloatField > (1.0 + 2.0)`,
		`FloatField < (10.0 / 2.0)`,
	}

	for _, expr := range validExprs {
		assertValidExpr(t, schema, expr)
	}
}

// TestExpr_BooleanLiteral verifies how standalone "true"/"false" literals
// are parsed by the proxy expression parser.
//
// Key behavior:
//   - Standalone "true" is converted to AlwaysTrueExpr
//   - Standalone "false" is converted to AlwaysFalseExpr (UnaryExpr(Not, AlwaysTrueExpr))
//   - Combined expressions like "BoolField == true" or "1==1" work fine
//   - After rewriting, "1==1" becomes AlwaysTrueExpr, "1==2" becomes AlwaysFalseExpr
func TestExpr_BooleanLiteral(t *testing.T) {
	schema := newTestSchema(true)
	helper, err := typeutil.CreateSchemaHelper(schema)
	require.NoError(t, err)

	// Case 1: standalone "true" variants → AlwaysTrueExpr
	for _, exprStr := range []string{"true", "True", "TRUE"} {
		expr, err := ParseExpr(helper, exprStr, nil)
		require.NoError(t, err, "standalone %q should succeed", exprStr)
		assert.NotNil(t, expr.GetAlwaysTrueExpr(),
			"standalone %q should become AlwaysTrueExpr", exprStr)
	}

	// Case 1b: standalone "false" variants → AlwaysFalseExpr
	for _, exprStr := range []string{"false", "False", "FALSE"} {
		expr, err := ParseExpr(helper, exprStr, nil)
		require.NoError(t, err, "standalone %q should succeed", exprStr)
		ue := expr.GetUnaryExpr()
		require.NotNil(t, ue, "standalone %q should be AlwaysFalseExpr", exprStr)
		assert.Equal(t, planpb.UnaryExpr_Not, ue.GetOp())
		assert.NotNil(t, ue.GetChild().GetAlwaysTrueExpr())
	}

	// Case 2: verify that handleExpr (internal) parses them into ValueExpr with Bool
	for _, exprStr := range []string{"true", "false"} {
		ret := handleExpr(helper, exprStr)
		ewt, ok := ret.(*ExprWithType)
		require.True(t, ok, "handleExpr(%q) should return *ExprWithType", exprStr)
		assert.Equal(t, schemapb.DataType_Bool, ewt.dataType)
		assert.True(t, ewt.nodeDependent, "boolean literal should be nodeDependent")

		ve := ewt.expr.GetValueExpr()
		require.NotNil(t, ve, "should be ValueExpr for %q", exprStr)
		if exprStr == "true" {
			assert.True(t, ve.GetValue().GetBoolVal())
		} else {
			assert.False(t, ve.GetValue().GetBoolVal())
		}
	}

	// Case 3: boolean literals in valid combined expressions
	// These all produce executable boolean predicates
	validBoolExprs := []string{
		"BoolField == true",
		"BoolField == false",
		"BoolField != true",
		"BoolField != false",
		"BoolField in [true, false]",
	}
	for _, exprStr := range validBoolExprs {
		assertValidExpr(t, helper, exprStr)
	}

	// Case 4: constant-folded expressions become AlwaysTrueExpr / AlwaysFalseExpr
	// "1==1" constant-folds to ValueExpr(true), then rewriter converts to AlwaysTrueExpr
	exprTrue, err := ParseExpr(helper, "1==1", nil)
	require.NoError(t, err)
	assert.NotNil(t, exprTrue.GetAlwaysTrueExpr(),
		"1==1 should be rewritten to AlwaysTrueExpr")

	// "1==2" constant-folds to ValueExpr(false), then rewriter converts to AlwaysFalseExpr
	// AlwaysFalseExpr is represented as UnaryExpr(Not, AlwaysTrueExpr)
	exprFalse, err := ParseExpr(helper, "1==2", nil)
	require.NoError(t, err)
	ue := exprFalse.GetUnaryExpr()
	require.NotNil(t, ue, "1==2 should be rewritten to UnaryExpr(Not, AlwaysTrueExpr)")
	assert.Equal(t, planpb.UnaryExpr_Not, ue.GetOp())
	assert.NotNil(t, ue.GetChild().GetAlwaysTrueExpr())

	// Case 5: empty expression becomes AlwaysTrueExpr (special case in handleExprInternal)
	exprEmpty, err := ParseExpr(helper, "", nil)
	require.NoError(t, err)
	assert.NotNil(t, exprEmpty.GetAlwaysTrueExpr(),
		"empty expression should be AlwaysTrueExpr")

	// Case 6: "true and false" / "true or false" — two boolean literals connected by logical operators
	// Both sides are GenericValue (from VisitBoolean), so VisitLogicalAnd calls And() which
	// constant-folds to ValueExpr(BoolVal = true && false = false) with nodeDependent=false (default).
	// Since nodeDependent=false, canBeExecuted() passes, then rewriter converts to AlwaysFalseExpr.
	t.Run("true_and_false", func(t *testing.T) {
		expr, err := ParseExpr(helper, "true and false", nil)
		require.NoError(t, err, "\"true and false\" should be valid")
		// And(true, false) = false → rewriter → AlwaysFalseExpr = UnaryExpr(Not, AlwaysTrueExpr)
		ue := expr.GetUnaryExpr()
		require.NotNil(t, ue, "should be AlwaysFalseExpr (UnaryExpr Not)")
		assert.Equal(t, planpb.UnaryExpr_Not, ue.GetOp())
		assert.NotNil(t, ue.GetChild().GetAlwaysTrueExpr())
	})

	t.Run("true_and_true", func(t *testing.T) {
		expr, err := ParseExpr(helper, "true and true", nil)
		require.NoError(t, err, "\"true and true\" should be valid")
		// And(true, true) = true → rewriter → AlwaysTrueExpr
		assert.NotNil(t, expr.GetAlwaysTrueExpr(),
			"\"true and true\" should become AlwaysTrueExpr")
	})

	t.Run("true_or_false", func(t *testing.T) {
		expr, err := ParseExpr(helper, "true or false", nil)
		require.NoError(t, err, "\"true or false\" should be valid")
		// Or(true, false) = true → rewriter → AlwaysTrueExpr
		assert.NotNil(t, expr.GetAlwaysTrueExpr(),
			"\"true or false\" should become AlwaysTrueExpr")
	})

	t.Run("false_or_false", func(t *testing.T) {
		expr, err := ParseExpr(helper, "false or false", nil)
		require.NoError(t, err, "\"false or false\" should be valid")
		// Or(false, false) = false → rewriter → AlwaysFalseExpr
		ue := expr.GetUnaryExpr()
		require.NotNil(t, ue, "should be AlwaysFalseExpr")
		assert.Equal(t, planpb.UnaryExpr_Not, ue.GetOp())
		assert.NotNil(t, ue.GetChild().GetAlwaysTrueExpr())
	})

	t.Run("false_and_false", func(t *testing.T) {
		expr, err := ParseExpr(helper, "false and false", nil)
		require.NoError(t, err, "\"false and false\" should be valid")
		// And(false, false) = false → rewriter → AlwaysFalseExpr
		ue := expr.GetUnaryExpr()
		require.NotNil(t, ue, "should be AlwaysFalseExpr")
		assert.Equal(t, planpb.UnaryExpr_Not, ue.GetOp())
		assert.NotNil(t, ue.GetChild().GetAlwaysTrueExpr())
	})

	// Case 7: boolean literal mixed with expression (issue #48443)
	t.Run("true_or_expr", func(t *testing.T) {
		// true or (Int64Field > 50) → AlwaysTrueExpr (short-circuit)
		expr, err := ParseExpr(helper, "true or (Int64Field > 50)", nil)
		require.NoError(t, err)
		assert.NotNil(t, expr.GetAlwaysTrueExpr(),
			"\"true or expr\" should become AlwaysTrueExpr")
	})

	t.Run("expr_or_true", func(t *testing.T) {
		// (Int64Field > 50) or true → AlwaysTrueExpr (short-circuit)
		expr, err := ParseExpr(helper, "(Int64Field > 50) or true", nil)
		require.NoError(t, err)
		assert.NotNil(t, expr.GetAlwaysTrueExpr(),
			"\"expr or true\" should become AlwaysTrueExpr")
	})

	t.Run("false_or_expr", func(t *testing.T) {
		// false or (Int64Field > 50) → Int64Field > 50
		expr, err := ParseExpr(helper, "false or (Int64Field > 50)", nil)
		require.NoError(t, err)
		assert.NotNil(t, expr.GetUnaryRangeExpr(),
			"\"false or expr\" should return the expr itself")
	})

	t.Run("true_and_expr", func(t *testing.T) {
		// true and (Int64Field > 50) → Int64Field > 50
		expr, err := ParseExpr(helper, "true and (Int64Field > 50)", nil)
		require.NoError(t, err)
		assert.NotNil(t, expr.GetUnaryRangeExpr(),
			"\"true and expr\" should return the expr itself")
	})

	t.Run("expr_and_true", func(t *testing.T) {
		// (Int64Field > 50) and true → Int64Field > 50
		expr, err := ParseExpr(helper, "(Int64Field > 50) and true", nil)
		require.NoError(t, err)
		assert.NotNil(t, expr.GetUnaryRangeExpr(),
			"\"expr and true\" should return the expr itself")
	})

	t.Run("false_and_expr", func(t *testing.T) {
		// false and (Int64Field > 50) → AlwaysFalseExpr (short-circuit)
		expr, err := ParseExpr(helper, "false and (Int64Field > 50)", nil)
		require.NoError(t, err)
		ue := expr.GetUnaryExpr()
		require.NotNil(t, ue, "\"false and expr\" should become AlwaysFalseExpr")
		assert.Equal(t, planpb.UnaryExpr_Not, ue.GetOp())
		assert.NotNil(t, ue.GetChild().GetAlwaysTrueExpr())
	})

	t.Run("expr_and_false", func(t *testing.T) {
		// (Int64Field > 50) and false → AlwaysFalseExpr (short-circuit)
		expr, err := ParseExpr(helper, "(Int64Field > 50) and false", nil)
		require.NoError(t, err)
		ue := expr.GetUnaryExpr()
		require.NotNil(t, ue, "\"expr and false\" should become AlwaysFalseExpr")
		assert.Equal(t, planpb.UnaryExpr_Not, ue.GetOp())
		assert.NotNil(t, ue.GetChild().GetAlwaysTrueExpr())
	})

	// Case 8: non-boolean literal with logical operators should still fail
	t.Run("int_or_expr", func(t *testing.T) {
		_, err := ParseExpr(helper, "1 or (Int64Field > 50)", nil)
		assert.Error(t, err)
	})

	t.Run("string_and_expr", func(t *testing.T) {
		_, err := ParseExpr(helper, "\"hello\" and (Int64Field > 50)", nil)
		assert.Error(t, err)
	})
}
