Skip to main content

OperatorSyntax

Struct OperatorSyntax 

Source
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: bool

Accept the PostgreSQL explicit-operator infix form a OPERATOR(schema.op) b.

§containment_operators: bool

Accept 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: bool

Accept 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: bool

Accept 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:

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: bool

Accept 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: bool

Accept 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: bool

Accept 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: bool

Accept 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: bool

Accept 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: bool

Accept MySQL’s <=> null-safe equality operator (a <=> ba 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: bool

Read 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: bool

Accept 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: bool

Accept 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: bool

Accept 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: bool

Extend 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: bool

Accept 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: bool

Accept 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: bool

Read 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

Source

pub const ANSI: Self

The ANSI predefined value.

Source§

impl OperatorSyntax

Source

pub const DUCKDB: Self

Available on crate feature duckdb only.

The DUCKDB preset for operator syntax.

Source§

impl OperatorSyntax

Source

pub const LENIENT: Self

Available on crate feature 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.

Source§

impl OperatorSyntax

Source

pub const MYSQL: Self

Available on crate feature mysql only.

The MYSQL preset for operator syntax.

Source§

impl OperatorSyntax

Source

pub const POSTGRES: Self

Available on crate feature postgres only.

The POSTGRES preset for operator syntax.

Source§

impl OperatorSyntax

Source

pub const SQLITE: Self

Available on crate feature sqlite only.

The SQLITE preset for operator syntax.

Trait Implementations§

Source§

impl Clone for OperatorSyntax

Source§

fn clone(&self) -> OperatorSyntax

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for OperatorSyntax

Source§

impl Debug for OperatorSyntax

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for OperatorSyntax

Source§

impl PartialEq for OperatorSyntax

Source§

fn eq(&self, other: &OperatorSyntax) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for OperatorSyntax

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.