pub struct OperatorSyntax {Show 18 fields
pub operator_construct: bool,
pub containment_operators: bool,
pub json_arrow_operators: bool,
pub jsonb_operators: bool,
pub double_equals: bool,
pub integer_divide_slash: bool,
pub starts_with_operator: bool,
pub is_general_equality: bool,
pub truth_value_tests: bool,
pub null_safe_equals: bool,
pub lambda_expressions: bool,
pub bitwise_operators: bool,
pub quantified_comparisons: bool,
pub quantified_comparison_lists: bool,
pub quantified_arbitrary_operator: bool,
pub custom_operators: bool,
pub null_test_postfix: bool,
pub postfix_operators: bool,
}Expand description
Dialect-owned infix/prefix operator syntax accepted by the parser.
The operator-acceptance and operator-spelling family, split out of ExpressionSyntax
when it crossed its 16-field line. Each flag decides whether the parser admits the
operator, while the binding powers that order them live in BindingPowerTable; a
spelling that folds onto an existing operator carries a spelling tag so it round-trips.
A flag whose form needs a dialect-gated lexeme says “also gates the tokenizer” in its
first paragraph — a flag crossing the lexer/parser boundary must declare it there.
Fields§
§operator_construct: boolAccept the PostgreSQL explicit-operator infix form a OPERATOR(schema.op) b.
containment_operators: boolAccept PostgreSQL’s containment operators — infix @> (contains) and <@
(contained by). Also gates the tokenizer: the @>/<@ lexemes are recognised
only under a dialect that sets this (PostgreSQL), so elsewhere @> stays a stray
@ then >, and <@ a < then a stray @. The @> munch requires the
following >, so a bare @ is always a stray byte here — the prefix @
absolute-value operator is a scoped follow-up, since its bare-@ lexeme contends
with the T-SQL/MySQL @name sigils and needs a tracked conflict. The <@ munch,
by contrast, shadows an abutting @name sigil (a<@x meaning a < @x) whenever
this is on together with a single-@ form — the tracked
LexicalConflict::ContainmentOperatorVersusAtName. The operators bind at
PostgreSQL’s “any other operator” precedence (BindingPowerTable::any_operator).
json_arrow_operators: boolAccept PostgreSQL’s JSON access operators — infix -> (field/element as
json) and ->> (as text). Also gates the tokenizer: the ->/->>
lexemes are munched only under a dialect that sets this, so elsewhere a->b
stays a - then a >. MySQL spells the same JSON accessors and can enable
this independently of containment_operators,
which is why the two are separate flags. Same “any other operator” precedence.
jsonb_operators: boolAccept PostgreSQL’s jsonb existence / path / search operators as one family: ?
(key exists), ?| (any key exists), ?& (all keys exist), @? (jsonpath returns
any item), @@ (jsonpath predicate match, also the tsvector @@ tsquery full-text
match), #> (extract at path), #>> (extract at path as text), and #- (delete at
path). Also gates the tokenizer: these lexemes are munched only under a dialect that
sets this. One flag for the whole family (the family-level granularity of
json_arrow_operators / containment_operators):
PostgreSQL enables the set as a unit and every other dialect leaves it off. All eight
bind at the “any other operator” precedence (BindingPowerTable::any_operator),
left-associative — engine-measured on pg_query (tighter than comparison, looser than
additive). The operators fold onto the dedicated
BinaryOperator Json… keys.
Three lead bytes are shared triggers the tokenizer partitions by follow byte:
?is otherwise the anonymous placeholder (ParameterSyntax::anonymous_question); the two never co-enable in a shipped preset (PostgreSQL has no?parameter), and a feature set enabling both is the trackedLexicalConflict::JsonbKeyExistsVersusAnonymousParameter.@@is otherwise the MySQL system-variable sigil (SessionVariableSyntax::system_variables); the trackedLexicalConflict::JsonbSearchOperatorVersusSystemVariable.@?is disjoint from every other@claimant by its second byte, so it adds no conflict.#>/#>>/#-ride the#byte, which reaches the operator scanner only under PostgreSQL’s#bitwise-XOR (hash_bitwise_xor, on together with this in the PostgreSQL preset); they are munched ahead of the bare#and stay disjoint from DuckDB’s#npositional column (#+digit) by follow byte.
The sibling regex/geometric/network operator surface builds on the same ?/@/#
lexeme foundation under its own flag(s): bare prefix @ (absolute value) stays a
stray byte here (the @?/@@ munch requires a follow byte), left for that work.
double_equals: boolAccept SQLite’s == equality spelling as a synonym for =. Also gates the
tokenizer: the doubled-= lexeme is munched to the equality operator only
under a dialect that sets this, so elsewhere a == b stays a = = b
and surfaces as a clean parse error. The two spellings fold onto the one
canonical BinaryOperator::Eq operator; the
EqualsSpelling tag records
which the source used so ==/= round-trip exactly, the same pattern as
MOD/RLIKE/REGEXP.
integer_divide_slash: boolAccept DuckDB’s // integer-division spelling. Also gates the tokenizer: the
doubled-/ lexeme is munched to the integer-division operator only under a dialect
that sets this, so elsewhere a // b stays a / / b and surfaces as a clean
parse error. No shipped preset lexes // as a line comment, so the doubled munch
never shadows a comment mode. The symbol folds onto the one canonical
BinaryOperator::IntegerDivide operator; the IntegerDivideSpelling
tag records the // spelling so it round-trips (this is load-bearing for validity,
not only fidelity: DuckDB has no DIV keyword and MySQL no // operator, so the
spelling cannot be normalized away). Distinct from MySQL’s DIV, which rides
KeywordOperators::MySql.
starts_with_operator: boolAccept DuckDB’s ^@ “starts with” infix operator ('hello' ^@ 'he'). Also
gates the tokenizer: ^@ is munched only when this is on; elsewhere ^ then
@ stay separate (and @ is often a stray byte).
is_general_equality: boolAccept SQLite’s IS / IS NOT as a general null-safe equality over
arbitrary operands (1 IS 1, 1 IS NOT 2), not just IS NULL /
IS [NOT] DISTINCT FROM. When on, an IS [NOT] <expr> whose right operand is
neither NULL nor DISTINCT FROM … folds onto the existing null-safe
IsNotDistinctFrom /
IsDistinctFrom operators —
SQLite’s IS is exactly IS NOT DISTINCT FROM. When off (ANSI/PostgreSQL/
MySQL), IS requires NULL or DISTINCT FROM, so 1 IS 1 is a clean parse
error.
truth_value_tests: boolAccept the SQL:2016 truth-value tests <expr> IS [NOT] {TRUE | FALSE | UNKNOWN}
(F571), parsed to the postfix Expr::IsTruth predicate.
On for ANSI/PostgreSQL/MySQL/DuckDB/Lenient, all of which accept the three-valued
UNKNOWN (engine-measured on pg_query, MySQL 8, DuckDB). Off for SQLite, whose IS
is a general null-safe equality (is_general_equality):
there IS TRUE/IS FALSE fold onto the boolean literal and IS UNKNOWN reads as
equality against an identifier unknown, so SQLite has no truth-value predicate and
this stays off (turning it on would over-accept IS UNKNOWN, which SQLite rejects
unless unknown is a bound column). Checked ahead of the general-equality reading so
that a dialect enabling both keeps the standard truth predicate.
null_safe_equals: boolAccept MySQL’s <=> null-safe equality operator (a <=> b ≡
a IS NOT DISTINCT FROM b). Also gates the tokenizer: the <=> lexeme is munched
(ahead of <=) only under a dialect that sets this, so elsewhere a <=> b stays
a <= > b and surfaces as a clean parse error. It folds onto the canonical
BinaryOperator::IsNotDistinctFrom operator; the
IsNotDistinctFromSpelling tag records the
<=> spelling so it round-trips (MySQL rejects the keyword IS NOT DISTINCT FROM,
so the spelling cannot be normalized away). Binds at comparison precedence, riding
the shared comparison row like the other comparison operators.
lambda_expressions: boolRead an infix -> whose left operand is a lambda-parameter list — a bare
unqualified name, a parenthesized name list (x, y), or the equivalent
ROW(x, y) — as a DuckDB single-arrow lambda
(Expr::Lambda) instead of the JSON-arrow binary
operator; any other left operand keeps the
JsonGet reading.
A grammar-position gate over a token another flag lexes: the -> lexeme is
munched only under json_arrow_operators, so
this flag is inert without it (the dependency is
FeatureDependencyViolation::LambdaExpressionsWithoutJsonArrowOperators; no lexical
trigger of its own, hence no LexicalConflict entry — the lexical registry covers
shared tokenizer triggers, this one covers grammar dependencies). The node split mirrors the
engine exactly (probed on DuckDB 1.5.4): DuckDB parses every -> as a
LAMBDA tree node — even 1 -> 2 or t.a -> 'k' — and defers the
lambda-vs-JSON decision to bind time, where a lambda-consuming argument
requires exactly the parameter shape above (“Parameters must be unqualified
comma-separated names like x or (x, y)”) and any other -> is re-read as JSON
extraction. Applying that bind-time shape test at parse time is a pure
node-label choice: lambda -> and JSON -> share one token, one binding
power, and one associativity, so acceptance is unchanged either way and the
text round-trips identically. Position-independent, like the engine: a lambda
parses anywhere an expression does (SELECT x -> x + 1 parses in DuckDB; only
the binder rejects an unconsumed lambda). Multi-parameter lists ride the
implicit-row parse, so they additionally need
ExpressionSyntax::row_constructor (on in every preset that sets this). On
for DuckDB only.
bitwise_operators: boolAccept the shared bitwise operators — binary | (OR), & (AND), <</>>
(shift), and prefix ~ (complement) — over integers. On in PostgreSQL, MySQL,
SQLite, and DuckDB (the family is cross-dialect); off in ANSI. A parser-acceptance
gate over lexemes that always tokenize (|/&/~ are operator-class bytes and
<</>> are maximal-munched unconditionally), so when off the operator ends the
expression and the trailing operand surfaces as a clean parse error. Each operator’s
binding power lives in BindingPowerTable, where the ranks diverge per dialect
(MySQL splits | < & < <</>>; PostgreSQL/SQLite/DuckDB share one rank).
Bitwise XOR is a separate pair of knobs — FeatureSet::caret_operator for the
^ spelling and FeatureSet::hash_bitwise_xor for # — because XOR’s spelling,
lexing, and precedence all diverge (# vs ^).
quantified_comparisons: boolAccept the quantified subquery comparison <expr> <cmp> {ANY | ALL | SOME} (<subquery>) (SQL-92 F291), as in a = ANY (SELECT …) / a > ALL (SELECT …).
On for ANSI/PostgreSQL/MySQL/DuckDB/Lenient. SQLite has no quantified comparison
(it spells the same intent with IN/EXISTS), so it is off; the ANY/ALL/
SOME keyword is then not read as a quantifier and surfaces as a clean parse
error. Gates only the subquery-quantifier reading; ALL/DISTINCT as an
aggregate-argument quantifier is the separate always-parsed set-quantifier.
quantified_comparison_lists: boolAccept the list-operand form of a quantified comparison — <expr> <cmp> {ANY | ALL | SOME} (<value>) where the parenthesized operand is a scalar
list/array value rather than a subquery, as in DuckDB ax = ANY (b) /
x = ANY ([1, 2, 3]) and PostgreSQL x = ANY (ARRAY[…]). On for
PostgreSQL/DuckDB/Lenient (which model it as PostgreSQL’s ScalarArrayOpExpr,
parsed to Expr::QuantifiedList); off for
ANSI/MySQL, which admit only the subquery quantifier, and vacuously off for
SQLite, which has no quantified comparison at all. Rides on top of
quantified_comparisons: when that gate leaves
ANY/ALL/SOME unread this flag is unreachable, so it is only meaningfully
set alongside it (the dependency is
FeatureDependencyViolation::QuantifiedComparisonListsWithoutQuantifiedComparisons).
When off, a non-subquery operand surfaces as the same clean
“a subquery” parse error the standard form has always produced.
quantified_arbitrary_operator: boolExtend the quantifier {ANY | ALL | SOME} (…) past the six comparison
operators to any infix operator — <expr> <op> {ANY | ALL | SOME} (…)
where <op> is arithmetic (+ - * / % ^), string concatenation (||),
bitwise (& | # << >>), or a comparison. PostgreSQL’s grammar admits every
operator in MathOp/Op here, only the boolean keywords AND/OR are
excluded (engine-probed); the standard and the other dialects restrict the
quantifier to the comparison operators. On for PostgreSQL/Lenient; off for
ANSI/MySQL/DuckDB/SQLite. Rides on
quantified_comparisons (and
quantified_comparison_lists for the
array-operand form): when the quantifier is unread this flag is unreachable (the
dependency is FeatureDependencyViolation::QuantifiedArbitraryOperatorWithoutQuantifiedComparisons).
When off, a non-comparison operator before ANY/ALL/SOME folds as an
ordinary binary operator and the quantifier keyword then rejects as usual.
custom_operators: boolAccept the general symbolic-operator surface — ANY operator drawn from the Op
character class (~ ! @ # ^ & | ? + - * / % < > =), in both infix and prefix
position, over a user-extensible operator set. A dialect-neutral capability any preset
can enable; PostgreSQL (its pg_operator) is the current enabler, and the model
follows its grammar. This is the ONE model for the whole
tail (regex ~/!~/~*/!~*; geometric/network/text-search &&/&</&>/<->/
<<|/|>>/^@/##/<^/<%/@-@; negator spellings *<>/*>=; the prefix
@/@@/|//||//!!; and a fully user-defined @#@) rather than an enumerated
per-lexeme set: the grammar admits every one as the same Op/qual_Op
production, so a bare a ~ b is exactly a OPERATOR(~) b
(Expr::NamedOperator with the
Bare spelling), and a prefix @ x is
Expr::PrefixOperator. Known operators still
fold onto their dedicated BinaryOperator keys; only
the remainder becomes a named operator.
Also gates the tokenizer. Under this flag the operator scanner switches to
PostgreSQL’s maximal-munch lexer rule: a run of Op characters is one operator,
truncated at an embedded --//* comment start and with a trailing +/- stripped
unless the run holds one of ~ ! @ # ^ & | ? % (engine-measured: a +- b is
a + (- b) but a @- b is one @- operator; a <-- b is a < then a --
comment). The run that matches no built-in operator becomes an
Operator::Custom token (the parser crate’s tokenizer) carrying its span. Off leaves
the fixed-form lexer untouched, so every other dialect’s operator lexing is
unchanged. The bare operators bind at the “any other operator” rank
(BindingPowerTable::any_operator),
left-associative. On for PostgreSQL and DuckDB.
The Op character class is not identical across enablers: DuckDB drops # and ?
(its positional-column #1 and anonymous-parameter ? sigils), so its operator runs
stop at those bytes where PostgreSQL’s do not — the lexer’s is_operator_char reads
the ExpressionSyntax::positional_column / ParameterSyntax::anonymous_question
flags rather than hard-coding one charset. DuckDB postfix symbolic operators (1 !,
removed from PostgreSQL in 14) are a separate axis this flag does not carry.
The bare @ operator shares its lead byte with the T-SQL @name / MySQL
@var / @@sysvar sigils; the sigil arms win where a dialect enables them (the
tracked LexicalConflict::CustomOperatorVersusAtName /
LexicalConflict::CustomOperatorVersusSystemVariable), and no shipped preset
enables both, so under PostgreSQL a bare @ is always the operator.
null_test_postfix: boolAccept PostgreSQL/SQLite’s one-word postfix null-test synonyms <expr> ISNULL and
<expr> NOTNULL (for IS NULL / IS NOT NULL), folded onto
Expr::IsNull with a
NullTestSpelling::Postfix tag so they round-trip.
A pure grammar gate at comparison precedence (the same non-associative rank as IS NULL): when off, the trailing ISNULL/NOTNULL keyword is left unconsumed and
surfaces as a clean parse error (MySQL, which has no such synonym — ISNULL(x) is a
function there, unaffected by this postfix gate). On for
PostgreSQL/DuckDB/SQLite/Lenient.
postfix_operators: boolRead a trailing symbolic operator with no following operand as a postfix operator
application (Expr::PostfixOperator): 10!
(factorial), 1 ~, 1 <->, 1 &. DuckDB keeps the generalized postfix reading
PostgreSQL removed in version 14 (a_expr Op %prec POSTFIXOP), so it is the only
enabler; PostgreSQL 14+ and every other preset reject the trailing operator.
A pure parser-position gate, MECE against custom_operators:
that flag owns the tokenizer’s maximal-munch lexer and the infix/prefix general
operator surface, while this flag owns only the postfix reduction of an already-lexed
Op-class token. The infix reading still wins whenever an operand follows the operator
(1 ! + 2 is the infix 1 ! (+2)); the postfix reduction fires only in the
operand-absent position (1 ! < 2, 10!, 1 ! FROM t), engine-measured on DuckDB
1.5.4 via duckdb_extract_statements (parse-accept) — the unknown postfix operators
then bind-reject (Scalar Function !~__postfix does not exist), an under-acceptance our
parse-only parser closes by accepting the parse. The postfix binds at the “any other
operator” left rank (looser than the arithmetic operators: 2 * 3 ! is (2 * 3)!).
The postfix-eligible tokens are the general symbolic operators — the Custom residue,
the lone ~/!/&&, and the dedicated & | << >> || <@ @> ^@; the JSON arrows
->/->> are excluded (DuckDB rejects them postfix). On for DuckDB, and for the
permissive Lenient union (a pure additive parser position with no contended trigger,
though only the always-lexed Op tokens reach it there — custom_operators is off
under Lenient for the @-sigil conflict).
Implementations§
Source§impl OperatorSyntax
impl OperatorSyntax
Sourcepub const LENIENT: Self
Available on crate feature lenient only.
pub const LENIENT: Self
lenient only.LENIENT: the explicit-operator construct, both SQLite equality spellings, MySQL
<=>, the bitwise family, and quantified comparisons — all pure additions with no
contended trigger. The PostgreSQL @-family operators (<@/->/->>) stay off:
LENIENT enables the @name session-variable read
(SessionVariableSyntax::LENIENT), which claims the same @+identifier trigger
the prefix @ absolute-value operator would, so a permissive superset gaining the
@-family is a scoped follow-up.
Trait Implementations§
Source§impl Clone for OperatorSyntax
impl Clone for OperatorSyntax
Source§fn clone(&self) -> OperatorSyntax
fn clone(&self) -> OperatorSyntax
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more