Skip to main content

JoinSyntax

Struct JoinSyntax 

Source
pub struct JoinSyntax {
    pub stacked_join_qualifiers: bool,
    pub full_outer_join: bool,
    pub natural_cross_join: bool,
    pub straight_join: bool,
    pub asof_join: bool,
    pub positional_join: bool,
    pub semi_anti_join: bool,
    pub sided_semi_anti_join: bool,
    pub apply_join: bool,
    pub recursive_search_cycle: bool,
    pub recursive_union_rejects_order_limit: bool,
    pub recursive_using_key: bool,
}
Expand description

Dialect-owned join-operator and recursive-query relation-composition syntax accepted by the parser.

The join-family flags — the join operators and their side/qualifier variants beyond the always-available inner/left/right/cross joins — together with the recursive-query structural clauses that compose relations. Split out of TableExpressionSyntax at its 16-field line as the relation-composition axis, distinct from the table-factor and factor-tail/alias cores. Each flag is a grammar gate: when off the keyword is left to the identifier grammar or surfaces as a clean parse error.

Fields§

§stacked_join_qualifiers: bool

Accept stacked join qualifiers — the PostgreSQL right-nesting where two joins precede their ON/USING constraints and the nearest constraint closes the innermost join: a JOIN b JOIN c ON p ON q reads as a JOIN (b JOIN c ON p) ON q (PostgreSQL table_ref: … | joined_table right-recursion). On for ANSI/PostgreSQL/ MySQL/DuckDB/Lenient. SQLite’s join-clause is flat — each join-operator takes exactly one immediately-following constraint — so it is off; the right operand is not extended past the first constraint, and a second stacked ON/USING (… USING (id) USING (id)) is then left unconsumed and surfaces as the syntax error SQLite reports (engine-measured via rusqlite). The common a JOIN b ON x JOIN c ON y — each constraint right after its own join — needs no nesting and is unaffected either way.

§full_outer_join: bool

Accept the FULL [OUTER] JOIN bilateral outer join. On for ANSI/PostgreSQL/SQLite/ DuckDB/Lenient. MySQL has no FULL join (it offers only LEFT/RIGHT outer joins), so it is off there: the FULL join-side keyword is not consumed, and an already-aliased factor followed by FULL [OUTER] JOIN (a x FULL OUTER JOIN b) is then a clean parse error — exactly what MySQL reports (engine-measured-rejected on mysql:8). Because FULL is a non-reserved word in MySQL, a bare a full JOIN b still reads full as the factor’s alias (grabbed before the join loop), matching the engine — this flag only governs the join-side reading, never the alias.

§natural_cross_join: bool

Accept NATURAL CROSS JOINNATURAL prefixing the CROSS join keyword. SQLite’s join-operator validates its up-to-three keywords post-hoc and admits NATURAL before any join type, CROSS included; PostgreSQL and DuckDB both parse-reject it (engine-probed). Because CROSS JOIN is the optimizer-hint spelling of INNER JOIN and NATURAL supplies the shared-column constraint, NATURAL CROSS JOIN is semantically a natural inner join — engine-probed on rusqlite: it yields the shared-column equijoin’s row/column shape, not the cross product — so the parser normalizes it into the canonical JoinOperator::Inner + JoinConstraint::Natural shape (the NATURAL INNER precedent, where the redundant side keyword is likewise dropped), rendering back as NATURAL JOIN. No new AST field is needed: the round-trip oracle compares structure, not spelling, so the elided CROSS word re-parses to the same AST. On for SQLite / Lenient, off elsewhere; when off, NATURAL CROSS fails the NATURAL arm’s mandatory JOIN and rejects unchanged.

§straight_join: bool

Accept MySQL’s STRAIGHT_JOIN join-order hint, in both of its surfaces: the join operator a STRAIGHT_JOIN b [ON ...] and the SELECT STRAIGHT_JOIN ... modifier. One flag gates both grammar points because they are a single dialect unit — MySQL always admits the modifier wherever it admits the join keyword (mirroring how CreateTableClauseSyntax::table_options gates the trailing options and the AUTO_INCREMENT attribute together). Both fold into the canonical inner-join shape / Select flag with a surface tag, never a new node. When off, STRAIGHT_JOIN is left to the identifier grammar (a non-reserved word under ANSI/PostgreSQL), so the MySQL construct is a clean parse divergence.

§asof_join: bool

