Skip to main content

QueryTailSyntax

Struct QueryTailSyntax 

Source
pub struct QueryTailSyntax {
Show 15 fields pub fetch_first: bool, pub limit_offset_comma: bool, pub locking_clauses: bool, pub key_lock_strengths: bool, pub stacked_locking_clauses: bool, pub using_sample: bool, pub leading_offset: bool, pub limit_expressions: bool, pub limit_percent: bool, pub with_ties_requires_order_by: bool, pub pipe_syntax: bool, pub limit_by_clause: bool, pub settings_clause: bool, pub format_clause: bool, pub for_xml_json_clause: bool,
}
Expand description

Dialect-owned query-tail syntax accepted by the parser.

The clauses parsed by the query-tail sequence that runs after the SELECT body. Split out of SelectSyntax at its 16-field line as the query-tail axis, distinct from the SELECT-core and grouping axes. Each flag is a grammar gate: when off the introducing keyword is left unconsumed and the clause surfaces as a clean parse error.

Fields§

§fetch_first: bool

Accept the standard OFFSET <n> { ROW | ROWS } and FETCH { FIRST | NEXT } <count> { ROW | ROWS } ONLY row-limiting spelling.

§limit_offset_comma: bool

Accept the MySQL/MariaDB/SQLite LIMIT <offset>, <count> two-argument comma form. It is a spelling of the same row limit as LIMIT <count> OFFSET <offset>, so it folds into the one canonical Limit shape tagged LimitSyntax::LimitOffset — never a new node. The comma binds the offset first, the count second (the reverse of LIMIT <count> OFFSET <offset>), which is exactly why it must be dialect data: reading the arguments in the wrong order silently swaps them.

§locking_clauses: bool

Accept a trailing row-locking clause (FOR UPDATE/FOR SHARE [OF …] [NOWAIT|SKIP LOCKED], plus MySQL’s legacy LOCK IN SHARE MODE), written after LIMIT. PostgreSQL and MySQL share this modern surface, so it folds into one canonical LockingClause on the query, gated here. When off (ANSI/SQLite/DuckDB, none of which admit a query-tail lock clause), the FOR/LOCK keyword is left unconsumed and surfaces as a clean parse error — the same reject mechanism the other query-tail gates use. On for PostgreSQL / MySQL / Lenient, off elsewhere. The gate covers the shared modern surface — a single FOR UPDATE/FOR SHARE clause with an OF <table>, … list and a wait tail. PostgreSQL’s NO KEY UPDATE/KEY SHARE strengths and its stacked clauses ride the further key_lock_strengths and stacked_locking_clauses gates; the clause before LIMIT is still deferred.

§key_lock_strengths: bool

Accept PostgreSQL’s FOR NO KEY UPDATE / FOR KEY SHARE row-locking strengths — the two levels between FOR UPDATE and FOR SHARE (LockStrength::NoKeyUpdate / LockStrength::KeyShare). Requires locking_clauses (it refines the strength keyword after FOR; the dependency is FeatureDependencyViolation::KeyLockStrengthsWithoutLockingClauses); when off, the NO/KEY after FOR is never a strength lead, so FOR NO KEY UPDATE / FOR KEY SHARE surface as clean parse errors — the over-acceptance guard MySQL relies on (its grammar has only UPDATE/SHARE, engine-verified). PostgreSQL alone spells the KEY/NO KEY refinements (libpg_query for_locking_strength), so this is on for PostgreSQL / Lenient, off elsewhere. FOR KEY UPDATE and FOR NO KEY SHARE stay rejected under both settings — PostgreSQL pairs NO KEY only with UPDATE and KEY only with SHARE (probed on libpg_query, pg-locking-clause-strengths-and-stacking).

§stacked_locking_clauses: bool

Accept several stacked row-locking clauses on one query (FOR UPDATE OF a FOR SHARE OF b) — PostgreSQL applies a distinct lock per table group, so Query::locking holds a list. Requires locking_clauses (it repeats the shared clause; the dependency is FeatureDependencyViolation::StackedLockingClausesWithoutLockingClauses); when off, exactly one clause is parsed and any following FOR/LOCK is left unconsumed, surfacing as a trailing-input parse error — the over-acceptance guard MySQL relies on (its grammar admits exactly one locking clause, engine-verified by mysql-select-tails-locking-hints-partition). On for PostgreSQL / Lenient, off elsewhere.

§using_sample: bool

Accept DuckDB’s USING SAMPLE <entry> query-level sample clause (Select::sample), written after QUALIFY and before the enclosing query’s ORDER BY (SELECT … USING SAMPLE 3 ORDER BY …). The entry is DuckDB’s tablesample_entry: a count-first <size> [ROWS|PERCENT|%] [ '(' method [',' seed] ')' ] or a method-first method '(' <size> ')' [REPEATABLE '(' seed ')']. On for DuckDB / Lenient, off elsewhere; when off the USING keyword in that position is left to fail as an unexpected statement token (matching the engines that lack the clause). Distinct from the table-factor TABLESAMPLE (TableExpressionSyntax::table_sample).

§leading_offset: bool

