pub struct AggregateCallSyntax {
pub group_concat_separator: bool,
pub within_group: bool,
pub aggregate_filter: bool,
pub filter_optional_where: bool,
pub aggregate_args_require_adjacent_paren: bool,
pub null_treatment: bool,
pub aggregate_calls_reject_empty_arguments: bool,
pub over_requires_windowable_function: bool,
pub window_function_tail: bool,
pub standalone_argument_order_by: bool,
}Expand description
Dialect-owned aggregate/window function-call syntax accepted by the parser.
The call-grammar forms specific to aggregate and window function calls — the
in-parenthesis and post-) tails, the argument-shape and arity restrictions, and
the OVER-eligibility gate. Split out of CallSyntax at its 16-field line as
the aggregate/window axis, distinct from the scalar special-form and general
call-tail cores. Each flag is a grammar gate: when off the tail keyword is left
unconsumed and surfaces as a clean parse error, or the restriction does not fire.
Fields§
§group_concat_separator: boolAccept the MySQL GROUP_CONCAT(<args> [ORDER BY …] SEPARATOR <string>) delimiter
tail — the trailing SEPARATOR '<sep>' inside an aggregate call’s parentheses,
after any in-parenthesis ORDER BY. It rides the shared
FunctionCall shape’s new
separator field, gated by
grammar position like the always-parsed in-parenthesis ORDER BY. When off
(ANSI/PostgreSQL, which write the delimiter as an ordinary string_agg argument),
the SEPARATOR keyword is left unconsumed and the unmatched ) surfaces as a
clean parse error.
within_group: boolAccept the WITHIN GROUP (ORDER BY …) ordered-set-aggregate tail (SQL:2008
T612/T614), as in percentile_cont(0.5) WITHIN GROUP (ORDER BY x). On for
ANSI/PostgreSQL/DuckDB/Lenient. Neither SQLite nor MySQL has ordered-set
aggregates (both engine-measured-rejected), so it is off for both; the WITHIN
keyword is then left unconsumed and surfaces as a clean parse error. Only the
WITHIN GROUP pair opens the clause, so a bare within stays a usable alias
regardless.
aggregate_filter: boolAccept the FILTER (WHERE <predicate>) aggregate-filter tail (SQL:2003 T612), as
in sum(x) FILTER (WHERE x > 1). On for ANSI/PostgreSQL/SQLite (3.30+)/DuckDB/
Lenient. MySQL has no aggregate FILTER clause (engine-measured-rejected on
mysql:8), so it is off there; the FILTER keyword is then left unconsumed and
surfaces as a clean parse error. Only FILTER immediately followed by ( opens the
clause, so a bare filter after a call stays a usable alias.
filter_optional_where: boolAccept an aggregate FILTER (…) tail whose predicate is not preceded by the
SQL-standard WHERE keyword, as in DuckDB’s sum(x) FILTER (x > 1) (probed on
1.5.4). On for DuckDb/Lenient; the presence of the keyword round-trips via
FunctionCall::filter_where. Off for
ANSI/PostgreSQL/SQLite, which require FILTER (WHERE …) — a keyword-less body then
surfaces as a clean “expected WHERE” parse error. Independent of
aggregate_filter: this only widens the body of a
clause that gate already admits, so it is inert when the filter clause itself is off
(MySQL).
aggregate_args_require_adjacent_paren: boolRequire a built-in aggregate’s argument parentheses to be adjacent to its name
for the aggregate-only argument forms — a leading *, a DISTINCT/ALL quantifier,
the in-parenthesis ORDER BY, or the SEPARATOR tail. On for MySQL, off everywhere
else. MySQL’s default (IGNORE_SPACE off) tokenizer treats a space before the (
as demoting a built-in aggregate to an ordinary/stored-function reference, where that
aggregate-only argument grammar is illegal: COUNT ( * ) / MAX ( ALL 1 ) /
COUNT ( DISTINCT 1 ) are engine-measured ER_PARSE_ERROR (1064) on mysql:8, while
the adjacent COUNT(*) accepts. A normal-argument spaced call is unaffected —
count (1) still parses (it fails only at name resolution, a binding not a syntax
error), so this narrowly rejects the aggregate-only forms rather than blanket-forbidding
a space (which would over-reject the valid general-call form). When off, the name/paren
gap is irrelevant and every dialect admits the aggregate forms with or without the space.
Only the default IGNORE_SPACE-off mode is modelled; the runtime sql_mode toggle to
IGNORE_SPACE on (which would accept the spaced aggregate forms) is out of scope.
null_treatment: boolAccept the IGNORE NULLS / RESPECT NULLS null-treatment written inside a
window/aggregate call’s parentheses (DuckDB’s last(s IGNORE NULLS) OVER (…)),
riding the shared FunctionCall’s
null_treatment field. On for
DuckDb/Lenient. DuckDB spells it inside the parentheses (the SQL:2016 post-)
position engine-rejects on 1.5.4), so it is parsed at the in-parenthesis tail after
any ORDER BY. When off, IGNORE/RESPECT is left unconsumed and the unmatched )
surfaces as a clean parse error. PostgreSQL has no null-treatment clause, so this
stays a DuckDB extension rather than a shared PG form.
aggregate_calls_reject_empty_arguments: boolReject an empty argument list f() on a MySQL built-in aggregate function —
the closed set MYSQL_AGGREGATE_FUNCTIONS in the parser crate (COUNT, SUM,
AVG, MIN, MAX, the BIT_*/STD*/VAR*/VARIANCE family, GROUP_CONCAT,
JSON_ARRAYAGG/JSON_OBJECTAGG). MySQL’s dedicated aggregate grammar requires at
least one argument (or the COUNT(*) wildcard), so COUNT() is ER_PARSE_ERROR
(1064) on mysql:8, while a niladic non-aggregate built-in (NOW(), UUID(),
PI()) and an empty user-function call are accepted (the latter fails only at
name resolution — a binding, not a syntax error — and a CONCAT()/ABS() empty
call is a wrong-parameter-count semantic reject, also not a syntax error). On for
MySQL, off elsewhere. When off, an empty aggregate call parses as an ordinary empty
function call (every non-MySQL dialect admits it). Only a single unquoted name in
the set matches — a backtick-quoted `count`() is a general call MySQL rejects
at binding, not a syntax error, so it stays accepted — and the COUNT(*) wildcard
and any argumented/quantified call are unaffected.
over_requires_windowable_function: boolRestrict the OVER (…) window clause to MySQL’s windowable functions — the
built-in aggregates (MYSQL_AGGREGATE_FUNCTIONS) ∪ the dedicated window functions
(MYSQL_WINDOW_FUNCTIONS: ROW_NUMBER, RANK, DENSE_RANK, PERCENT_RANK,
CUME_DIST, NTILE, LEAD, LAG, FIRST_VALUE, LAST_VALUE, NTH_VALUE), both
in the parser crate. MySQL grammatically admits OVER only on this set:
OVER on an ordinary scalar built-in or a user function (ABS(x) OVER (),
PERCENTILE_CONT(x, 0.5) OVER (), ANY_VALUE(x) OVER ()) is ER_PARSE_ERROR
(1064) on mysql:8, while SUM(x) OVER () / ROW_NUMBER() OVER () /
GROUP_CONCAT(x) OVER () parse (they fail only at binding or a not-supported-yet
semantic reject). On for MySQL, off elsewhere.
The vocabulary must stay complete: an omission would over-reject a valid
windowed call (the worse failure — no coverage-gap pin catches an over-rejection),
so it is the engine-verified full MySQL 8.0 aggregate + window function list. A
qualified name (db.f(x) OVER (), engine-rejected too) is not a single-part member
and is likewise rejected. When off, OVER attaches to any call, matching every
non-MySQL dialect.
This flag also gates the converse half of MySQL’s dedicated window-function
grammar — the requirements the pure window functions (MYSQL_WINDOW_FUNCTIONS,
admitted as call heads by carving them out of MYSQL_RESERVED_FUNCTION_NAME) carry
once admitted. Unlike the aggregates (whose OVER is optional), each window
function requires an OVER clause, takes a fixed positional argument arity
(ROW_NUMBER/RANK/DENSE_RANK/PERCENT_RANK/CUME_DIST exactly 0, NTILE/
FIRST_VALUE/LAST_VALUE exactly 1, LEAD/LAG 1–3, NTH_VALUE exactly 2), and
rejects the aggregate-only argument forms (*, a DISTINCT/ALL quantifier, an
in-parenthesis ORDER BY, a SEPARATOR) — each violation an ER_PARSE_ERROR
(1064) on mysql:8 (ROW_NUMBER() without OVER, ROW_NUMBER(1) OVER (),
NTILE() OVER (), RANK(DISTINCT a) OVER ()). These are one indivisible dialect
grammar, so they share the flag rather than a second knob that would have to
co-vary with it. The parser enforces them in parse_function_call.
window_function_tail: boolAccept MySQL’s window-function post-) tail — the SQL:2016
[FROM {FIRST | LAST}] [{RESPECT | IGNORE} NULLS] clauses written between a
null-treatment window function’s argument ) and its OVER clause, riding the
shared FunctionCall’s
window_tail field. On for MySQL, off
everywhere else. Engine-verified on mysql:8, the accepted surface is narrow: the
null treatment is admitted only on LEAD/LAG/FIRST_VALUE/LAST_VALUE/
NTH_VALUE and only as RESPECT NULLS (IGNORE NULLS grammar-admits but
feature-rejects, ER_NOT_SUPPORTED_YET 1235); FROM {FIRST | LAST} is admitted
only on NTH_VALUE and only as FROM FIRST (FROM LAST likewise 1235); the two
clauses appear in that fixed order (the reverse is ER_PARSE_ERROR, 1064), and
both sit strictly after the ) (the in-paren spelling MySQL rejects, unlike
DuckDB’s null_treatment). Keyed on a single
unquoted window-function name — a quoted `nth_value` or qualified
db.nth_value takes the general-call path (rejected there), matching the engine.
When off, the tail keywords are left unconsumed and the trailing text surfaces as
a clean parse error. The parser enforces the per-function admission in
parse_window_function_tail.
standalone_argument_order_by: boolAccept a bare in-parenthesis ORDER BY as the sole content of a call’s argument
list — no positional argument preceding it (rank(ORDER BY x) OVER w,
cume_dist(ORDER BY x DESC) OVER w). DuckDB lets a window/rank function carry its
ordering inside the call parentheses instead of the OVER (ORDER BY …) clause; the
order_by list is the same field the
arguments-then-ORDER BY form (array_agg(x ORDER BY y)) already fills, so the
call shape is unchanged — only the empty positional list is new. On for
DuckDb/Lenient, off elsewhere: standard SQL / PostgreSQL / MySQL require at least
one argument before an aggregate ORDER BY (engine-verified), so when off the
leading ORDER keyword falls into the argument-expression grammar where the
reserved word surfaces as a clean parse error.
A parse-level gate only. The per-function validity of the standalone form is a
binding concern DuckDB enforces after parsing — sum/array_agg/string_agg and
a bare rank(ORDER BY x) with no OVER are DuckDB binder/catalog rejects (the
standalone form parses), so a parse-only parser correctly accepts them here; the one
exception is DuckDB’s parser-level “ORDER BY is not supported for the window function
dense_rank”, a fine-grained per-function restriction this gate deliberately does not
model (see duckdb-order-by-in-agg-args-trailing-comma).
Implementations§
Trait Implementations§
Source§impl Clone for AggregateCallSyntax
impl Clone for AggregateCallSyntax
Source§fn clone(&self) -> AggregateCallSyntax
fn clone(&self) -> AggregateCallSyntax
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more