Accept DuckDB’s ASOF [INNER|LEFT|RIGHT|FULL [OUTER]] JOIN inexact-match temporal join (JoinOperator::AsOf). When off, ASOF is left to the identifier grammar (a non-reserved word under ANSI/PostgreSQL) — and because the next word is JOIN, the text still parses there, as an aliased plain join (a different-tree reading, unlike STRAIGHT_JOIN’s leftover-input reject). The flag alone is not enough for the DuckDB meaning on a bare table factor — the word must also be in FeatureSet::reserved_column_name or the factor’s alias swallows it first (the DuckDb preset reserves it; LENIENT keeps the ANSI reserved model, so there the join parses only after an explicit alias). On for DuckDb / Lenient, off elsewhere.

§positional_join: bool

Accept DuckDB’s POSITIONAL JOIN row-position pairing join (JoinOperator::Positional), which takes no ON/USING constraint and no side keyword. The same reserved-word interaction as asof_join applies. On for DuckDb / Lenient, off elsewhere.

§semi_anti_join: bool

Accept DuckDB’s SEMI JOIN / ANTI JOIN semi-/anti-join operators (JoinOperator::Semi/Anti), including their NATURAL and ASOF compositions (NATURAL SEMI JOIN, ASOF ANTI JOIN). One flag gates both because SEMI and ANTI are the same grammar production (a join_type keyword taking an ON/USING constraint) that DuckDB only ever ships together — the paired-flag doctrine (the straight_join precedent), unlike the distinct-grammar asof_join/positional_join pair. The same reserved-word interaction as asof_join applies: the DuckDb preset reserves both words as a ColId/bare-alias so a bare l SEMI JOIN r reads the join rather than aliasing l; LENIENT keeps the ANSI reserved model, so there the join parses only after an explicit alias. On for DuckDb / Lenient, off elsewhere.

§sided_semi_anti_join: bool

Accept the Spark/Hive/Databricks sided semi-/anti-join spelling ({LEFT|RIGHT} {SEMI|ANTI} JOIN), recorded as the SemiAntiSide axis on the same JoinOperator::Semi/Anti operators as DuckDB’s side-less form. A separate gate from semi_anti_join because it is a different engine family: DuckDB parse-rejects LEFT SEMI JOIN (engine-probed), so folding the sided spelling into semi_anti_join would over-accept it under the DuckDb preset. The leading LEFT/RIGHT keyword is already a reserved join side, so — unlike the keyword-led asof_join pair — no reserved-word interplay is needed: the preceding factor’s alias can never swallow it, and a plain LEFT [OUTER] JOIN is disambiguated by the following SEMI/ANTI keyword. One flag gates the whole sided family (both sides × SEMI/ANTI) as one grammar production (the straight_join/semi_anti_join paired-flag doctrine). The sided form always takes an ON/USING constraint and never composes with NATURAL/ASOF. On for the Databricks and Hive presets (whose engine family documents the spelling — Hive originated LEFT SEMI JOIN) and for Lenient (the permissive superset); off elsewhere, where the other engines parse-reject the sided spelling. The atomic flag admits the RIGHT-sided and ANTI spellings those presets do not all document — a known conservative-direction over-acceptance a future side-refinement would tighten.

§apply_join: bool

Accept MSSQL’s CROSS APPLY / OUTER APPLY join operators (JoinOperator::Apply) — a lateral-correlated join over a right table factor (derived table or table-valued function), taking no ON/USING constraint. One flag gates the whole APPLY family (both the CROSS and OUTER flavours) because they are a single grammar production differing only by that keyword (the straight_join paired-flag doctrine; the ApplyKind axis carries the flavour). No reserved-word interplay is needed — the leading CROSS/OUTER keyword already anchors the operator, so the preceding factor’s alias can never swallow it (unlike the keyword-led asof_join pair). On for the MSSQL preset and Lenient (the permissive superset), off elsewhere: the other engines parse-reject APPLY in join position.

§recursive_search_cycle: bool

Accept the SQL:2023 recursive-query SEARCH { DEPTH | BREADTH } FIRST BY … SET … and CYCLE … SET … [TO … DEFAULT …] USING … clauses on a CTE (Cte::search/cycle), written after the CTE body’s ). One flag gates both clauses because PostgreSQL ships them as a single recursive-query unit (the straight_join paired-flag doctrine) — no dialect admits one without the other. When off, the SEARCH/CYCLE keyword after a CTE body is left unconsumed and the statement is a clean parse error. On for PostgreSQL / Lenient; off for ANSI (the conservative baseline, like unnest), MySQL/SQLite (no such clauses), and DuckDB — which parse-rejects both (syntax error at or near "SEARCH", probed on 1.5.4), so it overrides the PostgreSQL surface it otherwise inherits (the MutationSyntax::data_modifying_ctes split precedent).