Accept a leading OFFSET <count> [LIMIT <count>] row-skip written without a preceding LIMIT (PostgreSQL’s [LIMIT …] [OFFSET …] where either may come first). On for ANSI/PostgreSQL/MySQL/DuckDB/Lenient. SQLite spells the skip only as LIMIT <count> OFFSET <count> / LIMIT <offset>, <count> — a bare OFFSET with no LIMIT is a syntax error there — so it is off; the OFFSET keyword is then left unconsumed and surfaces as a clean parse error. The OFFSET that trails a LIMIT is unaffected (parsed by the LIMIT branch).

§limit_expressions: bool

Accept an arbitrary expression as a LIMIT/OFFSET row count. On for ANSI/PostgreSQL/SQLite/DuckDB/Lenient, whose row limits admit a general expression (LIMIT 1 + 1, LIMIT (SELECT n FROM cfg)). MySQL restricts the count to an unsigned integer literal or a ? placeholder (its limit_clause grammar), so it is off there: an operand that parses to anything else is the syntax error MySQL reports (engine-measured-rejected on mysql:8 — LIMIT 1 + 1 / LIMIT (SELECT 1)). The whole operand is still parsed, then rejected on shape, so the diagnostic points at it. The plain LIMIT <int> / LIMIT ? / comma / OFFSET <int> forms are unaffected.

§limit_percent: bool

Accept DuckDB’s percentage LIMIT — a numeric-literal count directly followed by a % operator or the PERCENT keyword (LIMIT 40 PERCENT, LIMIT 35%), which returns that fraction of the result rows rather than a row number. On for DuckDB/Lenient only; off for ANSI/PostgreSQL/MySQL/SQLite, whose LIMIT has no percentage form. With the flag off the marker is left unconsumed — a % folds as modulo (needing a right operand) and a trailing PERCENT is leftover input — so both surface as the parse error those dialects report. DuckDB folds the marker only onto a bare numeric literal at a clause boundary: LIMIT 10 % 3 stays ordinary modulo, and LIMIT a PERCENT / LIMIT (1 + 1) PERCENT are DuckDB syntax errors (verified on 1.5.4), so the gate does not over-accept them.

§with_ties_requires_order_by: bool

Enforce PostgreSQL’s gram.y validity checks on FETCH FIRST … WITH TIES — the two semantic guards insertSelectOptions raises during raw parsing (so pg_query, a parse-only oracle, rejects them): WITH TIES requires a governing ORDER BY at the same query level (WITH TIES cannot be specified without ORDER BY clause), and WITH TIES cannot combine with a SKIP LOCKED locking clause (SKIP LOCKED and WITH TIES options cannot be used together). On for PostgreSQL only; other dialects with fetch_first keep accepting both forms unchanged. When off, neither guard fires. The name carries the load-bearing ORDER BY guard; the SKIP LOCKED combination guard is the same gram.y check site and rides along.

§pipe_syntax: bool

Accept BigQuery/ZetaSQL query pipe syntax: a trailing chain of |> operators that post-process a query result one step at a time (FROM t |> WHERE x |> SELECT a), modelled as Query::pipe_operators. This gate does double duty — it is read by the tokenizer to munch |> (pipe-arrow) as a single token (off, the two bytes stay | then >, so no dialect’s lexing shifts) and by the parser to admit the trailing |>-operator chain. When off (every shipped preset), a |> after a query is never a pipe separator and surfaces as a clean parse error, exactly as today.

Off for every shipped preset. This surface belongs to no engine we oracle against — the BigQuery preset that would home it deliberately defers it (a considered judgment; see that preset’s module docs) — so with no differential oracle to verify it, every shipped dialect (including DuckDB, which inherits the PostgreSQL value) leaves it off, and conformance sweeps see zero movement. LENIENT leaves it off for now as well: although the lenient charter admits any conflict-free pure-acceptance form (and |> is conflict-free — its munch is feature-gated, shadowing nothing), the framework ships only the reference WHERE operator, so enabling it in LENIENT today would make the “parse anything” preset accept |> WHERE while rejecting every other pipe operator — a fragment a reader of the lenient module could not predict, violating that module’s honesty bar. Once the pipe-operator surface is coherent (the planner-parity-pipe-* tickets land), LENIENT should flip this on as a pure-acceptance addition.

§limit_by_clause: bool

Accept ClickHouse LIMIT n [OFFSET m] BY expr, … — per-group row limiting, written after ORDER BY and before the ordinary LIMIT tail (both may appear in one query), modelled as Query::limit_by. The parser reads a leading LIMIT speculatively and treats it as LIMIT BY only when a BY follows the count and optional OFFSET; otherwise it rewinds and the token is the ordinary LIMIT, so a plain LIMIT n / LIMIT n OFFSET m parses identically whether this gate is on or off.

On for the ClickHouse preset and Lenient. ClickHouse has no differential oracle, so this is a no-oracle acceptance addition carried by the ClickHouse preset and the permissive union (the apply_join precedent): every oracle-compared dialect (ANSI/PostgreSQL/MySQL/SQLite/DuckDB) leaves it off — they parse-reject a BY after LIMIT — so conformance sweeps see zero movement.

§settings_clause: bool

