mirror of
https://github.com/milvus-io/milvus.git
synced 2026-10-12 02:19:10 +00:00
Fields named `iso` or `interval` can be created, but filters such as `iso > 1` fail because the lexer emits a keyword token where the parser expects an identifier. Accept 20 contextual keyword families through a shared `fieldName` rule in expression field positions while preserving their function, option, and timestamp syntax. Update the visitor and regenerate the parser with ANTLR 4.13.2. Reject `LIKE`, `AND`, `OR`, `NOT`, and `IN` as field names in every casing, and retain the existing case-insensitive `NULL` policy. Validate struct-array parent names on both Create and Add paths, alongside child names. Classify `ErrFieldInvalidName` (1701) as `InputError` at its definition so ordinary names, reserved names, and RootCoord's add-struct-field validator report the same classification. Remove the redundant Proxy error markers and validate each struct parent name once while preserving the existing validation order, codes, reasons, identity, and non-retryability. Compatibility: mixed-case names such as `And`, `In`, and `Like` previously lexed as ordinary identifiers and could be created and filtered. New Create/Add requests reject these names. Existing collections are not revalidated, but backup restoration or cross-cluster schema recreation containing these names will require renaming the affected fields. This tightening is intentional; contextual keyword field names remain supported. Regression coverage includes contextual keywords and their dedicated syntax, field identity/casing, SLL/LL parsing, core keyword rejection, ordinary and struct-array Create/Add paths, reserved field names, and InputError status/metric round trips. RootCoord's name validator now also has classification and status round-trip coverage. Validation: - Current review follow-up: all tests in `pkg/util/merr`, `pkg/util/requestutil`, and `pkg/common` passed with `-tags dynamic,test -gcflags='all=-N -l' -count=1`; `git diff --check` passed. - Current focused Proxy/RootCoord tests were blocked before execution by older local native libraries missing required APIs. The development host was inaccessible under the current network restrictions; native CI validation is pending. - Before this follow-up, the unchanged parser/rewriter implementation passed 1,182 tests/subtests, focused Proxy regressions passed 248 tests/subtests with race detection and coverage, and `merr`/`requestutil` guards passed 143 tests/subtests with race detection and coverage. - Generated parser output was reproduced with ANTLR 4.13.2. - A previous full `make -o build-cpp-with-unittest test-go` attempt timed out in `TestProxy/create_collection` while waiting for streaming assignments and metadata-cache initialization. Later groups were not reached; no fresh C++ build was performed. issue: #53925 Fixes #53925 --------- Signed-off-by: xiaofanluan <[email protected]> Co-authored-by: xiaofanluan <[email protected]>
244 lines
11 KiB
ANTLR
244 lines
11 KiB
ANTLR
grammar Plan;
|
|
|
|
expr:
|
|
fieldName (op1=(ADD | SUB) INTERVAL interval_string=StringLiteral)? op2=(LT | LE | GT | GE | EQ | NE) ISO compare_string=StringLiteral # TimestamptzCompareForward
|
|
| ISO compare_string=StringLiteral op2=(LT | LE | GT | GE | EQ | NE) fieldName (op1=(ADD | SUB) INTERVAL interval_string=StringLiteral)? # TimestamptzCompareReverse
|
|
| IntegerConstant # Integer
|
|
| FloatingConstant # Floating
|
|
| BooleanConstant # Boolean
|
|
| StringLiteral # String
|
|
| RawStringLiteral # RawString
|
|
| (fieldName|Meta) # Identifier
|
|
| JSONIdentifier # JSONIdentifier
|
|
| StructFieldIdentifier # StructField
|
|
| StructIndexFieldIdentifier # StructIndexField
|
|
| StructSubFieldIdentifier # StructSubField
|
|
| LBRACE fieldName RBRACE # TemplateVariable
|
|
| '(' expr ')' # Parens
|
|
| '[' expr (',' expr)* ','? ']' # Array
|
|
| EmptyArray # EmptyArray
|
|
| EXISTS expr # Exists
|
|
| expr LIKE expr # Like
|
|
| expr REGEXMATCH expr # RegexMatch
|
|
| expr REGEXNOTMATCH expr # RegexNotMatch
|
|
| TEXTMATCH'('fieldName',' expr (',' textMatchOption)? ')' # TextMatch
|
|
| TEXTMATCHFUZZY'('fieldName',' expr ',' Identifier ASSIGN IntegerConstant ')' # TextMatchFuzzy
|
|
| PHRASEMATCH'('fieldName',' expr (',' expr)? ')' # PhraseMatch
|
|
| RANDOMSAMPLE'(' expr ')' # RandomSample
|
|
| ElementFilter'('fieldName',' expr')' # ElementFilter
|
|
| op=(MATCH_ALL | MATCH_ANY) '(' fieldName ',' expr ')' # MatchSimple
|
|
| op=(MATCH_LEAST | MATCH_MOST | MATCH_EXACT) '(' fieldName ',' expr ',' THRESHOLD ASSIGN IntegerConstant ')' # MatchThreshold
|
|
| expr POW expr # Power
|
|
| op = (ADD | SUB | BNOT | NOT) expr # Unary
|
|
// | '(' typeName ')' expr # Cast
|
|
| expr op = (MUL | DIV | MOD) expr # MulDivMod
|
|
| expr op = (ADD | SUB) expr # AddSub
|
|
| expr op = (SHL | SHR) expr # Shift
|
|
| expr op = NOT? IN expr # Term
|
|
| (JSONContains | ArrayContains)'('expr',' expr')' # JSONContains
|
|
| (JSONContainsAll | ArrayContainsAll)'('expr',' expr')' # JSONContainsAll
|
|
| (JSONContainsAny | ArrayContainsAny)'('expr',' expr')' # JSONContainsAny
|
|
| op=(STEuqals | STTouches | STOverlaps | STCrosses | STContains | STIntersects | STWithin) '(' fieldName ',' expr ')' # SpatialBinary
|
|
| STDWithin'('fieldName',' expr',' expr')' # STDWithin
|
|
| STIsValid'('fieldName')' # STIsValid
|
|
| ArrayLength'('(fieldName | JSONIdentifier | StructFieldIdentifier | StructSubFieldIdentifier)')' # ArrayLength
|
|
| function=Identifier '(' field=fieldReference ',' expr ',' option=Identifier ASSIGN kind=Identifier ')' # MembershipMatchWithOption
|
|
| Identifier '(' ( expr (',' expr )* ','? )? ')' # Call
|
|
| expr op1 = (LT | LE) (fieldName | JSONIdentifier | StructSubFieldIdentifier | StructIndexFieldIdentifier) op2 = (LT | LE) expr # Range
|
|
| expr op1 = (GT | GE) (fieldName | JSONIdentifier | StructSubFieldIdentifier | StructIndexFieldIdentifier) op2 = (GT | GE) expr # ReverseRange
|
|
| expr op = (LT | LE | GT | GE) expr # Relational
|
|
| expr op = (EQ | NE) expr # Equality
|
|
| expr BAND expr # BitAnd
|
|
| expr BXOR expr # BitXor
|
|
| expr BOR expr # BitOr
|
|
| expr AND expr # LogicalAnd
|
|
| expr OR expr # LogicalOr
|
|
| (fieldName | JSONIdentifier) ISNULL # IsNull
|
|
| (fieldName | JSONIdentifier) ISNOTNULL # IsNotNull;
|
|
|
|
// These keywords retain their specialized syntax while also naming fields.
|
|
// Operators and constants remain reserved, and generic function names and
|
|
// option names continue to use Identifier rather than this rule.
|
|
fieldName:
|
|
Identifier
|
|
| ISO
|
|
| INTERVAL
|
|
| MINIMUM_SHOULD_MATCH
|
|
| THRESHOLD
|
|
| TEXTMATCHFUZZY
|
|
| MATCH_ALL
|
|
| MATCH_ANY
|
|
| MATCH_LEAST
|
|
| MATCH_MOST
|
|
| MATCH_EXACT
|
|
| ElementFilter
|
|
| STEuqals
|
|
| STTouches
|
|
| STOverlaps
|
|
| STCrosses
|
|
| STContains
|
|
| STIntersects
|
|
| STWithin
|
|
| STDWithin
|
|
| STIsValid;
|
|
|
|
fieldReference:
|
|
fieldName
|
|
| Meta
|
|
| JSONIdentifier
|
|
| StructFieldIdentifier
|
|
| StructIndexFieldIdentifier
|
|
| StructSubFieldIdentifier;
|
|
|
|
textMatchOption:
|
|
MINIMUM_SHOULD_MATCH ASSIGN IntegerConstant;
|
|
|
|
LBRACE: '{';
|
|
RBRACE: '}';
|
|
|
|
LT: '<';
|
|
LE: '<=';
|
|
GT: '>';
|
|
GE: '>=';
|
|
EQ: '==';
|
|
NE: '!=';
|
|
|
|
LIKE: 'like' | 'LIKE';
|
|
EXISTS: 'exists' | 'EXISTS';
|
|
TEXTMATCH: 'text_match'|'TEXT_MATCH';
|
|
TEXTMATCHFUZZY: 'text_match_fuzzy'|'TEXT_MATCH_FUZZY';
|
|
PHRASEMATCH: 'phrase_match'|'PHRASE_MATCH';
|
|
RANDOMSAMPLE: 'random_sample' | 'RANDOM_SAMPLE';
|
|
MATCH_ALL: 'match_all' | 'MATCH_ALL';
|
|
MATCH_ANY: 'match_any' | 'MATCH_ANY';
|
|
MATCH_LEAST: 'match_least' | 'MATCH_LEAST';
|
|
MATCH_MOST: 'match_most' | 'MATCH_MOST';
|
|
MATCH_EXACT: 'match_exact' | 'MATCH_EXACT';
|
|
INTERVAL: 'interval' | 'INTERVAL';
|
|
ISO: 'iso' | 'ISO';
|
|
MINIMUM_SHOULD_MATCH: 'minimum_should_match' | 'MINIMUM_SHOULD_MATCH';
|
|
THRESHOLD: 'threshold' | 'THRESHOLD';
|
|
REGEXMATCH: '=~';
|
|
REGEXNOTMATCH: '!~';
|
|
ASSIGN: '=';
|
|
|
|
ADD: '+';
|
|
SUB: '-';
|
|
MUL: '*';
|
|
DIV: '/';
|
|
MOD: '%';
|
|
POW: '**';
|
|
SHL: '<<';
|
|
SHR: '>>';
|
|
BAND: '&';
|
|
BOR: '|';
|
|
BXOR: '^';
|
|
|
|
AND: '&&' | 'and' | 'AND';
|
|
OR: '||' | 'or' | 'OR';
|
|
|
|
ISNULL: 'is null' | 'IS NULL';
|
|
ISNOTNULL: 'is not null' | 'IS NOT NULL';
|
|
|
|
BNOT: '~';
|
|
NOT: '!' | 'not' | 'NOT';
|
|
|
|
IN: 'in' | 'IN';
|
|
EmptyArray: '[' (Whitespace | Newline)* ']';
|
|
|
|
JSONContains: 'json_contains' | 'JSON_CONTAINS';
|
|
JSONContainsAll: 'json_contains_all' | 'JSON_CONTAINS_ALL';
|
|
JSONContainsAny: 'json_contains_any' | 'JSON_CONTAINS_ANY';
|
|
|
|
ArrayContains: 'array_contains' | 'ARRAY_CONTAINS';
|
|
ArrayContainsAll: 'array_contains_all' | 'ARRAY_CONTAINS_ALL';
|
|
ArrayContainsAny: 'array_contains_any' | 'ARRAY_CONTAINS_ANY';
|
|
ArrayLength: 'array_length' | 'ARRAY_LENGTH';
|
|
ElementFilter: 'element_filter' | 'ELEMENT_FILTER';
|
|
|
|
STEuqals:'st_equals' | 'ST_EQUALS';
|
|
STTouches:'st_touches' | 'ST_TOUCHES';
|
|
STOverlaps: 'st_overlaps' | 'ST_OVERLAPS';
|
|
STCrosses: 'st_crosses' | 'ST_CROSSES';
|
|
STContains: 'st_contains' | 'ST_CONTAINS';
|
|
STIntersects : 'st_intersects' | 'ST_INTERSECTS';
|
|
STWithin :'st_within' | 'ST_WITHIN';
|
|
STDWithin: 'st_dwithin' | 'ST_DWITHIN';
|
|
STIsValid: 'st_isvalid' | 'ST_ISVALID';
|
|
|
|
BooleanConstant: 'true' | 'True' | 'TRUE' | 'false' | 'False' | 'FALSE';
|
|
|
|
IntegerConstant:
|
|
DecimalConstant
|
|
| OctalConstant
|
|
| HexadecimalConstant
|
|
| BinaryConstant;
|
|
|
|
FloatingConstant:
|
|
DecimalFloatingConstant
|
|
| HexadecimalFloatingConstant;
|
|
|
|
Identifier: Nondigit (Nondigit | Digit)*;
|
|
Meta: '$meta';
|
|
|
|
StringLiteral: EncodingPrefix? ('"' DoubleSCharSequence? '"' | '\'' SingleSCharSequence? '\'');
|
|
RawStringLiteral: [rR] ('"' DoubleRChar* '"' | '\'' SingleRChar* '\'');
|
|
JSONIdentifier: (Identifier | Meta)('[' (StringLiteral | RawStringLiteral | DecimalConstant) ']')+;
|
|
StructIndexFieldIdentifier: Identifier '[' DecimalConstant ']' '[' Identifier ']';
|
|
StructFieldIdentifier: Identifier '[' Identifier ']';
|
|
StructSubFieldIdentifier: '$[' Identifier ']';
|
|
|
|
fragment EncodingPrefix: 'u8' | 'u' | 'U' | 'L';
|
|
|
|
fragment DoubleSCharSequence: DoubleSChar+;
|
|
fragment SingleSCharSequence: SingleSChar+;
|
|
|
|
fragment DoubleSChar: ~["\\\r\n] | EscapeSequence | '\\\n' | '\\\r\n';
|
|
fragment SingleSChar: ~['\\\r\n] | EscapeSequence | '\\\n' | '\\\r\n';
|
|
// Raw string chars: a backslash is kept verbatim (no unescaping). A backslash
|
|
// before the delimiter only prevents termination; both bytes stay in the token.
|
|
fragment DoubleRChar: ~["\\\r\n] | '\\' ~[\r\n];
|
|
fragment SingleRChar: ~['\\\r\n] | '\\' ~[\r\n];
|
|
fragment Nondigit: [a-zA-Z_];
|
|
fragment Digit: [0-9];
|
|
fragment BinaryConstant: '0' [bB] [0-1]+;
|
|
fragment DecimalConstant: NonzeroDigit Digit* | '0';
|
|
fragment OctalConstant: '0' OctalDigit*;
|
|
fragment HexadecimalConstant: '0' [xX] HexadecimalDigitSequence;
|
|
fragment NonzeroDigit: [1-9];
|
|
fragment OctalDigit: [0-7];
|
|
fragment HexadecimalDigit: [0-9a-fA-F];
|
|
fragment HexQuad:
|
|
HexadecimalDigit HexadecimalDigit HexadecimalDigit HexadecimalDigit;
|
|
fragment UniversalCharacterName:
|
|
'\\u' HexQuad
|
|
| '\\U' HexQuad HexQuad;
|
|
fragment DecimalFloatingConstant:
|
|
FractionalConstant ExponentPart?
|
|
| DigitSequence ExponentPart;
|
|
fragment HexadecimalFloatingConstant:
|
|
'0' [xX] (
|
|
HexadecimalFractionalConstant
|
|
| HexadecimalDigitSequence
|
|
) BinaryExponentPart;
|
|
fragment FractionalConstant:
|
|
DigitSequence? '.' DigitSequence
|
|
| DigitSequence '.';
|
|
fragment ExponentPart: [eE] [+-]? DigitSequence;
|
|
fragment DigitSequence: Digit+;
|
|
fragment HexadecimalFractionalConstant:
|
|
HexadecimalDigitSequence? '.' HexadecimalDigitSequence
|
|
| HexadecimalDigitSequence '.';
|
|
fragment HexadecimalDigitSequence: HexadecimalDigit+;
|
|
fragment BinaryExponentPart: [pP] [+-]? DigitSequence;
|
|
fragment EscapeSequence:
|
|
'\\' ['"?abfnrtv\\]
|
|
| '\\' OctalDigit OctalDigit? OctalDigit?
|
|
| '\\x' HexadecimalDigitSequence
|
|
| UniversalCharacterName
|
|
| '\\' ~[\r\n];
|
|
|
|
Whitespace: [ \t]+ -> skip;
|
|
|
|
Newline: ( '\r' '\n'? | '\n') -> skip;
|