§recursive_union_rejects_order_limit: bool

Parse-reject a top-level ORDER BY / LIMIT / OFFSET on a recursive CTE whose body is a UNION [ALL] set operation (WITH RECURSIVE t AS (SELECT … UNION ALL SELECT … FROM t ORDER BY …)). DuckDB special-cases the recursive term’s grammar: once WITH RECURSIVE fronts a UNION-bodied CTE the query is a recursive query, and a result-shaping modifier on it is a parse error (Parser Error: ORDER BY in a recursive query is not allowed / LIMIT or OFFSET in a recursive query is not allowed; probed on 1.5.4). “order_limit” names the whole modifier set — ORDER BY, LIMIT, and OFFSET — because DuckDB forbids them under one rule (no dialect admits one but not the others), so it is one behaviour, not three.

Three boundaries are load-bearing and engine-probed (1.5.4), so the check mirrors them exactly rather than firing on the bare RECURSIVE keyword: the body must be a UNION set operation (a non-set-op recursive CTE, or an INTERSECT/EXCEPT body, keeps its modifiers — those are not recursive-eligible); the modifier must sit on the set operation itself, not a parenthesized arm or a nested subquery (((SELECT … LIMIT 1) UNION ALL …) and … WHERE x < (SELECT … ORDER BY 1) both accept); and self-reference is not required (DuckDB rejects even a UNION whose right arm never names the CTE — the check is syntactic). On for DuckDB only; every other preset parse-accepts the modifier (PostgreSQL admits it outright, the rest defer the restriction to binding), so it stays off there and the modifier rides the ordinary query tail.

§recursive_using_key: bool

Accept DuckDB’s USING KEY (col, ...) recursive-CTE key clause (Cte::using_key), written between the CTE column list and AS (WITH RECURSIVE t(a, b) USING KEY (a) AS (…)). DuckDB’s keyed-recursion variant (stable since 1.3; probed accepting on 1.5.4): the recurring table becomes a key-indexed dictionary whose rows the recursive term overwrites in place. A distinct gate from recursive_search_cycle — a different engine (DuckDB, not PostgreSQL) and a different grammar slot (before AS, not after the body’s )), so neither implies the other. Positioned ahead of AS, the leading USING cannot be swallowed by any prior production (a CTE otherwise expects AS next), so enabling it shadows no existing spelling. On for DuckDB / Lenient (the permissive superset); off elsewhere, where the clause is a parse error.

Implementations§

Source§

impl JoinSyntax

Source

pub const ANSI: Self

The ANSI predefined value.

Source§

impl JoinSyntax

Source

pub const BIGQUERY: Self

Available on crate feature bigquery only.

The BIGQUERY preset for join syntax.

Source§

impl JoinSyntax

Source

pub const DATABRICKS: Self

Available on crate feature databricks only.

The DATABRICKS preset for join syntax.

Source§

impl JoinSyntax

Source

pub const DUCKDB: Self

Available on crate feature duckdb only.

The DUCKDB preset for join syntax.

Source§

impl JoinSyntax

Source

pub const HIVE: Self

Available on crate feature hive only.

The HIVE preset for join syntax.

Source§

impl JoinSyntax

Source

pub const LENIENT: Self

Available on crate feature lenient only.

The LENIENT preset for join syntax.

Source§

impl JoinSyntax

Source

pub const MSSQL: Self

Available on crate feature mssql only.

The MSSQL preset for join syntax.

Source§

impl JoinSyntax

Source

pub const MYSQL: Self

Available on crate feature mysql only.

The MYSQL preset for join syntax.

Source§

impl JoinSyntax

Source

pub const POSTGRES: Self

Available on crate feature postgres only.

The POSTGRES preset for join syntax.

Source§

impl JoinSyntax

Source

pub const SQLITE: Self

Available on crate feature sqlite only.

The SQLITE preset for join syntax.

Trait Implementations§

Source§

impl Clone for JoinSyntax

Source§

fn clone(&self) -> JoinSyntax

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 JoinSyntax

Source§

impl Debug for JoinSyntax

Source§

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

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

impl Eq for JoinSyntax

Source§

impl PartialEq for JoinSyntax

Source§

fn eq(&self, other: &JoinSyntax) -> 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 JoinSyntax

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.