Accept ClickHouse SETTINGS name = value, … — query-level setting overrides written after the ordinary LIMIT tail, modelled as Query::settings. SETTINGS is matched as a contextual keyword (it stays an ordinary identifier elsewhere); each pair is an identifier = value, the value a general expression.

On for the ClickHouse preset and Lenient. ClickHouse has no differential oracle, so this is a no-oracle acceptance addition carried by the ClickHouse preset and the permissive union (the limit_by_clause precedent): every oracle-compared dialect (ANSI/PostgreSQL/MySQL/SQLite/DuckDB) leaves it off — they parse-reject a trailing SETTINGS … as unconsumed input — so conformance sweeps see zero movement.

§format_clause: bool

Accept ClickHouse FORMAT <name> — the output-format clause that closes the query, modelled as Query::format. FORMAT is matched as a contextual keyword (it stays an ordinary identifier elsewhere); the format name is a bare, case-sensitive identifier (JSON, TabSeparated, Null), not a string literal.

On for the ClickHouse preset and Lenient. ClickHouse has no differential oracle, so this is a no-oracle acceptance addition carried by the ClickHouse preset and the permissive union (the settings_clause precedent): every oracle-compared dialect (ANSI/PostgreSQL/MySQL/SQLite/DuckDB) leaves it off — they parse-reject a trailing FORMAT … as unconsumed input — so conformance sweeps see zero movement.

§for_xml_json_clause: bool

Accept MSSQL’s FOR XML {RAW|AUTO|EXPLICIT|PATH} [, …] and FOR JSON {AUTO|PATH} [, …] result-shaping tails, which serialize the result set as XML/JSON rather than a rowset, modelled as Query::for_clause (see ForClause). The modes and their directives (ELEMENTS [XSINIL|ABSENT], BINARY BASE64, TYPE, ROOT ['name'], INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER) are typed data; the accepted grammar follows the MSSQL FOR XML / FOR JSON documentation (no MSSQL oracle, so that is the recorded acceptance bound).

Leading-keyword dispatch, follow-token partitioned against locking. FOR also introduces the row-locking clauses (locking_clauses). The two share the FOR lead but split on the follow token — XML/JSON here versus UPDATE/SHARE/NO/KEY for locking — so the dispatch is unambiguous under every preset combination and needs no GrammarConflict entry: the locking parser declines a FOR whose follow token is XML/JSON (so a stacked FOR UPDATE … FOR XML still reaches this clause), and this clause declines a FOR whose follow token is anything else.

On for MSSQL and Lenient. MSSQL has no differential oracle, so this is a no-oracle acceptance addition carried by the MSSQL preset and the permissive union: every oracle-compared dialect (ANSI/PostgreSQL/MySQL/SQLite/DuckDB) leaves it off — they parse-reject a trailing FOR XML/FOR JSON as unconsumed input (or, where locking_clauses is on, reject XML/JSON after FOR) — so conformance sweeps see zero movement.

Implementations§

Source§

impl QueryTailSyntax

Source

pub const ANSI: Self

The ANSI predefined value.

Source§

impl QueryTailSyntax

Source

pub const CLICKHOUSE: Self

Available on crate feature clickhouse only.

The CLICKHOUSE preset for query tail syntax.

Source§

impl QueryTailSyntax

Source

pub const DATABRICKS: Self

Available on crate feature databricks only.

The DATABRICKS preset for query tail syntax.

Source§

impl QueryTailSyntax

Source

pub const DUCKDB: Self

Available on crate feature duckdb only.

The DUCKDB preset for query tail syntax.

Source§

impl QueryTailSyntax

Source

pub const LENIENT: Self

Available on crate feature lenient only.

The LENIENT preset for query tail syntax.

Source§

impl QueryTailSyntax

Source

pub const MSSQL: Self

Available on crate feature mssql only.

MSSQL query-tail surface: the ANSI baseline plus the FOR XML/FOR JSON result-shaping tail. MSSQL has no query-tail row-locking clause (it spells isolation with WITH (…) table hints, TableExpressionSyntax::MSSQL), so locking_clauses stays off and the FOR lead is unambiguously the result-shaping clause here.

Source§

impl QueryTailSyntax

Source

pub const MYSQL: Self

Available on crate feature mysql only.

The MYSQL preset for query tail syntax.

Source§

impl QueryTailSyntax

Source

pub const POSTGRES: Self

Available on crate feature postgres only.

The POSTGRES preset for query tail syntax.

Source§

impl QueryTailSyntax

Source

pub const SNOWFLAKE: Self

Available on crate feature snowflake only.

The SNOWFLAKE preset for query tail syntax.

Source§

impl QueryTailSyntax

Source

pub const SQLITE: Self

Available on crate feature sqlite only.

The SQLITE preset for query tail syntax.

Trait Implementations§

Source§

impl Clone for QueryTailSyntax

Source§

fn clone(&self) -> QueryTailSyntax

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 QueryTailSyntax

Source§

impl Debug for QueryTailSyntax

Source§

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

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

impl Eq for QueryTailSyntax

Source§

impl PartialEq for QueryTailSyntax

Source§

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

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.