pub struct SelectSyntax {Show 18 fields
pub distinct_on: bool,
pub select_into: bool,
pub empty_target_list: bool,
pub qualify: bool,
pub alias_string_literals: bool,
pub bare_alias_string_literals: bool,
pub from_first: bool,
pub union_by_name: bool,
pub wildcard_modifiers: bool,
pub qualified_wildcard_alias: bool,
pub parenthesized_query_operands: bool,
pub values_rows_require_equal_arity: bool,
pub values_row_constructor: bool,
pub as_alias_rejects_reserved: bool,
pub trailing_comma: bool,
pub prefix_colon_alias: bool,
pub lateral_view_clause: bool,
pub connect_by_clause: bool,
}Expand description
Dialect-owned SELECT-core syntax extensions accepted by the parser.
The SELECT-body forms — the projection / select-list, the set-operation-operand, and
the VALUES-constructor surface — after the row-limiting/locking query tail and the
grouping/ordering clauses split out into QueryTailSyntax and GroupingSyntax at
this struct’s 16-field line. Acceptance is explicit dialect data, not a parser-side type
check: most flags are widening grammar gates (when off, the introducing keyword is left
unconsumed and the clause surfaces as a clean parse error), and a few are narrowing
well-formedness enforcements (per the flag-naming rule above).
Fields§
§distinct_on: boolAccept PostgreSQL SELECT DISTINCT ON (<expr>, ...).
select_into: boolAccept PostgreSQL’s SELECT … INTO [TEMP] <table> create-table form (the
INTO target sits between the projection and FROM, materializing the result
into a new relation). When off, INTO is left unconsumed and surfaces as a
clean parse error. This gates only the create-table form: bare standard
SELECT … INTO <variable> is PSM host/local-variable assignment — a different
construct — so ANSI leaves this off, and MySQL has no SELECT INTO <table>.
empty_target_list: boolAccept an empty SELECT target list — a projection with zero items (SELECT,
SELECT;, SELECT FROM t, SELECT WHERE …). libpg_query’s raw grammar makes
the projection optional before any clause (PostgreSQL rejects it later, at
parse-analysis, which is past our parse-level parity contract), so the honest
closure accepts it under the PostgreSQL preset only. When off (ANSI/MySQL, which
both require ≥1 select item), the projection’s first-item requirement stands and
a bare SELECT is a clean parse error.
qualify: boolAccept DuckDB’s QUALIFY <predicate> post-window filter clause, written after
the WINDOW clause (DuckDB’s grammar order: … HAVING … WINDOW … QUALIFY …;
verified against DuckDB 1.5.4). When off (ANSI/PostgreSQL/MySQL/SQLite, none of
which have the clause), the QUALIFY keyword is left unconsumed and surfaces
as a clean parse error — the same reject mechanism the other SELECT gates use.
This flag gates only the clause; whether QUALIFY is usable as a plain
identifier is the orthogonal reservation data (the reserved_* keyword sets:
DuckDB reserves it like HAVING, every other shipped dialect leaves it a free
identifier). On for DuckDB / Lenient, off elsewhere.
alias_string_literals: boolAccept a string literal as a column alias (SELECT 1 AS 'x', and MySQL’s
SELECT 1 AS "x" where "…" is a string), MySQL’s rule. The alias parser admits
a String token after AS, materialises its value as the alias identifier, and
records the source quote (QuoteStyle::Single /
Double) so it renders back quoted. When off,
a string in alias position is left unconsumed and surfaces as a clean parse error
(the standard requires an identifier). On for MySQL / Lenient, off elsewhere.
bare_alias_string_literals: boolAccept a string literal as a bare (AS-less) column alias (SELECT 1 'x'), on
top of the AS-introduced form alias_string_literals
gates. SQLite and MySQL read a string in bare-alias position as the column name
(engine-measured: SELECT 1 'x' prepares on both, naming the column x), while
DuckDB accepts only the AS 'x' form and rejects the bare SELECT 1 'x' (probed
on 1.5.4) — so the bare position is its own axis rather than a rider on
alias_string_literals. A separate axis rather than
widening that flag because the two enablers split: DuckDB arms the AS form here but
not the bare one. When off, a string in bare-alias position is left unconsumed and
surfaces as a clean parse error. On for SQLite / MySQL / Lenient, off elsewhere.
MySQL’s bare string alias overlaps same-line adjacent-string concatenation
(same_line_adjacent_concat):
SELECT 'a' 'b' is the single value 'ab', while SELECT 1 'x' is a bare alias
(both engine-measured on mysql:8.4.10). No carve-out flag is needed — parse ordering
resolves it: a string primary greedily folds every following unprefixed string
continuation into its own value before the alias parser runs, so a trailing string
reaches the bare-alias branch only when the preceding expression was not itself a
string. A prefixed string (N'…', _charset'…', bit) is never a bare alias
(rejected as an identifier), matching the engine’s rejects (SELECT 'a' _utf8'b').
from_first: boolAccept DuckDB’s FROM-first SELECT order: a query primary may lead with the
FROM clause (FROM <tables> [SELECT [DISTINCT] <projection>] …), the projection
written after it, or omitted entirely — the bare FROM <tables> is an implicit
SELECT *. Semantically the ordinary SELECT (DuckDB serializes FROM t SELECT x
and SELECT x FROM t to the same tree; probed on 1.5.4), so it parses to the
canonical Select tagged
SelectSpelling::FromFirst. The projection, when
present, must sit immediately after the FROM clause (DuckDB syntax-errors on
FROM t WHERE x SELECT y / FROM t GROUP BY a SELECT a), so it is parsed only in
that position and every following clause parses in its ordinary place. When off
(ANSI/PostgreSQL/MySQL/SQLite, none of which admit a statement-position FROM), a
leading FROM is never a query start, so it surfaces as a clean parse error — the
over-acceptance guard the differential oracle relies on. The gate is read wherever
a query primary may begin (statement, set operand, scalar/IN subquery, CTE body,
derived table), so the one flag composes everywhere. On for DuckDB / Lenient, off
elsewhere.
union_by_name: boolAccept DuckDB’s UNION [ALL] BY NAME name-matched set operation: pair the two
inputs’ columns by name (padding missing columns with NULL) instead of by
position (SetExpr::SetOperation::by_name — a flag on
the set-op node, orthogonal to all). DuckDB restricts BY NAME to UNION
(INTERSECT BY NAME / EXCEPT BY NAME are syntax errors; probed on 1.5.4), so
the parser consumes the modifier only after UNION; after INTERSECT/EXCEPT
the BY keyword is left unconsumed and surfaces as the usual operand reject.
When off (ANSI/PostgreSQL/MySQL/SQLite, none of which have the modifier), the
BY after a set operator is likewise left unconsumed and a bare UNION BY NAME
is a clean parse error — the same reject mechanism the other SELECT gates use.
On for DuckDB / Lenient, off elsewhere.
wildcard_modifiers: boolAccept DuckDB’s * / t.* wildcard modifiers — the EXCLUDE (…),
REPLACE (expr AS col), and RENAME (col AS new) tail that rewrites which
columns the wildcard expands to (WildcardOptions
on the wildcard select item). DuckDB fixes their surface order (EXCLUDE, then
REPLACE, then RENAME, each at most once; any other order is a syntax error,
probed on 1.5.4), which the parser enforces by parsing them in that sequence.
One flag, not three: DuckDB ships the trio as a single grammar production and no
shipped dialect adopts a subset (the paired-flag doctrine). This gates the
select-list/RETURNING wildcard tail; the sibling COLUMNS(…) expression form
is CallSyntax::columns_expression,
a separate grammar position (expression vs projection item). When off
(ANSI/PostgreSQL/MySQL/SQLite), the EXCLUDE/REPLACE/RENAME keyword after a
* is left unconsumed and surfaces as a clean parse error — the over-acceptance
guard the differential oracle relies on. On for DuckDB / Lenient, off elsewhere.
qualified_wildcard_alias: boolAccept a trailing [AS] alias on a qualified wildcard select item (SELECT t.* x,
SELECT t.* AS x, SELECT s.t.* x), folding it onto the
QualifiedWildcard item’s alias slot
with the source AliasSpelling (bare vs AS).
PostgreSQL treats t.* as an ordinary column-reference expression (columnref, an
a_expr), so it flows through the very same target_el: a_expr [AS] label projection
alias an ordinary value takes: the bare form admits the BareColLabel reserved-word
set (SELECT t.* select parses, SELECT t.* from/order reject) and the AS form the
full ColLabel set (SELECT t.* AS from parses) — measured against libpg_query, which
matches the parser’s ordinary projection-alias boundary exactly, so the alias is parsed
by reusing it. The bare * wildcard is the separate non-aliasable target_el: '*' production, so this gate never touches it (a bare-* alias is DuckDB’s rename-all,
which rides wildcard_modifiers).
Deliberately its own axis, not folded into wildcard_modifiers:
the two behaviours have different measured boundaries — the EXCLUDE/REPLACE/RENAME
modifier tail and the bare-* rename-all alias are DuckDB-only, whereas a qualified
wildcard’s plain alias is accepted by PostgreSQL and DuckDB (engine-probed: PG’s
libpg_query and DuckDB 1.5.4 accept t.* x/t.* AS x, while SQLite/MySQL and the SQL
standard special-case t.* as a non-aliasable wildcard production and reject it —
measured Reject on rusqlite/mysql:8 with the table provisioned). One behaviour = one flag.
When off (ANSI/MySQL/SQLite and the ANSI-derived presets), the alias word is left
unconsumed after t.* and surfaces as a clean parse error. On for PostgreSQL / DuckDB /
Lenient, off elsewhere.
parenthesized_query_operands: boolAccept a parenthesized query as a set-operation / statement / CTE-body / CTAS /
INSERT-source operand — (SELECT …) UNION (SELECT …), ((SELECT …)) LIMIT 1,
CREATE TABLE t AS (SELECT …) (PostgreSQL select_with_parens; the canonical
set-op AST). On for ANSI/PostgreSQL/MySQL/DuckDB/Lenient. SQLite has no
parenthesized compound operand — a select-core is SELECT …/VALUES …, never
( … ) — so it is off; a leading ( in operand position is then a syntax error
(engine-measured via rusqlite). The parenthesized query that SQLite does admit —
a FROM table-or-subquery grouping (FROM ((SELECT 1))) and an expression-position
scalar subquery (SELECT ((SELECT 1))) — is a complete standalone primary, not an
operand, and stays accepted with the flag off (the parser threads a grouping
context through those two positions); a paren-query there that is extended by a
set operator or an ORDER BY/LIMIT tail (FROM ((SELECT 1) UNION (SELECT 2)),
FROM ((SELECT 1) LIMIT 1)) is the syntax error SQLite reports.
values_rows_require_equal_arity: boolReject a VALUES table-value constructor whose rows differ in width
(VALUES (1, 2), (3)) at parse time. Unlike the additive gates above, this is a
well-formedness enforcement: on rejects the ragged constructor, off accepts it.
Equal row degree is a universal SQL rule, but engines check it at different phases,
and this validator models per-dialect parse acceptance. DuckDB checks at parse —
Parser Error: VALUES lists must all be the same length, in every VALUES position
(standalone, derived table, INSERT; measured on 1.5.4) — so the DuckDb preset
turns it on. PostgreSQL’s raw grammar (libpg_query) and MySQL accept a ragged
constructor and defer the arity check to parse-analysis / bind, past our parse-level
parity contract, so it stays off there (their presets keep accepting it, exactly as
empty_target_list accepts a bare SELECT under the PostgreSQL preset). LENIENT
is a pure-acceptance superset, so it also leaves this off. Both counts the check
compares — the two rows’ arities — are present in the parse tree, so this is a
shape-level gate, not a semantic one. On for DuckDB only; off elsewhere.
values_row_constructor: boolAccept a bare-parenthesized row (VALUES (1), (2)) as a VALUES table-value
constructor in query position — a top-level query body, a set-operation operand,
a CTE body, or a derived table. On for ANSI/PostgreSQL/SQLite/DuckDB/Lenient. MySQL
spells the query-position constructor VALUES ROW(1), ROW(2) (the TABLE/VALUES
row-constructor grammar): a bare (…) row there is the syntax error MySQL reports
(engine-measured on mysql:8 — VALUES (1) / SELECT … FROM (VALUES (1)) … /
VALUES (1) UNION … all ER_PARSE_ERROR), so it is off there. Scoped to query
position only: the INSERT … VALUES (…) source list is a distinct grammar that
admits bare rows on every dialect (MySQL included), so it is parsed on a separate
path this gate does not touch.
as_alias_rejects_reserved: boolReject a reserved word as an AS-introduced projection alias
(SELECT 1 AS <label>), routing the position to the stricter
reserved_bare_alias set instead of the
permissive reserved_as_label. Off for
ANSI/PostgreSQL/SQLite/DuckDB/Lenient, whose projection AS alias is a
PostgreSQL-style ColLabel: PostgreSQL admits every keyword there
(SELECT a AS select parses) via its empty reserved_as_label, and SQLite draws
no ColId/ColLabel split so its non-empty reserved_as_label already rejects
them — neither needs this gate. MySQL has no ColLabel relaxation: an AS alias
rejects exactly the words a bare alias does (SELECT 1 AS range/AS left/AS delete are ER_PARSE_ERROR on mysql:8, while the non-reserved SELECT 1 AS any
parses), so it is on there. Scoped to the projection AS alias only: the
dotted-name continuation (t.range, a qualified-name label MySQL admits) is a
separate position that keeps the permissive reserved_as_label set.
trailing_comma: boolAccept a single trailing comma before a list’s closing delimiter in the list
positions DuckDB tolerates it: the SELECT projection list (SELECT a, b, FROM t), the query-position and INSERT VALUES row lists and each parenthesized
row (VALUES (1), (2), / VALUES (1, 2,)), the […] / ARRAY[…] list and
{…} struct and MAP {…} collection literals, the IN (…) list, the GROUP BY
key list and its ROLLUP(…) / CUBE(…) / GROUPING SETS (…) sub-lists
(GROUP BY a, b, / GROUP BY ROLLUP(a, b,)), the wildcard-modifier lists
(* EXCLUDE (a,), * REPLACE (e AS c,), * RENAME (a AS b,)), the
COALESCE special-form argument list (coalesce(1, 2,)), and the
CREATE TABLE table-element list (CREATE TABLE t (a INT, b INT,)), after a
column or a constraint element alike. The comma is
discarded — the list shape is unchanged, so no AST node carries it and the render
drops it (canonical form, a lossy-spelling trade); it is not semantically
meaningful.
Scoped to those list sites only, because DuckDB is not uniform: engine-probed
(1.5.4), an ordinary function-argument list (greatest(1, 2,)), a bare
parenthesized / row constructor ((1, 2,)), an ORDER BY / PARTITION BY list,
and an INSERT column list (INSERT INTO t (a, b,) …) all reject the trailing
comma, so this gate is applied per accepting list site rather than in the shared
parse_comma_separated. COALESCE is the lone function-call exception —
coalesce(1, 2,) accepts because DuckDB parses it as a grammar special form, not
a func_application, whereas every sibling (greatest/least/nullif/concat)
keeps rejecting the comma. Only a single trailing comma is admitted ([1, 2, ,]
and a leading [,] stay parse errors). When off (ANSI/PostgreSQL/MySQL/SQLite,
none of which admit a trailing comma), the dangling comma is left for the item
parser to reject — the same clean parse error those dialects report. On for DuckDB
/ Lenient, off elsewhere.
prefix_colon_alias: boolAccept DuckDB’s prefix colon alias — an alias written before its value as
<alias> : <value>, in two positions: a projection item (SELECT j : 42, the
alias j on the value 42) and a FROM table factor (FROM b : a, the alias
b on the relation a — the alias precedes the table). It is pure sugar for the
standard trailing AS alias: DuckDB records it in the ordinary alias field with
no spelling tag and canonically re-emits it as AS (json round-trip on 1.5.4:
SELECT j : 42 → SELECT 42 AS j, FROM b : a → FROM a AS b), so it folds
onto the existing SelectItem::Expr /
TableFactor alias field and renders as AS — never a
new node. The <alias> is a single bare (BareColLabel) or quoted identifier: a
qualified a.b :, a call f() :, and a reserved word are all rejected, and the
prefix is mutually exclusive with a trailing alias (SELECT x : 42 AS y /
FROM b : a c reject — the value’s own alias parse is suppressed once a prefix is
read, so a trailing alias is left unconsumed and rejects).
This is a grammar-position gate, not a lexical trigger: a lone : always
tokenizes as a Colon punctuation token, and reading it as an alias separator at a
select-item / table-factor head is a parser decision (like slice a[x:y] and struct
{a: b} are parser readings of the same byte), so no LexicalConflict variant
governs it. The one genuine hazard is a parser-position collision with
ExpressionSyntax::semi_structured_access — a top-level base : key path would
read the same <ident> : head — so the two must not be enabled together
(GrammarConflict::PrefixColonAliasVersusSemiStructuredAccess). On for DuckDB and
Lenient, both of which leave semi_structured_access off, so no shipped preset has the
collision; a future dialect enabling both would silently mis-read one — the hazard the
tokenizer-trigger-only lexical registry cannot catch, now tracked by the grammar
registry.
lateral_view_clause: boolAccept the Hive/Spark LATERAL VIEW [OUTER] <generator>(args) <alias> [AS <col> [, …]] table-generating clauses, written after the whole FROM
clause and before WHERE and repeatable (Hive LanguageManual LateralView:
fromClause: FROM baseTable (lateralView)*; Spark SqlBaseParser.g4 places
lateralView* at the same fromClause tail), modelled as
Select::lateral_views (see
LateralView for the typed shape, the sqlparser-rs
parity reshapings, and the recorded acceptance bound — there is no Hive/Spark
oracle, so the two engines’ published grammars are the acceptance evidence).
Leading-keyword dispatch, position- and follow-token-partitioned against
LATERAL derived tables. LATERAL also introduces the standard derived-table /
function factor (TableFactorSyntax::lateral). The two occupy disjoint
grammar positions — a table-factor head (after FROM/,/a join keyword) versus
after the complete FROM relation list — and split on the follow token (VIEW
here; ( or a function/subquery head there), so the dispatch is unambiguous
under every preset combination and needs no GrammarConflict entry: this
clause claims a LATERAL only when VIEW follows and only once the FROM list
is complete, a cursor position the factor grammar never holds.
On for Hive, Databricks, and Lenient. Hive and Databricks have no
differential oracle, so this is a no-oracle acceptance addition carried by the
two conservative presets and the permissive union: every oracle-compared dialect
(ANSI/PostgreSQL/MySQL/SQLite/DuckDB) leaves it off — they parse-reject a
post-FROM LATERAL as unconsumed input — so conformance sweeps see zero
movement.
connect_by_clause: boolAccept the Oracle-style [START WITH <cond>] CONNECT BY [NOCYCLE] <cond>
hierarchical query clause, written after WHERE and before GROUP BY with
START WITH and CONNECT BY in either order, modelled as
Select::connect_by (see
HierarchicalClause for the typed shape, the
either-order fidelity tag, and the recorded acceptance bounds). The clause also
enables the UnaryOperator::Prior operator, but only
inside the CONNECT BY condition — the global expression grammar is unchanged, so
a bare prior stays an ordinary column name.
Grammar evidence (no Oracle/Snowflake oracle). Snowflake’s public docs are the
citable grammar (there is no Oracle preset): they document
START WITH … CONNECT BY [PRIOR] col = [PRIOR] col. Oracle contributes the
either-order rule, the after-WHERE position, and NOCYCLE (which Snowflake’s
docs explicitly omit) — modelling the Oracle superset is a documented
conservative-direction over-acceptance, captured on the owning ticket.
On for Snowflake and Lenient. Snowflake has no differential oracle, so this is
a no-oracle acceptance addition carried by the Snowflake preset and the permissive
union. Databricks/Spark does not get it: Databricks documents recursive CTEs
(WITH RECURSIVE) instead of CONNECT BY and does not support the Oracle
CONNECT BY … START WITH syntax (Databricks SQL reference), so it stays off there
alongside every oracle-compared dialect (ANSI/PostgreSQL/MySQL/SQLite/DuckDB),
which parse-reject a post-WHERE CONNECT BY/START WITH as unconsumed input —
so conformance sweeps see zero movement.
Implementations§
Source§impl SelectSyntax
impl SelectSyntax
Source§impl SelectSyntax
impl SelectSyntax
Sourcepub const CLICKHOUSE: Self
Available on crate feature clickhouse only.
pub const CLICKHOUSE: Self
clickhouse only.ClickHouse SELECT surface: the ANSI baseline plus the three ClickHouse query
tails, each a contextual clause at the query boundary that shadows no existing
reading (a plain LIMIT n still parses; SETTINGS/FORMAT are contextual, so
they divert only with the gate on). Every other SELECT knob is conservatively
ANSI: ClickHouse’s further SELECT extensions (e.g. WITH FILL, ARRAY JOIN,
SAMPLE) have no modelled gate and are deferred to focused tickets.
Source§impl SelectSyntax
impl SelectSyntax
Sourcepub const DATABRICKS: Self
Available on crate feature databricks only.
pub const DATABRICKS: Self
databricks only.Databricks SELECT surface: the ANSI baseline plus the documented Databricks
clauses — the QUALIFY <predicate> post-window filter, the GROUP BY ALL clause
mode, the ORDER BY ALL clause mode, and the Spark-inherited LATERAL VIEW
generator clause. Unlike Snowflake, Databricks ships ORDER BY ALL alongside
GROUP BY ALL, so both are on. Every other SELECT knob is conservatively ANSI.
Source§impl SelectSyntax
impl SelectSyntax
Source§impl SelectSyntax
impl SelectSyntax
Sourcepub const HIVE: Self
Available on crate feature hive only.
pub const HIVE: Self
hive only.Hive SELECT surface: the ANSI baseline plus the LATERAL VIEW table-generating
clause Hive originated (LanguageManual LateralView). The derived-table LATERAL
factor (TableFactorSyntax::HIVE) stays off, so LATERAL leads only this
clause under the preset. Every other SELECT knob is conservatively ANSI.
Source§impl SelectSyntax
impl SelectSyntax
Source§impl SelectSyntax
impl SelectSyntax
Source§impl SelectSyntax
impl SelectSyntax
Source§impl SelectSyntax
impl SelectSyntax
Sourcepub const SNOWFLAKE: Self
Available on crate feature snowflake only.
pub const SNOWFLAKE: Self
snowflake only.Snowflake SELECT surface: the ANSI baseline plus the documented Snowflake
clauses — the QUALIFY <predicate> post-window filter, the GROUP BY ALL clause
mode, and the Oracle-style START WITH/CONNECT BY hierarchical query clause.
ORDER BY ALL is deliberately not enabled: Snowflake ships GROUP BY ALL
without ORDER BY ALL (see order_by_all).
Every other SELECT knob is conservatively ANSI.
Source§impl SelectSyntax
impl SelectSyntax
Trait Implementations§
Source§impl Clone for SelectSyntax
impl Clone for SelectSyntax
Source§fn clone(&self) -> SelectSyntax
fn clone(&self) -> SelectSyntax
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more