squonk_ast/dialect/ansi.rs
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Moderately AI Inc.
3
4//! The always-compiled ANSI/standard dialect preset.
5//!
6//! Other presets derive from this data without making the shared
7//! [`FeatureSet`]/[`KeywordSet`]/`ByteClasses` machinery dialect-specific.
8
9use super::keyword::{
10 POSTGRES_AS_LABEL_KEYWORDS, POSTGRES_COL_NAME_KEYWORDS, POSTGRES_RESERVED_KEYWORDS,
11 POSTGRES_TYPE_FUNC_NAME_KEYWORDS,
12};
13use super::{
14 AccessControlSyntax, AggregateCallSyntax, CallSyntax, CaretOperator, Casing,
15 ColumnDefinitionSyntax, CommentSyntax, ConstraintSyntax, CreateTableClauseSyntax,
16 DoubleAmpersand, ExistenceGuards, ExpressionSyntax, FeatureSet, GroupingSyntax,
17 IdentifierQuote, IdentifierSyntax, IndexAlterSyntax, JoinSyntax, Keyword, KeywordOperators,
18 KeywordSet, MaintenanceSyntax, MutationSyntax, NullOrdering, NumericLiteralSyntax,
19 OperatorSyntax, ParameterSyntax, PipeOperator, PredicateSyntax, QueryTailSyntax,
20 STANDARD_BYTE_CLASSES, SelectSyntax, SessionVariableSyntax, ShowSyntax, StatementDdlGates,
21 StringFuncForms, StringLiteralSyntax, TableExpressionSyntax, TableFactorSyntax, TargetSpelling,
22 TransactionSyntax, TypeNameSyntax, UtilitySyntax, ViewSequenceClauseSyntax,
23};
24use crate::precedence::{STANDARD_BINDING_POWERS, STANDARD_SET_OPERATION_BINDING_POWERS};
25
26/// Standard `"`-only identifier quoting shared by the ANSI and PostgreSQL presets.
27pub const STANDARD_IDENTIFIER_QUOTES: &[IdentifierQuote] = &[IdentifierQuote::Symmetric('"')];
28
29// --- Per-position reject sets (prod-keyword-position-reserved-sets) -----------
30//
31// PostgreSQL reserves keywords per grammatical position via the four `kwlist.h`
32// classes plus the `BARE_LABEL`/`AS_LABEL` axis; these compose the generated
33// category bitsets into the reject set each parser gate consults. They are sourced
34// from the PostgreSQL category model — the practical, libpg-query-validated one —
35// for *both* the ANSI/generic and PostgreSQL presets: the strict SQL:2016 reserved
36// list reserves `COUNT`/`COALESCE`/… as identifiers, which would reject the common
37// `count(*)` / `coalesce(a, b)` SQL every working dialect accepts, so the generic
38// dialect adopts PostgreSQL's position-aware categories (the differential oracle
39// confirms the agreement). A dialect wanting strict ANSI reservation overrides
40// these fields via a `FeatureDelta`. They live in the always-compiled ANSI module
41// because PostgreSQL reuses them verbatim, so they are baseline data, not a
42// PostgreSQL-gated cost.
43//
44// Source of truth (the `dialect-ref` citation convention — see
45// docs/dialect-references/manifest.toml): `postgres/kwlist` @
46// REL_18_BETA1-3053-g4b0bf0788b0, the four `kwlist.h` categories vendored at
47// docs/dialect-references/corpora/postgres/kwlist.h (pin matches
48// conformance/corpus/postgres). Cite the manifest id + pin, not a bare URL that
49// rots; a version bump there is a deliberate commit that re-cites here.
50
51/// `ColId` reject set (column/table name, FROM/correlation alias, qualifier): a
52/// keyword usable as a bare ColId is `unreserved ∪ col_name`, so this rejects
53/// `type_func_name ∪ reserved` (e.g. `JOIN`/`LEFT` cannot be a table alias).
54pub const RESERVED_COLUMN_NAME: KeywordSet =
55 POSTGRES_TYPE_FUNC_NAME_KEYWORDS.union(POSTGRES_RESERVED_KEYWORDS);
56
57/// Keywords PostgreSQL never admits as a generic `func_application` name, on top
58/// of the reserved set.
59///
60/// PostgreSQL's generic call name is `type_function_name` (`unreserved ∪
61/// type_func_name`); the `col_name` keywords are admitted as functions *only*
62/// through dedicated productions. Those productions split two ways:
63///
64/// - Some take an ordinary argument list (`coalesce(a, b)`, `greatest`, `least`,
65/// `xmlconcat`, `grouping`, `json`, `substring`, `overlay`, `trim`,
66/// `normalize`, the `json_object`/`json_array`/`json_scalar`/`json_serialize`
67/// builders, `xmlforest`), so parsing them as ordinary calls happens to agree
68/// with PostgreSQL — we keep admitting those.
69/// - The rest have non-generic argument syntax (`position(x IN y)`,
70/// `xmlelement(...)`, the `json_query`/`json_value`/… builders, `treat(x AS
71/// ty)`), are the bare type spellings used only in a type position (`int`,
72/// `bit`, `numeric`, `interval`, …), or are keywords with no call form at all
73/// (`values`, `between`, `setof`, `inout`/`out`, `merge_action`, `operator`,
74/// `none`). PostgreSQL rejects `kw(1)` for every one of these; admitting them
75/// as ordinary calls is exactly the divergence the keyword-position oracle
76/// pinned, so they are reserved here. `nullif` is *not* listed: its dedicated
77/// `NULLIF(a, b)` production is enforced by a parser arity check instead, so
78/// the valid two-argument form keeps parsing.
79pub const POSTGRES_NON_GENERIC_FUNCTION_KEYWORDS: KeywordSet = KeywordSet::from_keywords(&[
80 Keyword::Between,
81 Keyword::Bigint,
82 Keyword::Bit,
83 Keyword::Boolean,
84 Keyword::Char,
85 Keyword::Character,
86 Keyword::Dec,
87 Keyword::Decimal,
88 Keyword::Float,
89 Keyword::Inout,
90 Keyword::Int,
91 Keyword::Integer,
92 Keyword::Interval,
93 Keyword::JsonExists,
94 Keyword::JsonObjectagg,
95 Keyword::JsonQuery,
96 Keyword::JsonTable,
97 Keyword::JsonValue,
98 Keyword::MergeAction,
99 Keyword::National,
100 Keyword::Nchar,
101 Keyword::None,
102 Keyword::Numeric,
103 Keyword::Operator,
104 Keyword::Out,
105 Keyword::Position,
106 Keyword::Precision,
107 Keyword::Real,
108 Keyword::Setof,
109 Keyword::Smallint,
110 Keyword::Time,
111 Keyword::Timestamp,
112 Keyword::Treat,
113 Keyword::Values,
114 Keyword::Varchar,
115 Keyword::Xmlattributes,
116 Keyword::Xmlelement,
117 Keyword::Xmlexists,
118 Keyword::Xmlnamespaces,
119 Keyword::Xmlparse,
120 Keyword::Xmlpi,
121 Keyword::Xmlroot,
122 Keyword::Xmlserialize,
123 Keyword::Xmltable,
124]);
125
126/// Function-name reject set: `reserved ∪` the `col_name`/special keywords that
127/// have no generic call form (see `POSTGRES_NON_GENERIC_FUNCTION_KEYWORDS`).
128/// PostgreSQL's generic `func_application` admits only `type_function_name`; the
129/// `col_name` functions with an ordinary argument list (`coalesce`, …) still parse
130/// because they are *not* in the reject set, while the bare type spellings and
131/// non-generic-syntax builders are rejected here to match PostgreSQL.
132pub const RESERVED_FUNCTION_NAME: KeywordSet =
133 POSTGRES_RESERVED_KEYWORDS.union(POSTGRES_NON_GENERIC_FUNCTION_KEYWORDS);
134
135/// Fully reserved words rejected by PostgreSQL's `var_value` production. This is
136/// deliberately narrower than [`RESERVED_FUNCTION_NAME`], which also contains keywords
137/// whose dedicated function syntax prevents them from being generic call names.
138pub const RESERVED_SET_VALUE_WORDS: KeywordSet = POSTGRES_RESERVED_KEYWORDS;
139
140/// Type-name reject set (`type_function_name`): a keyword usable as a type name is
141/// `unreserved ∪ type_func_name`, so this rejects `col_name ∪ reserved` — matching
142/// PostgreSQL, which rejects `CAST(x AS coalesce)` (`coalesce` is `col_name`).
143/// Built-in type spellings are matched contextually before any gate, so this only
144/// governs user-defined type names.
145pub const RESERVED_TYPE_NAME: KeywordSet =
146 POSTGRES_COL_NAME_KEYWORDS.union(POSTGRES_RESERVED_KEYWORDS);
147
148/// Bare-label (`BareColLabel`) reject set: the `AS_LABEL` keywords, which cannot be
149/// a column alias without `AS`. This is the axis behind `SELECT a over` /
150/// `SELECT a filter` rejecting while `SELECT a select` accepts.
151pub const RESERVED_BARE_ALIAS: KeywordSet = POSTGRES_AS_LABEL_KEYWORDS;
152
153impl CommentSyntax {
154 /// The `ANSI` predefined value.
155 pub const ANSI: Self = Self {
156 line_comment_hash: false,
157 // `\n`-only line-comment termination is the strict SQL-standard baseline and the
158 // pre-existing behaviour of every preset; PostgreSQL/DuckDB widen it to `\r` too
159 // (`CommentSyntax::POSTGRES`), SQLite/MySQL keep it off.
160 line_comment_ends_at_carriage_return: false,
161 nested_block_comments: true,
162 versioned_comments: None,
163 // The strict baseline: an unterminated `/* …` running to EOF is a hard error
164 // everywhere but SQLite (`CommentSyntax::SQLITE`).
165 unterminated_block_comment_at_eof: false,
166 };
167}
168
169impl StringLiteralSyntax {
170 /// The `ANSI` predefined value.
171 pub const ANSI: Self = Self {
172 escape_strings: false,
173 dollar_quoted_strings: false,
174 national_strings: false,
175 double_quoted_strings: false,
176 backslash_escapes: false,
177 unicode_strings: false,
178 bit_string_literals: false,
179 blob_literals: false,
180 charset_introducers: false,
181 // The standard requires a newline in the separator between adjacent literals.
182 same_line_adjacent_concat: false,
183 };
184}
185
186impl NumericLiteralSyntax {
187 /// The `ANSI` predefined value.
188 pub const ANSI: Self = Self {
189 hex_integers: false,
190 octal_integers: false,
191 binary_integers: false,
192 underscore_separators: false,
193 radix_leading_underscore: false,
194 money_literals: false,
195 reject_trailing_junk: false,
196 };
197}
198
199impl ParameterSyntax {
200 /// The `ANSI` predefined value.
201 pub const ANSI: Self = Self {
202 positional_dollar: false,
203 positional_dollar_large: false,
204 anonymous_question: false,
205 named_colon: false,
206 named_at: false,
207 named_dollar: false,
208 numbered_question: false,
209 };
210}
211
212impl SessionVariableSyntax {
213 /// The `ANSI` predefined value.
214 pub const ANSI: Self = Self {
215 user_variables: false,
216 system_variables: false,
217 variable_assignment: false,
218 };
219}
220
221impl IdentifierSyntax {
222 /// The `ANSI` predefined value.
223 pub const ANSI: Self = Self {
224 non_ascii: super::NonAsciiIdentifierSyntax::UnicodeAlphanumeric,
225 dollar_in_identifiers: false,
226 string_literal_identifiers: false,
227 string_literal_table_names: false,
228 empty_quoted_identifiers: false,
229 };
230}
231
232impl TableExpressionSyntax {
233 /// The `ANSI` predefined value.
234 pub const ANSI: Self = Self {
235 only: false,
236 table_sample: false,
237 parenthesized_joins: true,
238 table_alias_column_lists: true,
239 join_using_alias: false,
240 // MySQL-only table-factor tails.
241 index_hints: false,
242 // MSSQL-only `WITH (...)` table hints.
243 table_hints: false,
244 partition_selection: false,
245 base_table_alias_column_lists: true,
246 // DuckDB-only string-literal table alias (`FROM t AS 't'('k')`).
247 string_literal_aliases: false,
248 aliased_parenthesized_join: true,
249 // The standard bare table alias is a `ColId`, same as the table name (SQLite is the
250 // outlier whose bare alias is the narrower `ids` class that reserves JOIN keywords).
251 bare_table_alias_is_bare_label: false,
252 // Version / time-travel modifiers are BigQuery/MSSQL/Databricks extensions.
253 table_version: false,
254 // PartiQL / SUPER table-position JSON paths are Redshift/Snowflake extensions.
255 table_json_path: false,
256 // SQLite's `INDEXED BY` / `NOT INDEXED` index directive is a SQLite extension.
257 indexed_by: false,
258 prefix_colon_alias: false,
259 };
260}
261
262impl JoinSyntax {
263 /// The `ANSI` predefined value.
264 pub const ANSI: Self = Self {
265 // Standard SQL right-nests stacked join qualifiers (`a JOIN b JOIN c ON p ON q`).
266 stacked_join_qualifiers: true,
267 full_outer_join: true,
268 // SQLite-only `NATURAL CROSS JOIN` (PostgreSQL/DuckDB parse-reject it).
269 natural_cross_join: false,
270 straight_join: false,
271 // DuckDB-only nonstandard joins.
272 asof_join: false,
273 positional_join: false,
274 semi_anti_join: false,
275 sided_semi_anti_join: false,
276 apply_join: false,
277 // The SQL:2023 recursive-query SEARCH/CYCLE clauses stay off in this conservative
278 // ANSI-ish baseline (like `unnest`); PostgreSQL/Lenient enable them.
279 recursive_search_cycle: false,
280 // DuckDB-only parse restriction on a `UNION`-bodied recursive CTE's ORDER BY/LIMIT.
281 recursive_union_rejects_order_limit: false,
282 // DuckDB-only `USING KEY` recursive-CTE key clause; off in this baseline.
283 recursive_using_key: false,
284 };
285}
286
287impl TableFactorSyntax {
288 /// The `ANSI` predefined value.
289 pub const ANSI: Self = Self {
290 lateral: false,
291 table_functions: false,
292 rows_from: false,
293 // UNNEST is SQL-standard, but this ANSI-ish baseline keeps the table-function
294 // surface off (like `table_functions`), so `FROM unnest(…)` is a clean reject.
295 unnest: false,
296 unnest_with_offset: false,
297 table_function_ordinality: false,
298 // The standard admits a special value function as a `FROM` source and an alias on a
299 // parenthesized join (MySQL is the outlier that rejects both).
300 special_function_table_source: true,
301 // DuckDB-only PIVOT/UNPIVOT operators.
302 pivot: false,
303 unpivot: false,
304 // DuckDB-only DESCRIBE/SHOW/SUMMARIZE table source.
305 show_ref: false,
306 // DuckDB-only bare `FROM VALUES (…) AS t` row-list table factor.
307 from_values: false,
308 // JSON_TABLE / XMLTABLE table factors are PostgreSQL/Lenient-only; off here.
309 json_table: false,
310 xml_table: false,
311 // `TABLE(<expr>)` is a Lenient-only factor (no oracle-backed preset ships it);
312 // off in this conservative baseline.
313 table_expr_factor: false,
314 // The standard PIVOT's extended value sources / `DEFAULT ON NULL` are a
315 // BigQuery/Snowflake/Lenient form; off in this conservative baseline.
316 pivot_value_sources: false,
317 // MATCH_RECOGNIZE is a Snowflake/Oracle form; off in this conservative baseline.
318 match_recognize: false,
319 // OPENJSON is a SQL Server form; off in this conservative baseline.
320 open_json: false,
321 };
322}
323
324impl MutationSyntax {
325 /// The `ANSI` predefined value.
326 pub const ANSI: Self = Self {
327 insert_ignore: false,
328 insert_overwrite: false,
329 returning: false,
330 on_conflict: false,
331 on_duplicate_key_update: false,
332 multi_column_assignment: false,
333 update_tuple_value_row_arity: false,
334 where_current_of: false,
335 merge: true,
336 // The MySQL `REPLACE` statement and `INSERT ... SET` source are dialect
337 // extensions, not standard surface, so the ANSI baseline rejects both.
338 replace_into: false,
339 insert_set: false,
340 // The MySQL `UPDATE`/`DELETE ... ORDER BY ... LIMIT` tails are dialect
341 // extensions; standard SQL row-limits neither statement.
342 update_delete_tails: false,
343 joined_update_delete: false,
344 // The SQLite `INSERT OR`/`UPDATE OR <action>` conflict prefix is a SQLite
345 // extension; standard SQL has no such verb-level conflict resolution.
346 or_conflict_action: false,
347 insert_column_matching: false,
348 delete_using: true,
349 update_from: true,
350 // The standard admits an alias on a `DELETE … USING` target and a leading `WITH`
351 // before `INSERT` (MySQL rejects both).
352 delete_using_target_alias: true,
353 cte_before_insert: true,
354 // SQL:2016's `<merge statement>` takes no `<with clause>` (unlike `INSERT`,
355 // whose source query carries one), so a leading `WITH` before `MERGE` is a
356 // PostgreSQL/DuckDB extension the ANSI baseline rejects.
357 cte_before_merge: false,
358 // The standard's `<with clause>` bodies are query expressions only; the
359 // data-modifying CTE is a PostgreSQL extension.
360 data_modifying_ctes: false,
361 // SQL:2016's `<merge when clause>` is only `MATCHED`/`NOT MATCHED` and its
362 // `<merge insert specification>` has no `DEFAULT VALUES` alternative, so both
363 // are PostgreSQL/DuckDB extensions the ANSI baseline rejects.
364 merge_when_not_matched_by: false,
365 merge_insert_default_values: false,
366 // The `<override clause>` on a merge insert *is* SQL:2016 standard surface, so
367 // the ANSI baseline accepts it (DuckDB is the outlier that rejects it).
368 merge_insert_overriding: true,
369 merge_insert_multirow: false,
370 merge_update_set_star: false,
371 merge_insert_star_by_name: false,
372 merge_error_action: false,
373 update_set_qualified_column: true,
374 };
375}
376
377impl StatementDdlGates {
378 /// The `ANSI` predefined value.
379 pub const ANSI: Self = Self {
380 colocation_groups: false,
381 // `CREATE TRIGGER`'s only modelled body form is SQLite's, so the standard
382 // baseline does not dispatch it.
383 create_trigger: false,
384 // The macro DDL is DuckDB-only; the standard baseline does not dispatch it.
385 create_macro: false,
386 create_secret: false,
387 // The user-defined-type DDL (`CREATE TYPE`/`DROP TYPE`) is a DuckDB extension here.
388 create_type: false,
389 // Virtual tables are a SQLite-only concept; the standard baseline does not dispatch
390 // `CREATE VIRTUAL TABLE`.
391 create_virtual_table: false,
392 // T176 sequence generators are an *optional* standard feature modelled via the
393 // PostgreSQL/DuckDB presets; the bare-standard baseline does not dispatch `SEQUENCE`.
394 create_sequence: false,
395 extension_ddl: false,
396 transform_ddl: false,
397 alter_system: false,
398 // MySQL's tablespace / logfile-group storage DDL has no ANSI equivalent (MySQL turns
399 // both on; MySQL derives from this preset).
400 tablespace_ddl: false,
401 logfile_group_ddl: false,
402 schemas: true,
403 // ANSI accepts the `CREATE SCHEMA` head but not the embedded-element form here:
404 // the standard embedding is validated only against the PostgreSQL oracle, so it
405 // is gated to PostgreSQL/Lenient rather than widening the reference dialect's
406 // accept surface without a differential; a trailing `CREATE`/`GRANT` stays a
407 // separate top-level statement under ANSI.
408 schema_elements: false,
409 databases: true,
410 // ANSI has no MySQL `DROP DATABASE`/`DROP SCHEMA` single-name synonym drop.
411 drop_database: false,
412 materialized_views: true,
413 routines: true,
414 or_replace: true,
415 create_or_replace_table: false,
416 // `CREATE RECURSIVE VIEW` is gated to DuckDB/Lenient; the standard baseline
417 // leaves `RECURSIVE` unconsumed before the expected `VIEW`.
418 // The standard baseline has no MySQL-style compound-statement routine body.
419 compound_statements: false,
420 alter_database: false,
421 alter_database_options: false,
422 server_definition: false,
423 alter_instance: false,
424 spatial_reference_system: false,
425 resource_group: false,
426 alter_sequence: false,
427 alter_object_set_schema: false,
428 };
429}
430impl ViewSequenceClauseSyntax {
431 /// View/sequence clause surface for the `ANSI` preset.
432 pub const ANSI: Self = Self {
433 materialized_view_to: false,
434 create_sequence_cache: false,
435 temporary_views: true,
436 recursive_views: false,
437 view_definition_options: false,
438 };
439}
440
441impl CreateTableClauseSyntax {
442 /// The `ANSI` predefined value.
443 pub const ANSI: Self = Self {
444 table_options: false,
445 // The SQLite trailing `WITHOUT ROWID` table option is a dialect extension, not
446 // standard surface, so the baseline rejects it.
447 without_rowid_table_option: false,
448 // The SQLite trailing `STRICT` table option is a dialect extension, not standard
449 // surface, so the baseline rejects it.
450 strict_table_option: false,
451 storage_parameters: true,
452 on_commit: true,
453 create_table_as_with_data: true,
454 create_table_as_execute: false,
455 // Declarative partitioning is a PostgreSQL extension, not standard SQL.
456 declarative_partitioning: false,
457 // Table inheritance and the LIKE source-table element are PostgreSQL extensions;
458 // the statement-level `CREATE TABLE t LIKE src` is a MySQL extension.
459 table_inheritance: false,
460 like_source_table: false,
461 statement_level_table_like: false,
462 unlogged_tables: false,
463 table_access_method: false,
464 without_oids: false,
465 typed_tables: false,
466 };
467}
468
469impl ColumnDefinitionSyntax {
470 /// The `ANSI` predefined value.
471 pub const ANSI: Self = Self {
472 // The keywordless generated-column `AS (…)` shorthand and the SQLite `CREATE
473 // TABLE` decorations are dialect extensions, not standard surface.
474 generated_column_shorthand: false,
475 // The SQLite column-level `ON CONFLICT <resolution>` clause is a dialect
476 // extension, not standard surface, so the baseline rejects it.
477 column_conflict_resolution_clause: false,
478 // A typeless column is a SQLite extension; the standard baseline requires a type,
479 // so a column with no type is a clean parse error.
480 typeless_column_definitions: false,
481 // DuckDB's type-optional-for-generated-columns narrowing is a dialect extension; the
482 // standard baseline requires a type on every column, generated or not.
483 typeless_generated_columns: false,
484 // The SQLite joined `AUTOINCREMENT` attribute is a dialect extension, not standard
485 // surface, so the baseline rejects it.
486 joined_autoincrement_attribute: false,
487 // An `ASC`/`DESC` order on an inline `PRIMARY KEY` is a SQLite extension; the standard
488 // baseline leaves the trailing keyword unconsumed and rejects it.
489 inline_primary_key_ordering: false,
490 // A `CONSTRAINT <name>` prefix on a column `COLLATE` is a SQLite extension; the standard
491 // baseline has no column COLLATE at all, so the named wrapper is off.
492 named_column_collate_constraint: false,
493 identity_columns: true,
494 compact_identity_columns: false,
495 // The standard accepts a bare (unparenthesized) expression default and a
496 // `CONSTRAINT <name>` prefix on any inline column constraint (MySQL restricts both).
497 default_expression_requires_parens: false,
498 column_default_requires_b_expr: false,
499 // Column COLLATE, UNLOGGED, column STORAGE/COMPRESSION, the table USING access method,
500 // legacy WITHOUT OIDS, and typed `OF <type>` tables are all dialect extensions absent
501 // from standard SQL.
502 column_collation: false,
503 column_storage: false,
504 };
505}
506
507impl ConstraintSyntax {
508 /// The `ANSI` predefined value.
509 pub const ANSI: Self = Self {
510 deferrable_constraints: true,
511 named_inline_non_check_constraints: true,
512 // The standard requires a constraint element after `CONSTRAINT <name>` (SQLite only
513 // makes it optional).
514 bare_constraint_name: false,
515 exclusion_constraints: false,
516 constraint_no_inherit_not_valid: false,
517 index_constraint_parameters: false,
518 constraint_column_collate_order: false,
519 referential_action_cascade_set: true,
520 check_constraint_subqueries: true,
521 };
522}
523
524impl IndexAlterSyntax {
525 /// The `ANSI` predefined value.
526 pub const ANSI: Self = Self {
527 rename_constraint: false,
528 alter_table_set_options: false,
529 drop_primary_key: false,
530 alter_column_add_identity: false,
531 index_storage_parameters: false,
532 drop_behavior: true,
533 // ANSI has no MySQL `DROP INDEX … ON <table>` form.
534 index_drop_on_table: false,
535 index_concurrently: false,
536 index_using_method: false,
537 partial_index: false,
538 index_if_not_exists: true,
539 index_nulls_order: true,
540 alter_table_extended: true,
541 alter_nested_column_paths: false,
542 alter_existence_guards: true,
543 alter_column_set_data_type: true,
544 routine_arg_types: true,
545 routine_arg_defaults: true,
546 routine_arg_modes: true,
547 // The SQL-standard `<language name>` is a bare identifier, not a string constant.
548 routine_language_string: false,
549 alter_table_multiple_actions: true,
550 };
551}
552
553impl ExistenceGuards {
554 /// The `ANSI` predefined value.
555 pub const ANSI: Self = Self {
556 if_exists: false,
557 view_if_not_exists: false,
558 create_database_if_not_exists: false,
559 };
560}
561
562impl SelectSyntax {
563 /// The `ANSI` predefined value.
564 pub const ANSI: Self = Self {
565 distinct_on: false,
566 select_into: false,
567 // Standard SQL requires at least one select item, so a bare `SELECT` is rejected.
568 empty_target_list: false,
569 // `QUALIFY` is a DuckDB (Teradata-origin) extension, not standard SQL.
570 qualify: false,
571 // A string literal as a column alias is a MySQL extension; the standard requires
572 // an identifier.
573 alias_string_literals: false,
574 bare_alias_string_literals: false,
575 // `UNION [ALL] BY NAME` is a DuckDB name-matched set operation, not standard
576 // SQL; `BY` after a set operator is a syntax error here.
577 union_by_name: false,
578 wildcard_modifiers: false,
579 wildcard_replace: false,
580 intersect_all: true,
581 except_all: true,
582 // The standard's qualified asterisk is a non-aliasable `<all fields reference>`; a
583 // trailing alias after `t.*` rejects (the ANSI-derived presets inherit this).
584 qualified_wildcard_alias: false,
585 // FROM-first SELECT (`FROM t SELECT x`, bare `FROM t`) is a DuckDB extension; a
586 // leading `FROM` is never a statement start in standard SQL.
587 from_first: false,
588 explicit_table: true,
589 // Standard SQL admits a parenthesized query as a compound operand
590 // (`(SELECT …) UNION (SELECT …)`, PostgreSQL `select_with_parens`).
591 parenthesized_query_operands: true,
592 // A ragged VALUES constructor is a DuckDB parse-time reject; the ANSI baseline
593 // leaves the arity check to bind, so it accepts one at parse (no oracle forces the
594 // strict-baseline reject, and keeping it off makes this a clean DuckDB-only delta).
595 values_rows_require_equal_arity: false,
596 // The standard query-position VALUES constructor is bare-parenthesized rows.
597 values_row_constructor: true,
598 // Standard SQL's `AS` projection alias is a `ColLabel` admitting reserved words;
599 // only MySQL rejects them there.
600 as_alias_rejects_reserved: false,
601 // A trailing comma in a list is a DuckDB tolerance; standard SQL rejects the
602 // dangling comma.
603 trailing_comma: false,
604 // DuckDB's prefix colon alias (`SELECT j : 42`, `FROM b : a`) is not standard SQL;
605 // a `:` at a select-item / table-factor head is a parse error here.
606 prefix_colon_alias: false,
607 // Hive/Spark `LATERAL VIEW` is not standard SQL; a post-FROM `LATERAL` is a
608 // parse error here.
609 lateral_view_clause: false,
610 // The Oracle-style `START WITH`/`CONNECT BY` hierarchical query clause is not
611 // standard SQL; a post-WHERE `CONNECT BY`/`START WITH` is a parse error here.
612 connect_by_clause: false,
613 };
614}
615
616impl QueryTailSyntax {
617 /// The `ANSI` predefined value.
618 pub const ANSI: Self = Self {
619 fetch_first: true,
620 limit_offset_comma: false,
621 // A query-tail row-locking clause (`FOR UPDATE`/`FOR SHARE`) is a
622 // PostgreSQL/MySQL extension, not a standard SQL query clause.
623 locking_clauses: false,
624 // The PostgreSQL-only strength refinements and stacked clauses are likewise
625 // non-standard; with no base locking clause they never lead here.
626 key_lock_strengths: false,
627 stacked_locking_clauses: false,
628 using_sample: false,
629 leading_offset: true,
630 limit_expressions: true,
631 limit_percent: false,
632 with_ties_requires_order_by: false,
633 // BigQuery/ZetaSQL `|>` pipe syntax is not standard SQL; a `|>` after a query is a
634 // parse error here (and the `|>` token never lexes with the gate off).
635 pipe_syntax: false,
636 // ClickHouse `LIMIT n BY …` is not standard SQL; a `BY` after `LIMIT` is a
637 // parse error here.
638 limit_by_clause: false,
639 // ClickHouse `SETTINGS …` is not standard SQL; a trailing `SETTINGS` is a parse
640 // error here.
641 settings_clause: false,
642 // ClickHouse `FORMAT …` is not standard SQL; a trailing `FORMAT` is a parse error
643 // here.
644 format_clause: false,
645 // MSSQL `FOR XML`/`FOR JSON` is not standard SQL; a trailing `FOR XML`/`FOR JSON`
646 // is a parse error here.
647 for_xml_json_clause: false,
648 };
649}
650
651impl GroupingSyntax {
652 /// The `ANSI` predefined value.
653 pub const ANSI: Self = Self {
654 grouping_sets: true,
655 // The trailing `WITH ROLLUP` is a MySQL-only spelling; standard SQL writes the
656 // super-aggregate as the `ROLLUP (…)` grouping set above.
657 with_rollup: false,
658 // Standard SQL sorts only by `ASC`/`DESC`; the operator-driven `USING` sort is
659 // a PostgreSQL extension.
660 order_by_using: false,
661 // `GROUP BY ALL` / `ORDER BY ALL` are DuckDB clause modes, not standard SQL;
662 // `ALL` after either keyword pair is a parse error here (the word is reserved).
663 group_by_all: false,
664 group_by_set_quantifier: false,
665 order_by_all: false,
666 };
667}
668
669impl UtilitySyntax {
670 /// The `ANSI` predefined value.
671 pub const ANSI: Self = Self {
672 copy: false,
673 // `COPY INTO` is Snowflake-specific bulk load/unload — not standard SQL, so the
674 // ANSI baseline leaves the surface off (MySQL and the other ANSI-derived presets
675 // inherit it off).
676 copy_into: false,
677 stage_references: false,
678 comment_on: false,
679 comment_if_exists: false,
680 pragma: false,
681 attach: false,
682 kill: false,
683 // MySQL's `HANDLER` low-level cursor family: no ANSI equivalent, so off at the
684 // baseline (MySQL turns it on; MySQL derives from this preset).
685 handler_statements: false,
686 // MySQL's `INSTALL`/`UNINSTALL` `PLUGIN`/`COMPONENT` family: no ANSI equivalent, so off
687 // at the baseline (MySQL turns it on; MySQL derives from this preset).
688 plugin_component_statements: false,
689 // MySQL's server-administration families (SHUTDOWN/RESTART/CLONE/IMPORT TABLE/HELP/
690 // BINLOG): no ANSI equivalent, so off at the baseline (MySQL turns each on).
691 shutdown: false,
692 restart: false,
693 clone: false,
694 import_table: false,
695 help_statement: false,
696 binlog: false,
697 // MySQL's `CACHE INDEX` / `LOAD INDEX INTO CACHE` MyISAM key-cache pair: no ANSI
698 // equivalent, so off at the baseline (MySQL turns it on; MySQL derives from here).
699 key_cache_statements: false,
700 use_statement: false,
701 use_qualified_name: false,
702 // DuckDB-only `USE 'name'` / `USE E'…'` / `USE $$…$$` string-literal target.
703 use_string_literal_name: false,
704 // DuckDB's `PREPARE`/`EXECUTE`/`DEALLOCATE` and `CALL` statements are not standard
705 // SQL, so the ANSI baseline dispatches neither (and MySQL inherits both off).
706 prepared_statements: false,
707 // The PostgreSQL typed parameter list is a widening of `PREPARE`, which is itself
708 // off here; the ANSI baseline leaves it off too (and MySQL inherits it off).
709 prepare_typed_parameters: false,
710 // MySQL's `PREPARE ... FROM` / `EXECUTE ... USING` / `{DEALLOCATE | DROP} PREPARE`
711 // lifecycle: no ANSI equivalent, so off at the baseline (MySQL turns it on).
712 prepared_statements_from: false,
713 call: false,
714 // The `CALL` statement itself is off at the baseline, so its MySQL bare-name widening
715 // is off too (and every ANSI-derived preset inherits it off).
716 call_bare_name: false,
717 load_extension: false,
718 load_bare_name: false,
719 load_data: false,
720 reset_scope: false,
721 detach_if_exists: false,
722 // `DO` is the PostgreSQL anonymous code block — not standard SQL, so the ANSI
723 // baseline leaves the leading keyword undispatched.
724 do_statement: false,
725 // MySQL's `DO <expr-list>` evaluate-and-discard statement — MySQL-only, so the ANSI
726 // baseline leaves it off (and every ANSI-derived preset inherits it off).
727 do_expression_list: false,
728 // MySQL's `LOCK/UNLOCK {TABLES|TABLE}` per-table locking and its
729 // `LOCK INSTANCE FOR BACKUP`/`UNLOCK INSTANCE` backup-lock pair — MySQL-only, so
730 // the ANSI baseline leaves both leading keywords undispatched.
731 lock_tables: false,
732 lock_instance: false,
733 // SQLite's `BEGIN {DEFERRED|IMMEDIATE|EXCLUSIVE}` modifier is not standard SQL
734 // (the standard/PostgreSQL `BEGIN` takes its own `TransactionMode` list instead),
735 // so the ANSI baseline leaves the modifier keyword unrecognized and it falls
736 // through to the existing `BEGIN`-body error.
737 // MySQL's `XA` distributed-transaction family is MySQL-only, so the ANSI baseline
738 // leaves the leading `XA` keyword undispatched (every ANSI-derived preset inherits
739 // it off unless it opts back in).
740 // The standalone `RENAME TABLE`/`RENAME USER` statements are MySQL-only, so the
741 // ANSI baseline does not dispatch the leading `RENAME` keyword.
742 rename_statement: false,
743 signal_diagnostics: false,
744 // `EXPORT`/`IMPORT DATABASE` are DuckDB catalogue-dump statements, not standard
745 // SQL, so the ANSI baseline leaves both leading keywords undispatched (MySQL,
746 // Snowflake, and Databricks inherit the pair off).
747 export_import_database: false,
748 // `UPDATE EXTENSIONS` is DuckDB extension management, not standard SQL, so the ANSI
749 // baseline never takes the `EXTENSIONS` lookahead and every `UPDATE` reaches the DML
750 // parser (MySQL, Snowflake, and Databricks inherit it off).
751 update_extensions: false,
752 // MySQL's `FLUSH` / `PURGE BINARY LOGS` server-administration statements — leading
753 // keyword gates off in the ANSI baseline (only MySQL/Lenient arm them).
754 flush: false,
755 purge_binary_logs: false,
756 replication_statements: false,
757 };
758}
759impl TransactionSyntax {
760 /// Transaction-control surface for the `ANSI` preset (split from UtilitySyntax).
761 pub const ANSI: Self = Self {
762 start_transaction: true,
763 start_transaction_block_optional: false,
764 transaction_work_keyword: true,
765 begin_transaction_keyword: true,
766 commit_transaction_keyword: true,
767 rollback_transaction_keyword: true,
768 transaction_name: false,
769 begin_transaction_modes: true,
770 transaction_savepoints: true,
771 set_transaction: true,
772 transaction_isolation_mode: true,
773 transaction_access_mode: true,
774 transaction_deferrable_mode: true,
775 start_transaction_isolation_mode: true,
776 start_transaction_deferrable_mode: true,
777 start_transaction_consistent_snapshot: false,
778 transaction_multiple_modes: true,
779 transaction_modes_require_commas: false,
780 transaction_modes_reject_duplicates: false,
781 abort_transaction_alias: false,
782 end_transaction_alias: false,
783 transaction_release: false,
784 transaction_chain: true,
785 release_savepoint_keyword_optional: true,
786 begin_transaction_mode: false,
787 xa_transactions: false,
788 };
789}
790
791impl ShowSyntax {
792 /// The `ANSI` predefined value.
793 pub const ANSI: Self = Self {
794 describe: false,
795 describe_summarize: false,
796 session_statements: true,
797 set_value_reserved_words: RESERVED_SET_VALUE_WORDS,
798 set_value_on_keyword: true,
799 set_value_null_keyword: false,
800 show_tables: false,
801 show_columns: false,
802 show_create_table: false,
803 show_functions: false,
804 show_routine_status: false,
805 show_verbose: false,
806 show_admin: false,
807 };
808}
809
810impl MaintenanceSyntax {
811 /// The `ANSI` predefined value.
812 pub const ANSI: Self = Self {
813 vacuum: false,
814 vacuum_analyze: false,
815 reindex: false,
816 analyze: false,
817 analyze_columns: false,
818 // `CHECKPOINT`/`LOAD` are PostgreSQL/DuckDB utility statements and the DuckDB
819 // `RESET`-scope / `DETACH … IF EXISTS` extensions are DuckDB-only — none is
820 // standard SQL, so the ANSI baseline dispatches/accepts none.
821 checkpoint: false,
822 checkpoint_database: false,
823 // The MySQL admin-table verbs (`ANALYZE/CHECK/CHECKSUM/OPTIMIZE/REPAIR TABLE`) are
824 // MySQL-only, so the ANSI baseline dispatches none.
825 table_maintenance: false,
826 };
827}
828
829impl AccessControlSyntax {
830 /// The `ANSI` predefined value.
831 pub const ANSI: Self = Self {
832 alter_role_rename: false,
833 access_control: true,
834 // The standard admits the schema-scoped grant objects and the `OPTION FOR` prefix.
835 access_control_extended_objects: true,
836 // The standard has no MySQL account-management DDL (`CREATE USER`, `CREATE ROLE`, …).
837 user_role_management: false,
838 // The standard uses the typed-object/role-spec grant grammar, not MySQL's account-based one.
839 access_control_account_grants: false,
840 };
841}
842
843impl TypeNameSyntax {
844 /// The `ANSI` predefined value.
845 pub const ANSI: Self = Self {
846 extended_scalar_type_names: false,
847 enum_type: false,
848 set_type: false,
849 numeric_modifiers: false,
850 integer_display_width: false,
851 composite_types: false,
852 // The standard accepts a length-less `VARCHAR` and the zoned temporal types
853 // (`TIMESTAMPTZ`, `WITH TIME ZONE`); MySQL requires the length and has no zoned type.
854 varchar_requires_length: false,
855 zoned_temporal_types: true,
856 // Empty `DECIMAL()` parens are a DuckDB spelling; the standard rejects them
857 // (`pg_query` rejects `DECIMAL()`).
858 empty_type_parens: false,
859 // MySQL's `CHARACTER SET`/`ASCII`/`UNICODE`/`BYTE`/`BINARY` type annotation; the
860 // standard/PostgreSQL reject it (`pg_query` syntax-errors `CHAR(5) CHARACTER SET x`).
861 character_set_annotation: false,
862 // A signed `numeric`/`decimal` precision/scale is a PostgreSQL raw-parse laxity; the
863 // standard requires an unsigned modifier.
864 signed_type_modifier: false,
865 // ClickHouse's `Nullable(T)` combinator has no differential oracle; the standard
866 // has no such type (its head resolves to a user-defined name here).
867 nullable_type: false,
868 // ClickHouse's `LowCardinality(T)` combinator likewise has no differential
869 // oracle; the standard has no such type.
870 low_cardinality_type: false,
871 // ClickHouse's `FixedString(N)` constructor likewise has no differential oracle;
872 // the standard has no such type.
873 fixed_string_type: false,
874 // ClickHouse's `DateTime64(P[, 'tz'])` constructor likewise has no differential
875 // oracle; the standard has no such type.
876 datetime64_type: false,
877 // ClickHouse's `Nested(name Type, ...)` composite likewise has no differential
878 // oracle; the standard has no such type.
879 nested_type: false,
880 // ClickHouse's `Int8`…`Int256`/`UInt*` fixed-bit-width integer names likewise have no
881 // differential oracle; the standard reads them as user-defined type names.
882 bit_width_integer_names: false,
883 // SQLite's liberal multi-word / two-argument affinity type name; the standard has a
884 // closed type vocabulary, so `LONG INTEGER` / `VARCHAR(123,456)` are parse errors
885 // (`pg_query` rejects both).
886 liberal_type_names: false,
887 string_type_modifiers: false,
888 angle_bracket_types: false,
889 };
890}
891
892impl ExpressionSyntax {
893 /// The `ANSI` predefined value.
894 pub const ANSI: Self = Self {
895 typecast_operator: false,
896 subscript: false,
897 // DuckDB's three-bound `[lower:upper:step]` slice is a dialect extension.
898 slice_step: false,
899 collate: false,
900 at_time_zone: false,
901 semi_structured_access: false,
902 array_constructor: false,
903 multidim_array_literals: false,
904 // The DuckDB `[…]`/`{…}`/`MAP` collection literals are a dialect extension.
905 collection_literals: false,
906 row_constructor: false,
907 // BigQuery's `STRUCT(...)` value constructor is a dialect extension.
908 struct_constructor: false,
909 field_selection: false,
910 field_wildcard: false,
911 typed_string_literals: true,
912 // The ANSI prefix-typed interval literal (`INTERVAL '1' HOUR TO SECOND`).
913 typed_interval_literal: true,
914 // DuckDB's relaxed interval spellings are a dialect extension.
915 relaxed_interval_syntax: false,
916 mysql_interval_operator: false,
917 // DuckDB's `#n` positional column reference is a dialect extension.
918 positional_column: false,
919 lambda_keyword: false,
920 };
921}
922
923impl OperatorSyntax {
924 /// The `ANSI` predefined value.
925 pub const ANSI: Self = Self {
926 operator_construct: false,
927 containment_operators: false,
928 json_arrow_operators: false,
929 jsonb_operators: false,
930 double_equals: false,
931 // DuckDB-only `//` spelling.
932 integer_divide_slash: false,
933 starts_with_operator: false,
934 is_general_equality: false,
935 // Truth-value tests `IS [NOT] {TRUE|FALSE|UNKNOWN}` are standard SQL (F571).
936 truth_value_tests: true,
937 // `<=>` is MySQL-only.
938 null_safe_equals: false,
939 // The single-arrow lambda is DuckDB-only (and `->` does not even lex here).
940 lambda_expressions: false,
941 // Bitwise `| & ~ << >>` are a shared PostgreSQL/MySQL/SQLite/DuckDB extension, not
942 // standard SQL, so the ANSI baseline rejects them.
943 bitwise_operators: false,
944 quantified_comparisons: true,
945 quantified_comparison_lists: false,
946 // The standard quantifier admits only the comparison operators; the any-operator
947 // extension is PostgreSQL's. MySQL/SQLite inherit this `false`.
948 quantified_arbitrary_operator: false,
949 // The general PostgreSQL `Op`-class operator surface is a PostgreSQL extension, not
950 // standard SQL, so the ANSI baseline rejects it (`^` exponentiation is likewise off,
951 // via `caret_operator` on the preset below).
952 custom_operators: false,
953 null_test_postfix: false,
954 // Postfix symbolic operators are a non-standard extension; the ANSI baseline rejects a
955 // trailing operator.
956 postfix_operators: false,
957 };
958}
959
960impl CallSyntax {
961 /// The `ANSI` predefined value.
962 pub const ANSI: Self = Self {
963 named_argument: false,
964 utc_special_functions: false,
965 columns_expression: false,
966 extract_from_syntax: true,
967 try_cast: false,
968 // ANSI/standard `CAST` admits any type name as its target.
969 restricted_cast_targets: false,
970 // The DuckDB-specific call tails — a quoted `EXTRACT` field, dot-method chaining,
971 // and in-parenthesis null-treatment — are dialect extensions, off in the baseline.
972 extract_string_field: false,
973 method_chaining: false,
974 sqljson_constructors_require_argument: false,
975 // The SQL/JSON expression functions are modelled against PostgreSQL's raw-parse
976 // surface, not verified against the bare ISO grammar, so they stay off for ANSI.
977 sqljson_expression_functions: false,
978 // The SQL/XML expression functions are likewise modelled against PostgreSQL's
979 // raw-parse surface, not the bare ISO grammar, so they stay off for ANSI.
980 xml_expression_functions: false,
981 variadic_argument: false,
982 // `merge_action()` is a PostgreSQL-only support function.
983 merge_action_function: false,
984 convert_function: false,
985 };
986}
987
988impl StringFuncForms {
989 /// The `ANSI` predefined value.
990 pub const ANSI: Self = Self {
991 // The four standard string special forms are core SQL-92/SQL:1999 grammar
992 // (E021-06/-09/-11, T312), so ANSI takes their *standard* shapes — like
993 // `extract_from_syntax` above: SUBSTRING is FROM-first only (no FOR-leading
994 // order), POSITION operands are the symmetric restricted level, OVERLAY is
995 // the PLACING form only (the standard defines no plain `overlay` call), and
996 // TRIM is the single-source `[side] [chars] FROM src` operand (no
997 // PostgreSQL trim_list tails). The PostgreSQL-only SIMILAR/ESCAPE regex
998 // substring stays off.
999 substring_from_for: true,
1000 substring_leading_for: false,
1001 substring_similar: false,
1002 substring_plain_call_requires_2_or_3_args: false,
1003 substr_from_for: false,
1004 position_in: true,
1005 position_asymmetric_operands: false,
1006 overlay_placing: true,
1007 overlay_requires_placing: true,
1008 trim_from: true,
1009 trim_list_syntax: false,
1010 // `COLLATION FOR (<expr>)` is a PostgreSQL-only common-subexpr.
1011 collation_for_expression: false,
1012 // The `CEIL TO <field>` keyword form is sqlparser-rs-parity surface only —
1013 // no probed oracle engine's grammar admits it.
1014 ceil_to_field: false,
1015 // The `FLOOR TO <field>` keyword form is sqlparser-rs-parity surface only —
1016 // no probed oracle engine's grammar admits it.
1017 floor_to_field: false,
1018 match_against: false,
1019 };
1020}
1021
1022impl AggregateCallSyntax {
1023 /// The `ANSI` predefined value.
1024 pub const ANSI: Self = Self {
1025 // The MySQL `GROUP_CONCAT(... SEPARATOR …)` delimiter and `UTC_*` niladic
1026 // functions are dialect extensions.
1027 group_concat_separator: false,
1028 within_group: true,
1029 aggregate_filter: true,
1030 // Standard SQL requires the `WHERE` keyword inside `FILTER (…)`; only DuckDB drops it.
1031 filter_optional_where: false,
1032 // Standard SQL admits an aggregate's argument forms regardless of a space before the
1033 // `(`; only MySQL's `IGNORE_SPACE`-off tokenizer makes the space significant.
1034 aggregate_args_require_adjacent_paren: false,
1035 null_treatment: false,
1036 // The MySQL built-in aggregate/window arity restrictions; ANSI admits an empty
1037 // call and `OVER` on any function.
1038 aggregate_calls_reject_empty_arguments: false,
1039 over_requires_windowable_function: false,
1040 window_function_tail: false,
1041 standalone_argument_order_by: false,
1042 };
1043}
1044
1045impl PredicateSyntax {
1046 /// The `ANSI` predefined value.
1047 pub const ANSI: Self = Self {
1048 is_distinct_from: true,
1049 like: true,
1050 ilike: false,
1051 similar_to: false,
1052 // The standard `OVERLAPS` period predicate stays off in this conservative base
1053 // (like `similar_to`/`ilike` above); PostgreSQL and Lenient enable it. MySQL and
1054 // SQLite inherit this `false` (both reject the predicate, engine-probed).
1055 overlaps_period_predicate: false,
1056 // The unparenthesized `IN <value>` operator is a DuckDB extension; the standard
1057 // requires the parentheses.
1058 unparenthesized_in_list: false,
1059 // The pattern-match quantifier `LIKE/ILIKE ANY|ALL (array)` is PostgreSQL's.
1060 // MySQL/SQLite inherit this `false`.
1061 pattern_match_quantifier: false,
1062 between_symmetric: false,
1063 is_normalized: false,
1064 // The standard `IN` predicate requires at least one list element; an empty `IN ()`
1065 // is a syntax error. SQLite overrides this to accept it.
1066 empty_in_list: false,
1067 // The two-word `<expr> NOT NULL` postfix null test is a SQLite/DuckDB extension; the
1068 // standard has only `IS NOT NULL`. SQLite and Lenient override this to accept it.
1069 null_test_two_word_postfix: false,
1070 };
1071}
1072
1073impl FeatureSet {
1074 /// The generic/standard dialect data.
1075 pub const ANSI: Self = Self {
1076 identifier_casing: Casing::Upper,
1077 identifier_quotes: STANDARD_IDENTIFIER_QUOTES,
1078 default_null_ordering: NullOrdering::NullsLast,
1079 reserved_column_name: RESERVED_COLUMN_NAME,
1080 reserved_function_name: RESERVED_FUNCTION_NAME,
1081 reserved_type_name: RESERVED_TYPE_NAME,
1082 reserved_bare_alias: RESERVED_BARE_ALIAS,
1083 // Standard/PostgreSQL admit every keyword as a `ColLabel` (`SELECT a AS select`).
1084 reserved_as_label: KeywordSet::EMPTY,
1085 // Standard relation names are catalog-qualified (`catalog.schema.table`).
1086 catalog_qualified_names: true,
1087 byte_classes: STANDARD_BYTE_CLASSES,
1088 binding_powers: STANDARD_BINDING_POWERS,
1089 set_operation_powers: STANDARD_SET_OPERATION_BINDING_POWERS,
1090 string_literals: StringLiteralSyntax::ANSI,
1091 numeric_literals: NumericLiteralSyntax::ANSI,
1092 parameters: ParameterSyntax::ANSI,
1093 session_variables: SessionVariableSyntax::ANSI,
1094 identifier_syntax: IdentifierSyntax::ANSI,
1095 table_expressions: TableExpressionSyntax::ANSI,
1096 join_syntax: JoinSyntax::ANSI,
1097 table_factor_syntax: TableFactorSyntax::ANSI,
1098 expression_syntax: ExpressionSyntax::ANSI,
1099 operator_syntax: OperatorSyntax::ANSI,
1100 call_syntax: CallSyntax::ANSI,
1101 string_func_forms: StringFuncForms::ANSI,
1102 aggregate_call_syntax: AggregateCallSyntax::ANSI,
1103 predicate_syntax: PredicateSyntax::ANSI,
1104 pipe_operator: PipeOperator::StringConcat,
1105 double_ampersand: DoubleAmpersand::Unsupported,
1106 keyword_operators: KeywordOperators::Unsupported,
1107 // `^` has no infix meaning and `#` is not the XOR operator: bitwise XOR (`#`/`^`)
1108 // and `^` exponentiation are PostgreSQL/MySQL operators, not standard SQL.
1109 caret_operator: CaretOperator::Unsupported,
1110 hash_bitwise_xor: false,
1111 comment_syntax: CommentSyntax::ANSI,
1112 mutation_syntax: MutationSyntax::ANSI,
1113 statement_ddl_gates: StatementDdlGates::ANSI,
1114 view_sequence_clause_syntax: ViewSequenceClauseSyntax::ANSI,
1115 create_table_clause_syntax: CreateTableClauseSyntax::ANSI,
1116 column_definition_syntax: ColumnDefinitionSyntax::ANSI,
1117 constraint_syntax: ConstraintSyntax::ANSI,
1118 index_alter_syntax: IndexAlterSyntax::ANSI,
1119 existence_guards: ExistenceGuards::ANSI,
1120 select_syntax: SelectSyntax::ANSI,
1121 query_tail_syntax: QueryTailSyntax::ANSI,
1122 grouping_syntax: GroupingSyntax::ANSI,
1123 utility_syntax: UtilitySyntax::ANSI,
1124 transaction_syntax: TransactionSyntax::ANSI,
1125 show_syntax: ShowSyntax::ANSI,
1126 maintenance_syntax: MaintenanceSyntax::ANSI,
1127 access_control_syntax: AccessControlSyntax::ANSI,
1128 type_name_syntax: TypeNameSyntax::ANSI,
1129 // The generic baseline renders its own canonical type spellings (ADR-0011).
1130 target_spelling: TargetSpelling::Ansi,
1131 };
1132}
1133
1134/// Prefer [`FeatureSet::ANSI`] for struct update.
1135pub const ANSI: FeatureSet = FeatureSet::ANSI;
1136
1137// Compile-time proof the ANSI baseline claims no shared tokenizer trigger twice. The
1138// ratchet must sit where a preset grows: a future edit that adds a contending feature
1139// fails the build here rather than silently shadowing one meaning (the discipline
1140// `LENIENT` already carries, applied uniformly).
1141const _: () = assert!(FeatureSet::ANSI.is_lexically_consistent());
1142// The two sibling self-consistency registries are ratcheted the same way, so the
1143// parse-entry `debug_assert!` folds all three to dead code for this preset: no refinement
1144// flag rides an unset base, and no two features contend for one parser-position head.
1145const _: () = assert!(FeatureSet::ANSI.has_satisfied_feature_dependencies());
1146const _: () = assert!(FeatureSet::ANSI.has_no_grammar_conflict());
1147
1148#[cfg(test)]
1149mod tests {
1150 use super::Keyword;
1151 use super::*;
1152
1153 #[test]
1154 fn per_position_sets_are_data_driven_and_position_specific() {
1155 // `JOIN` is type_func_name: a function/type name but not a bare ColId.
1156 assert!(RESERVED_COLUMN_NAME.contains(Keyword::Join));
1157 assert!(!RESERVED_FUNCTION_NAME.contains(Keyword::Join));
1158 assert!(!RESERVED_TYPE_NAME.contains(Keyword::Join));
1159
1160 // `COALESCE` is col_name: a column name and (for us) a function, but not a
1161 // type name.
1162 assert!(!RESERVED_COLUMN_NAME.contains(Keyword::Coalesce));
1163 assert!(!RESERVED_FUNCTION_NAME.contains(Keyword::Coalesce));
1164 assert!(RESERVED_TYPE_NAME.contains(Keyword::Coalesce));
1165
1166 // `SELECT` is reserved: rejected as every kind of name, yet still a bare
1167 // label (it is BARE_LABEL, not AS_LABEL).
1168 assert!(RESERVED_COLUMN_NAME.contains(Keyword::Select));
1169 assert!(RESERVED_FUNCTION_NAME.contains(Keyword::Select));
1170 assert!(RESERVED_TYPE_NAME.contains(Keyword::Select));
1171 assert!(!RESERVED_BARE_ALIAS.contains(Keyword::Select));
1172
1173 // `OVER`/`FILTER` are unreserved (usable as any name) yet AS_LABEL, so they
1174 // are the bare-label divergence: not a bare alias, but everything else.
1175 for keyword in [Keyword::Over, Keyword::Filter] {
1176 assert!(!RESERVED_COLUMN_NAME.contains(keyword));
1177 assert!(!RESERVED_FUNCTION_NAME.contains(keyword));
1178 assert!(!RESERVED_TYPE_NAME.contains(keyword));
1179 assert!(RESERVED_BARE_ALIAS.contains(keyword));
1180 }
1181 }
1182}