| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| mod ansi; | |
| mod bigquery; | |
| mod clickhouse; | |
| mod databricks; | |
| mod duckdb; | |
| mod generic; | |
| mod hive; | |
| mod mssql; | |
| mod mysql; | |
| mod oracle; | |
| mod postgresql; | |
| mod redshift; | |
| mod snowflake; | |
| mod spark; | |
| mod sqlite; | |
| mod teradata; | |
| use core::any::{Any, TypeId}; | |
| use core::fmt::Debug; | |
| use core::iter::Peekable; | |
| use core::str::Chars; | |
| use log::debug; | |
| pub use self::ansi::AnsiDialect; | |
| pub use self::bigquery::BigQueryDialect; | |
| pub use self::clickhouse::ClickHouseDialect; | |
| pub use self::databricks::DatabricksDialect; | |
| pub use self::duckdb::DuckDbDialect; | |
| pub use self::generic::GenericDialect; | |
| pub use self::hive::HiveDialect; | |
| pub use self::mssql::MsSqlDialect; | |
| pub use self::mysql::MySqlDialect; | |
| pub use self::oracle::OracleDialect; | |
| pub use self::postgresql::PostgreSqlDialect; | |
| pub use self::redshift::RedshiftSqlDialect; | |
| pub use self::snowflake::parse_snowflake_stage_name; | |
| pub use self::snowflake::SnowflakeDialect; | |
| pub use self::spark::SparkSqlDialect; | |
| pub use self::sqlite::SQLiteDialect; | |
| pub use self::teradata::TeradataDialect; | |
| /// Macro for streamlining the creation of derived `Dialect` objects. | |
| /// The generated struct includes `new()` and `default()` constructors. | |
| /// Requires the `derive-dialect` feature. | |
| /// | |
| /// # Syntax | |
| /// | |
| /// ```text | |
| /// derive_dialect!(NewDialect, BaseDialect); | |
| /// derive_dialect!(NewDialect, BaseDialect, overrides = { method = value, ... }); | |
| /// derive_dialect!(NewDialect, BaseDialect, preserve_type_id = true); | |
| /// derive_dialect!(NewDialect, BaseDialect, preserve_type_id = true, overrides = { ... }); | |
| /// ``` | |
| /// | |
| /// # Example | |
| /// | |
| /// ``` | |
| /// use sqlparser::derive_dialect; | |
| /// use sqlparser::dialect::{Dialect, GenericDialect}; | |
| /// | |
| /// // Override boolean methods (supports_*, allow_*, etc.) | |
| /// derive_dialect!(CustomDialect, GenericDialect, overrides = { | |
| /// supports_order_by_all = true, | |
| /// supports_nested_comments = true, | |
| /// }); | |
| /// | |
| /// let dialect = CustomDialect::new(); | |
| /// assert!(dialect.supports_order_by_all()); | |
| /// assert!(dialect.supports_nested_comments()); | |
| /// ``` | |
| /// | |
| /// # Overriding `identifier_quote_style` | |
| /// | |
| /// Use a char literal or `None`: | |
| /// ``` | |
| /// use sqlparser::derive_dialect; | |
| /// use sqlparser::dialect::{Dialect, PostgreSqlDialect}; | |
| /// | |
| /// derive_dialect!(BacktickPostgreSqlDialect, PostgreSqlDialect, | |
| /// preserve_type_id = true, | |
| /// overrides = { identifier_quote_style = '`' } | |
| /// ); | |
| /// let d: &dyn Dialect = &BacktickPostgreSqlDialect::new(); | |
| /// assert_eq!(d.identifier_quote_style("foo"), Some('`')); | |
| /// | |
| /// derive_dialect!(QuotelessPostgreSqlDialect, PostgreSqlDialect, | |
| /// preserve_type_id = true, | |
| /// overrides = { identifier_quote_style = None } | |
| /// ); | |
| /// let d: &dyn Dialect = &QuotelessPostgreSqlDialect::new(); | |
| /// assert_eq!(d.identifier_quote_style("foo"), None); | |
| /// ``` | |
| /// | |
| /// # Type Identity | |
| /// | |
| /// By default, derived dialects have their own `TypeId`. Set `preserve_type_id = true` to | |
| /// retain the base dialect's identity with respect to the parser's `dialect.is::<T>()` checks: | |
| /// ``` | |
| /// use sqlparser::derive_dialect; | |
| /// use sqlparser::dialect::{Dialect, GenericDialect}; | |
| /// | |
| /// derive_dialect!(EnhancedGenericDialect, GenericDialect, | |
| /// preserve_type_id = true, | |
| /// overrides = { | |
| /// supports_order_by_all = true, | |
| /// supports_nested_comments = true, | |
| /// } | |
| /// ); | |
| /// let d: &dyn Dialect = &EnhancedGenericDialect::new(); | |
| /// assert!(d.is::<GenericDialect>()); // still recognized as a GenericDialect | |
| /// assert!(d.supports_nested_comments()); | |
| /// assert!(d.supports_order_by_all()); | |
| /// ``` | |
| #[cfg(feature = "derive-dialect")] | |
| pub use sqlparser_derive::derive_dialect; | |
| use crate::ast::{ColumnOption, Expr, GranteesType, Ident, ObjectNamePart, Statement}; | |
| pub use crate::keywords; | |
| use crate::keywords::Keyword; | |
| use crate::parser::{Parser, ParserError}; | |
| use crate::tokenizer::Token; | |
| #[cfg(not(feature = "std"))] | |
| use alloc::boxed::Box; | |
| /// Convenience check if a [`Parser`] uses a certain dialect. | |
| /// | |
| /// Note: when possible, please use the new style, adding a method to | |
| /// the [`Dialect`] trait rather than using this macro. | |
| /// | |
| /// The benefits of adding a method on `Dialect` over this macro are: | |
| /// 1. user defined [`Dialect`]s can customize the parsing behavior | |
| /// 2. The differences between dialects can be clearly documented in the trait | |
| /// | |
| /// `dialect_of!(parser is SQLiteDialect | GenericDialect)` evaluates | |
| /// to `true` if `parser.dialect` is one of the [`Dialect`]s specified. | |
| macro_rules! dialect_of { | |
| ( $parsed_dialect: ident is $($dialect_type: ty)|+ ) => { | |
| ($($parsed_dialect.dialect.is::<$dialect_type>())||+) | |
| }; | |
| } | |
| // Similar to above, but for applying directly against an instance of dialect | |
| // instead of a struct member named dialect. This avoids lifetime issues when | |
| // mixing match guards and token references. | |
| macro_rules! dialect_is { | |
| ($dialect:ident is $($dialect_type:ty)|+) => { | |
| ($($dialect.is::<$dialect_type>())||+) | |
| } | |
| } | |
| /// Encapsulates the differences between SQL implementations. | |
| /// | |
| /// # SQL Dialects | |
| /// | |
| /// SQL implementations deviate from one another, either due to | |
| /// custom extensions or various historical reasons. This trait | |
| /// encapsulates the parsing differences between dialects. | |
| /// | |
| /// [`GenericDialect`] is the most permissive dialect, and parses the union of | |
| /// all the other dialects, when there is no ambiguity. However, it does not | |
| /// currently allow `CREATE TABLE` statements without types specified for all | |
| /// columns; use [`SQLiteDialect`] if you require that. | |
| /// | |
| /// # Examples | |
| /// Most users create a [`Dialect`] directly, as shown on the [module | |
| /// level documentation]: | |
| /// | |
| /// ``` | |
| /// # use sqlparser::dialect::AnsiDialect; | |
| /// let dialect = AnsiDialect {}; | |
| /// ``` | |
| /// | |
| /// It is also possible to dynamically create a [`Dialect`] from its | |
| /// name. For example: | |
| /// | |
| /// ``` | |
| /// # use sqlparser::dialect::{AnsiDialect, dialect_from_str}; | |
| /// let dialect = dialect_from_str("ansi").unwrap(); | |
| /// | |
| /// // Parsed dialect is an instance of `AnsiDialect`: | |
| /// assert!(dialect.is::<AnsiDialect>()); | |
| /// ``` | |
| /// | |
| /// [module level documentation]: crate | |
| pub trait Dialect: Debug + Any { | |
| /// Determine the [`TypeId`] of this dialect. | |
| /// | |
| /// By default, return the same [`TypeId`] as [`Any::type_id`]. Can be overridden by | |
| /// dialects that behave like other dialects (for example, when wrapping a dialect). | |
| fn dialect(&self) -> TypeId { | |
| self.type_id() | |
| } | |
| /// Determine if a character starts a quoted identifier. The default | |
| /// implementation, accepting "double quoted" ids is both ANSI-compliant | |
| /// and appropriate for most dialects (with the notable exception of | |
| /// MySQL, MS SQL, and sqlite). You can accept one of characters listed | |
| /// in `Word::matching_end_quote` here | |
| fn is_delimited_identifier_start(&self, ch: char) -> bool { | |
| ch == '"' || ch == '`' | |
| } | |
| /// Determine if a character starts a potential nested quoted identifier. | |
| /// Example: RedShift supports the following quote styles to all mean the same thing: | |
| /// ```sql | |
| /// SELECT 1 AS foo; | |
| /// SELECT 1 AS "foo"; | |
| /// SELECT 1 AS [foo]; | |
| /// SELECT 1 AS ["foo"]; | |
| /// ``` | |
| fn is_nested_delimited_identifier_start(&self, _ch: char) -> bool { | |
| false | |
| } | |
| /// Only applicable whenever [`Self::is_nested_delimited_identifier_start`] returns true | |
| /// If the next sequence of tokens potentially represent a nested identifier, then this method | |
| /// returns a tuple containing the outer quote style, and if present, the inner (nested) quote style. | |
| /// | |
| /// Example (Redshift): | |
| /// ```text | |
| /// `["foo"]` => Some(`[`, Some(`"`)) | |
| /// `[foo]` => Some(`[`, None) | |
| /// `[0]` => None | |
| /// `"foo"` => None | |
| /// ``` | |
| fn peek_nested_delimited_identifier_quotes( | |
| &self, | |
| mut _chars: Peekable<Chars<'_>>, | |
| ) -> Option<(char, Option<char>)> { | |
| None | |
| } | |
| /// Return the character used to quote identifiers. | |
| fn identifier_quote_style(&self, _identifier: &str) -> Option<char> { | |
| None | |
| } | |
| /// Determine if a character is a valid start character for an unquoted identifier | |
| fn is_identifier_start(&self, ch: char) -> bool; | |
| /// Determine if a character is a valid unquoted identifier character | |
| fn is_identifier_part(&self, ch: char) -> bool; | |
| /// Most dialects do not have custom operators. Override this method to provide custom operators. | |
| fn is_custom_operator_part(&self, _ch: char) -> bool { | |
| false | |
| } | |
| /// Determine if the dialect supports escaping characters via '\' in string literals. | |
| /// | |
| /// Some dialects like BigQuery and Snowflake support this while others like | |
| /// Postgres do not. Such that the following is accepted by the former but | |
| /// rejected by the latter. | |
| /// ```sql | |
| /// SELECT 'ab\'cd'; | |
| /// ``` | |
| /// | |
| /// Conversely, such dialects reject the following statement which | |
| /// otherwise would be valid in the other dialects. | |
| /// ```sql | |
| /// SELECT '\'; | |
| /// ``` | |
| fn supports_string_literal_backslash_escape(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect tokenize `N'...'` as a national string literal? | |
| /// | |
| /// Dialects such as SQLite treat `N` as a plain identifier, so `N'foo'` is | |
| /// the identifier `N` followed by a string literal, not a national string. | |
| fn supports_national_string_literal(&self) -> bool { | |
| true | |
| } | |
| /// Treats literal prefixes immediately after `.` as unquoted identifiers. | |
| /// | |
| /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/identifiers.html), | |
| /// [MariaDB](https://mariadb.com/docs/server/reference/sql-structure/sql-language-structure/identifier-names) | |
| fn supports_literal_prefix_in_qualified_name(&self) -> bool { | |
| false | |
| } | |
| /// Determine whether the dialect strips the backslash when escaping LIKE wildcards (%, _). | |
| /// | |
| /// [MySQL] has a special case when escaping single quoted strings which leaves these unescaped | |
| /// so they can be used in LIKE patterns without double-escaping (as is necessary in other | |
| /// escaping dialects, such as [Snowflake]). Generally, special characters have escaping rules | |
| /// causing them to be replaced with a different byte sequences (e.g. `'\0'` becoming the zero | |
| /// byte), and the default if an escaped character does not have a specific escaping rule is to | |
| /// strip the backslash (e.g. there is no rule for `h`, so `'\h' = 'h'`). MySQL's special case | |
| /// for ignoring LIKE wildcard escapes is to *not* strip the backslash, so that `'\%' = '\\%'`. | |
| /// This applies to all string literals though, not just those used in LIKE patterns. | |
| /// | |
| /// ```text | |
| /// mysql> select '\_', hex('\\'), hex('_'), hex('\_'); | |
| /// +----+-----------+----------+-----------+ | |
| /// | \_ | hex('\\') | hex('_') | hex('\_') | | |
| /// +----+-----------+----------+-----------+ | |
| /// | \_ | 5C | 5F | 5C5F | | |
| /// +----+-----------+----------+-----------+ | |
| /// 1 row in set (0.00 sec) | |
| /// ``` | |
| /// | |
| /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/string-literals.html | |
| /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/functions/like#usage-notes | |
| fn ignores_wildcard_escapes(&self) -> bool { | |
| false | |
| } | |
| /// Determine if the dialect supports string literals with `U&` prefix. | |
| /// This is used to specify Unicode code points in string literals. | |
| /// For example, in PostgreSQL, the following is a valid string literal: | |
| /// ```sql | |
| /// SELECT U&'\0061\0062\0063'; | |
| /// ``` | |
| /// This is equivalent to the string literal `'abc'`. | |
| /// See | |
| /// - [Postgres docs](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-UESCAPE) | |
| /// - [H2 docs](http://www.h2database.com/html/grammar.html#string) | |
| fn supports_unicode_string_literal(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support `FILTER (WHERE expr)` for aggregate queries? | |
| fn supports_filter_during_aggregation(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports referencing another named window | |
| /// within a window clause declaration. | |
| /// | |
| /// Example | |
| /// ```sql | |
| /// SELECT * FROM mytable | |
| /// WINDOW mynamed_window AS another_named_window | |
| /// ``` | |
| fn supports_window_clause_named_window_reference(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `ARRAY_AGG() [WITHIN GROUP (ORDER BY)]` expressions. | |
| /// Otherwise, the dialect should expect an `ORDER BY` without the `WITHIN GROUP` clause, e.g. [`ANSI`] | |
| /// | |
| /// [`ANSI`]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#array-aggregate-function | |
| fn supports_within_after_array_aggregation(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `PARTITION BY` appearing after `ORDER BY` | |
| /// in a `CREATE TABLE` statement (in addition to the standard placement before `ORDER BY`). | |
| /// | |
| /// ClickHouse DDL uses this ordering: | |
| /// <https://clickhouse.com/docs/en/sql-reference/statements/create/table#partition-by> | |
| fn supports_partition_by_after_order_by(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports ClickHouse-style `ARRAY JOIN` / `LEFT ARRAY JOIN` / | |
| /// `INNER ARRAY JOIN` syntax for unnesting arrays inline. | |
| /// | |
| /// <https://clickhouse.com/docs/en/sql-reference/statements/select/array-join> | |
| fn supports_array_join_syntax(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect treats `ALTER USER` as a synonym for `ALTER ROLE`. | |
| /// | |
| /// In PostgreSQL, `ALTER USER` and `ALTER ROLE` are synonyms that accept the same | |
| /// option syntax, so `ALTER USER` is parsed into a [`Statement::AlterRole`]. | |
| /// | |
| /// <https://www.postgresql.org/docs/current/sql-alteruser.html> | |
| /// | |
| /// [`Statement::AlterRole`]: crate::ast::Statement::AlterRole | |
| fn supports_alter_user_as_alter_role(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `AS MATERIALIZED` and `AS NOT MATERIALIZED` | |
| /// in a common table expression. | |
| /// | |
| /// ```sql | |
| /// WITH cte AS MATERIALIZED (SELECT 1) SELECT * FROM cte | |
| /// ``` | |
| fn supports_cte_materialization(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialects supports `group sets, roll up, or cube` expressions. | |
| fn supports_group_by_expr(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialects supports `GROUP BY` modifiers prefixed by a `WITH` keyword. | |
| /// Example: `GROUP BY value WITH ROLLUP`. | |
| fn supports_group_by_with_modifier(&self) -> bool { | |
| false | |
| } | |
| /// Indicates whether the dialect supports left-associative join parsing | |
| /// by default when parentheses are omitted in nested joins. | |
| /// | |
| /// Most dialects (like MySQL or Postgres) assume **left-associative** precedence, | |
| /// so a query like: | |
| /// | |
| /// ```sql | |
| /// SELECT * FROM t1 NATURAL JOIN t5 INNER JOIN t0 ON ... | |
| /// ``` | |
| /// is interpreted as: | |
| /// ```sql | |
| /// ((t1 NATURAL JOIN t5) INNER JOIN t0 ON ...) | |
| /// ``` | |
| /// and internally represented as a **flat list** of joins. | |
| /// | |
| /// In contrast, some dialects (e.g. **Snowflake**) assume **right-associative** | |
| /// precedence and interpret the same query as: | |
| /// ```sql | |
| /// (t1 NATURAL JOIN (t5 INNER JOIN t0 ON ...)) | |
| /// ``` | |
| /// which results in a **nested join** structure in the AST. | |
| /// | |
| /// If this method returns `false`, the parser must build nested join trees | |
| /// even in the absence of parentheses to reflect the correct associativity | |
| fn supports_left_associative_joins_without_parens(&self) -> bool { | |
| true | |
| } | |
| /// Returns true if the dialect supports the `(+)` syntax for OUTER JOIN. | |
| fn supports_outer_join_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports a join specification on CROSS JOIN. | |
| fn supports_cross_join_constraint(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports CONNECT BY. | |
| fn supports_connect_by(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `EXECUTE IMMEDIATE` statements. | |
| fn supports_execute_immediate(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the MATCH_RECOGNIZE operation. | |
| fn supports_match_recognize(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `(NOT) IN ()` expressions | |
| fn supports_in_empty_list(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports a bare expression as the right-hand | |
| /// side of `IN`, without a parenthesized list — as in `x IN 'a'`. | |
| fn supports_in_unparenthesized_expr(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `GLOBAL IN` and `GLOBAL NOT IN`. | |
| fn supports_global_in(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `BEGIN {DEFERRED | IMMEDIATE | EXCLUSIVE | TRY | CATCH} [TRANSACTION]` statements | |
| fn supports_start_transaction_modifier(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `END {TRY | CATCH}` statements | |
| fn supports_end_transaction_modifier(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports named arguments of the form `FUN(a = '1', b = '2')`. | |
| fn supports_named_fn_args_with_eq_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports named arguments of the form `FUN(a : '1', b : '2')`. | |
| fn supports_named_fn_args_with_colon_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports named arguments of the form `FUN(a := '1', b := '2')`. | |
| fn supports_named_fn_args_with_assignment_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports named arguments of the form `FUN(a => '1', b => '2')`. | |
| fn supports_named_fn_args_with_rarrow_operator(&self) -> bool { | |
| true | |
| } | |
| /// Returns true if dialect supports argument name as arbitrary expression. | |
| /// e.g. `FUN(LOWER('a'):'1', b:'2')` | |
| /// Such function arguments are represented in the AST by the `FunctionArg::ExprNamed` variant, | |
| /// otherwise use the `FunctionArg::Named` variant (compatible reason). | |
| fn supports_named_fn_args_with_expr_name(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports identifiers starting with a numeric | |
| /// prefix such as tables named `59901_user_login` | |
| fn supports_numeric_prefix(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if a period directly after an identifier starts a tuple | |
| /// element access such as `t.1`, instead of the number `.1`. | |
| fn supports_tuple_element_access(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports numbers containing underscores, e.g. `10_000_000` | |
| fn supports_numeric_literal_underscores(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialects supports specifying null treatment | |
| /// as part of a window function's parameter list as opposed | |
| /// to after the parameter list. | |
| /// | |
| /// i.e The following syntax returns true | |
| /// ```sql | |
| /// FIRST_VALUE(a IGNORE NULLS) OVER () | |
| /// ``` | |
| /// while the following syntax returns false | |
| /// ```sql | |
| /// FIRST_VALUE(a) IGNORE NULLS OVER () | |
| /// ``` | |
| fn supports_window_function_null_treatment_arg(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports defining structs or objects using a | |
| /// syntax like `{'x': 1, 'y': 2, 'z': 3}`. | |
| fn supports_dictionary_syntax(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports defining object using the | |
| /// syntax like `Map {1: 10, 2: 20}`. | |
| fn support_map_literal_syntax(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports lambda functions, for example: | |
| /// | |
| /// ```sql | |
| /// SELECT transform(array(1, 2, 3), x -> x + 1); -- returns [2,3,4] | |
| /// ``` | |
| /// | |
| /// This enables both the `->` spelling above and the `LAMBDA` keyword | |
| /// spelling gated by [`Self::supports_lambda_keyword_syntax`]. A dialect | |
| /// that uses `->` as a binary operator should override only the latter. | |
| fn supports_lambda_functions(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `LAMBDA` keyword spelling of | |
| /// lambda functions, for example: | |
| /// | |
| /// ```sql | |
| /// SELECT list_transform([1, 2, 3], lambda x : x + 1); -- returns [2, 3, 4] | |
| /// ``` | |
| /// | |
| /// This spelling does not claim the `->` token, so it can be enabled by | |
| /// dialects that already give `->` a different meaning, such as JSON | |
| /// member access. | |
| /// | |
| /// Defaults to [`Self::supports_lambda_functions`], so dialects supporting | |
| /// the `->` spelling accept the `LAMBDA` spelling too unless they say | |
| /// otherwise. | |
| /// | |
| /// See <https://duckdb.org/docs/stable/sql/functions/lambda> | |
| fn supports_lambda_keyword_syntax(&self) -> bool { | |
| self.supports_lambda_functions() | |
| } | |
| /// Returns true if the dialect supports multiple variable assignment | |
| /// using parentheses in a `SET` variable declaration. | |
| /// | |
| /// ```sql | |
| /// SET (variable[, ...]) = (expression[, ...]); | |
| /// ``` | |
| fn supports_parenthesized_set_variables(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports multiple `SET` statements | |
| /// in a single statement. | |
| /// | |
| /// ```sql | |
| /// SET variable = expression [, variable = expression]; | |
| /// ``` | |
| fn supports_comma_separated_set_assignments(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `ORDER BY` in `UPDATE` statements. | |
| /// | |
| /// ```sql | |
| /// UPDATE foo SET bar = false WHERE foo = true ORDER BY foo ASC; | |
| /// ``` | |
| /// See <https://dev.mysql.com/doc/refman/8.4/en/update.html> | |
| fn supports_update_order_by(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports an `EXCEPT` clause following a | |
| /// wildcard in a select list. | |
| /// | |
| /// For example | |
| /// ```sql | |
| /// SELECT * EXCEPT order_id FROM orders; | |
| /// ``` | |
| fn supports_select_wildcard_except(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect has a CONVERT function which accepts a type first | |
| /// and an expression second, e.g. `CONVERT(varchar, 1)` | |
| fn convert_type_before_value(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports triple quoted string | |
| /// e.g. `"""abc"""` | |
| fn supports_triple_quoted_string(&self) -> bool { | |
| false | |
| } | |
| /// Dialect-specific prefix parser override | |
| fn parse_prefix(&self, _parser: &mut Parser) -> Option<Result<Expr, ParserError>> { | |
| // return None to fall back to the default behavior | |
| None | |
| } | |
| /// Does the dialect support the `APPROXIMATE PERCENTILE_DISC` function syntax? | |
| /// See <https://docs.aws.amazon.com/redshift/latest/dg/r_APPROXIMATE_PERCENTILE_DISC.html> | |
| fn supports_approximate_percentile_disc(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support trailing commas around the query? | |
| fn supports_trailing_commas(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support parsing `LIMIT 1, 2` as `LIMIT 2 OFFSET 1`? | |
| fn supports_limit_comma(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports concatenating of string literal | |
| /// Example: `SELECT 'Hello ' "world" => SELECT 'Hello world'` | |
| fn supports_string_literal_concatenation(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports concatenating string literals with a newline. | |
| /// For example, the following statement would return `true`: | |
| /// ```sql | |
| /// SELECT 'abc' in ( | |
| /// 'a' | |
| /// 'b' | |
| /// 'c' | |
| /// ); | |
| /// ``` | |
| fn supports_string_literal_concatenation_with_newline(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support trailing commas in the projection list? | |
| fn supports_projection_trailing_commas(&self) -> bool { | |
| self.supports_trailing_commas() | |
| } | |
| /// Returns true if the dialect supports trailing commas in the `FROM` clause of a `SELECT` statement. | |
| /// Example: `SELECT 1 FROM T, U, LIMIT 1` | |
| fn supports_from_trailing_commas(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports trailing commas in the | |
| /// column definitions list of a `CREATE` statement. | |
| /// Example: `CREATE TABLE T (x INT, y TEXT,)` | |
| fn supports_column_definition_trailing_commas(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports double dot notation for object names | |
| /// | |
| /// Example | |
| /// ```sql | |
| /// SELECT * FROM db_name..table_name | |
| /// ``` | |
| fn supports_object_name_double_dot_notation(&self) -> bool { | |
| false | |
| } | |
| /// Return true if the dialect supports the STRUCT literal | |
| /// | |
| /// Example | |
| /// ```sql | |
| /// SELECT STRUCT(1 as one, 'foo' as foo, false) | |
| /// ``` | |
| fn supports_struct_literal(&self) -> bool { | |
| false | |
| } | |
| /// Return true if the dialect supports empty projections in SELECT statements | |
| /// | |
| /// Example | |
| /// ```sql | |
| /// SELECT from table_name | |
| /// ``` | |
| fn supports_empty_projections(&self) -> bool { | |
| false | |
| } | |
| /// Return true if the dialect supports wildcard expansion on | |
| /// arbitrary expressions in projections. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT STRUCT<STRING>('foo').* FROM T | |
| /// ``` | |
| fn supports_select_expr_star(&self) -> bool { | |
| false | |
| } | |
| /// Return true if the dialect supports "FROM-first" selects. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// FROM table | |
| /// SELECT * | |
| /// ``` | |
| fn supports_from_first_select(&self) -> bool { | |
| false | |
| } | |
| /// Return true if the dialect supports "FROM-first" inserts. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// WITH cte AS (SELECT key FROM src) | |
| /// FROM cte | |
| /// INSERT OVERWRITE table my_table | |
| /// SELECT * | |
| /// | |
| /// See <https://hive.apache.org/docs/latest/language/common-table-expression/> | |
| /// ``` | |
| fn supports_from_first_insert(&self) -> bool { | |
| false | |
| } | |
| /// Return true if the dialect supports pipe operator. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * | |
| /// FROM table | |
| /// |> limit 1 | |
| /// ``` | |
| /// | |
| /// See <https://cloud.google.com/bigquery/docs/pipe-syntax-guide#basic_syntax> | |
| fn supports_pipe_operator(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support MySQL-style `'user'@'host'` grantee syntax? | |
| fn supports_user_host_grantee(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support the `MATCH() AGAINST()` syntax? | |
| fn supports_match_against(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports an exclude option | |
| /// following a wildcard in the projection section. For example: | |
| /// `SELECT * EXCLUDE col1 FROM tbl`. | |
| /// | |
| /// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_EXCLUDE_list.html) | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/select) | |
| fn supports_select_wildcard_exclude(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports an exclude option | |
| /// as the last item in the projection section, not necessarily | |
| /// after a wildcard. For example: | |
| /// `SELECT *, c1, c2 EXCLUDE c3 FROM tbl` | |
| /// | |
| /// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_EXCLUDE_list.html) | |
| fn supports_select_exclude(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports specifying multiple options | |
| /// in a `CREATE TABLE` statement for the structure of the new table. For example: | |
| /// `CREATE TABLE t (a INT, b INT) AS SELECT 1 AS b, 2 AS a` | |
| fn supports_create_table_multi_schema_info_sources(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports MySQL-specific SELECT modifiers | |
| /// like `HIGH_PRIORITY`, `STRAIGHT_JOIN`, `SQL_SMALL_RESULT`, etc. | |
| /// | |
| /// For example: | |
| /// ```sql | |
| /// SELECT HIGH_PRIORITY STRAIGHT_JOIN SQL_SMALL_RESULT * FROM t1 JOIN t2 ON ... | |
| /// ``` | |
| /// | |
| /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/select.html) | |
| fn supports_select_modifiers(&self) -> bool { | |
| false | |
| } | |
| /// Dialect-specific infix parser override | |
| /// | |
| /// This method is called to parse the next infix expression. | |
| /// | |
| /// If `None` is returned, falls back to the default behavior. | |
| fn parse_infix( | |
| &self, | |
| _parser: &mut Parser, | |
| _expr: &Expr, | |
| _precedence: u8, | |
| ) -> Option<Result<Expr, ParserError>> { | |
| // return None to fall back to the default behavior | |
| None | |
| } | |
| /// Dialect-specific precedence override | |
| /// | |
| /// This method is called to get the precedence of the next token. | |
| /// | |
| /// If `None` is returned, falls back to the default behavior. | |
| fn get_next_precedence(&self, _parser: &Parser) -> Option<Result<u8, ParserError>> { | |
| // return None to fall back to the default behavior | |
| None | |
| } | |
| /// Get the precedence of the next token, looking at the full token stream. | |
| /// | |
| /// A higher number => higher precedence | |
| /// | |
| /// See [`Self::get_next_precedence`] to override the behavior for just the | |
| /// next token. | |
| /// | |
| /// The default implementation is used for many dialects, but can be | |
| /// overridden to provide dialect-specific behavior. | |
| fn get_next_precedence_default(&self, parser: &Parser) -> Result<u8, ParserError> { | |
| if let Some(precedence) = self.get_next_precedence(parser) { | |
| return precedence; | |
| } | |
| macro_rules! p { | |
| ($precedence:ident) => { | |
| self.prec_value(Precedence::$precedence) | |
| }; | |
| } | |
| let token = parser.peek_token_ref(); | |
| debug!("get_next_precedence_full() {token:?}"); | |
| match &token.token { | |
| Token::Word(w) if w.keyword == Keyword::OR => Ok(p!(Or)), | |
| Token::Word(w) if w.keyword == Keyword::AND => Ok(p!(And)), | |
| Token::Word(w) if w.keyword == Keyword::XOR => Ok(p!(Xor)), | |
| Token::Word(w) if w.keyword == Keyword::AT => { | |
| match ( | |
| &parser.peek_nth_token_ref(1).token, | |
| &parser.peek_nth_token_ref(2).token, | |
| ) { | |
| (Token::Word(w), Token::Word(w2)) | |
| if w.keyword == Keyword::TIME && w2.keyword == Keyword::ZONE => | |
| { | |
| Ok(p!(AtTz)) | |
| } | |
| _ => Ok(self.prec_unknown()), | |
| } | |
| } | |
| Token::Word(w) if w.keyword == Keyword::NOT => { | |
| match &parser.peek_nth_token_ref(1).token { | |
| // The precedence of NOT varies depending on keyword that | |
| // follows it. If it is followed by IN, BETWEEN, or LIKE, | |
| // it takes on the precedence of those tokens. Otherwise, it | |
| // is not an infix operator, and therefore has zero | |
| // precedence. | |
| Token::Word(w) if w.keyword == Keyword::IN => Ok(p!(Between)), | |
| Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(p!(Between)), | |
| Token::Word(w) if w.keyword == Keyword::LIKE => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::RLIKE => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::REGEXP => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::MATCH => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::GLOB => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::MEMBER => Ok(p!(Like)), | |
| Token::Word(w) | |
| if w.keyword == Keyword::NULL && !parser.in_column_definition_state() => | |
| { | |
| Ok(p!(Is)) | |
| } | |
| _ => Ok(self.prec_unknown()), | |
| } | |
| } | |
| Token::Word(w) if w.keyword == Keyword::NOTNULL && self.supports_notnull_operator() => { | |
| Ok(p!(Is)) | |
| } | |
| Token::Word(w) if w.keyword == Keyword::IS => Ok(p!(Is)), | |
| Token::Word(w) if w.keyword == Keyword::GLOBAL && self.supports_global_in() => { | |
| match ( | |
| &parser.peek_nth_token_ref(1).token, | |
| &parser.peek_nth_token_ref(2).token, | |
| ) { | |
| (Token::Word(w), _) if w.keyword == Keyword::IN => Ok(p!(Between)), | |
| (Token::Word(w), Token::Word(w2)) | |
| if w.keyword == Keyword::NOT && w2.keyword == Keyword::IN => | |
| { | |
| Ok(p!(Between)) | |
| } | |
| _ => Ok(self.prec_unknown()), | |
| } | |
| } | |
| Token::Word(w) if w.keyword == Keyword::IN => Ok(p!(Between)), | |
| Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(p!(Between)), | |
| Token::Word(w) if w.keyword == Keyword::OVERLAPS => Ok(p!(Between)), | |
| Token::Word(w) if w.keyword == Keyword::LIKE => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::RLIKE => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::REGEXP => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::MATCH => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::GLOB => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::MEMBER => Ok(p!(Like)), | |
| Token::Word(w) if w.keyword == Keyword::OPERATOR => Ok(p!(Between)), | |
| Token::Word(w) if w.keyword == Keyword::DIV => Ok(p!(MulDivModOp)), | |
| Token::Period => Ok(p!(Period)), | |
| Token::Assignment | |
| | Token::Eq | |
| | Token::Lt | |
| | Token::LtEq | |
| | Token::Neq | |
| | Token::Gt | |
| | Token::GtEq | |
| | Token::DoubleEq | |
| | Token::Tilde | |
| | Token::TildeAsterisk | |
| | Token::ExclamationMarkTilde | |
| | Token::ExclamationMarkTildeAsterisk | |
| | Token::DoubleTilde | |
| | Token::DoubleTildeAsterisk | |
| | Token::ExclamationMarkDoubleTilde | |
| | Token::ExclamationMarkDoubleTildeAsterisk | |
| | Token::Spaceship => Ok(p!(Eq)), | |
| Token::Pipe | |
| | Token::QuestionMarkDash | |
| | Token::DoubleSharp | |
| | Token::Overlap | |
| | Token::AmpersandLeftAngleBracket | |
| | Token::AmpersandRightAngleBracket | |
| | Token::QuestionMarkDashVerticalBar | |
| | Token::AmpersandLeftAngleBracketVerticalBar | |
| | Token::VerticalBarAmpersandRightAngleBracket | |
| | Token::TwoWayArrow | |
| | Token::LeftAngleBracketCaret | |
| | Token::RightAngleBracketCaret | |
| | Token::QuestionMarkSharp | |
| | Token::QuestionMarkDoubleVerticalBar | |
| | Token::QuestionPipe | |
| | Token::TildeEqual | |
| | Token::AtSign | |
| | Token::ShiftLeftVerticalBar | |
| | Token::VerticalBarShiftRight => Ok(p!(Pipe)), | |
| Token::Caret | Token::Sharp | Token::ShiftRight | Token::ShiftLeft => Ok(p!(Caret)), | |
| Token::Ampersand => Ok(p!(Ampersand)), | |
| Token::Plus | Token::Minus => Ok(p!(PlusMinus)), | |
| Token::Mul | Token::Div | Token::DuckIntDiv | Token::Mod | Token::StringConcat => { | |
| Ok(p!(MulDivModOp)) | |
| } | |
| Token::DoubleColon | Token::ExclamationMark | Token::LBracket | Token::CaretAt => { | |
| Ok(p!(DoubleColon)) | |
| } | |
| Token::Colon => match &parser.peek_nth_token_ref(1).token { | |
| // When colon is followed by a string or a number, it's usually in MAP syntax. | |
| Token::SingleQuotedString(_) | Token::Number(_, _) => Ok(self.prec_unknown()), | |
| // In other cases, it's used in semi-structured data traversal like in variant or JSON | |
| // string columns. See `JsonAccess`. | |
| _ => Ok(p!(Colon)), | |
| }, | |
| Token::Arrow | |
| | Token::LongArrow | |
| | Token::HashArrow | |
| | Token::HashLongArrow | |
| | Token::AtArrow | |
| | Token::ArrowAt | |
| | Token::HashMinus | |
| | Token::AtQuestion | |
| | Token::AtAt | |
| | Token::Question | |
| | Token::QuestionAnd | |
| | Token::CustomBinaryOperator(_) => Ok(p!(PgOther)), | |
| _ => Ok(self.prec_unknown()), | |
| } | |
| } | |
| /// Dialect-specific statement parser override | |
| /// | |
| /// This method is called to parse the next statement. | |
| /// | |
| /// If `None` is returned, falls back to the default behavior. | |
| fn parse_statement(&self, _parser: &mut Parser) -> Option<Result<Statement, ParserError>> { | |
| // return None to fall back to the default behavior | |
| None | |
| } | |
| /// Dialect-specific column option parser override | |
| /// | |
| /// This method is called to parse the next column option. | |
| /// | |
| /// If `None` is returned, falls back to the default behavior. | |
| fn parse_column_option( | |
| &self, | |
| _parser: &mut Parser, | |
| ) -> Result<Option<Result<Option<ColumnOption>, ParserError>>, ParserError> { | |
| // return None to fall back to the default behavior | |
| Ok(None) | |
| } | |
| /// Decide the lexical Precedence of operators. | |
| /// | |
| /// Uses (APPROXIMATELY) <https://www.postgresql.org/docs/7.0/operators.htm#AEN2026> as a reference | |
| fn prec_value(&self, prec: Precedence) -> u8 { | |
| match prec { | |
| Precedence::Period => 100, | |
| Precedence::DoubleColon => 50, | |
| Precedence::AtTz => 41, | |
| Precedence::MulDivModOp => 40, | |
| Precedence::PlusMinus => 30, | |
| Precedence::Xor => 24, | |
| Precedence::Ampersand => 23, | |
| Precedence::Caret => 22, | |
| Precedence::Pipe => 21, | |
| Precedence::Colon => 21, | |
| // "any other operator" -- `->`, `@>`, custom operators. PostgreSQL | |
| // places this row above `BETWEEN` / `LIKE` and below `+` / `-` | |
| // (`%left Op OPERATOR RIGHT_ARROW '|'` in gram.y), so it must bind | |
| // more tightly than `IS`, whose right operand would otherwise stop | |
| // short of it. | |
| Precedence::PgOther => 21, | |
| Precedence::Between => 20, | |
| Precedence::Eq => 20, | |
| Precedence::Like => 19, | |
| Precedence::Is => 17, | |
| Precedence::UnaryNot => 15, | |
| Precedence::And => 10, | |
| Precedence::Or => 5, | |
| } | |
| } | |
| /// Returns the precedence when the precedence is otherwise unknown | |
| fn prec_unknown(&self) -> u8 { | |
| 0 | |
| } | |
| /// Returns true if this dialect requires the `TABLE` keyword after `DESCRIBE` | |
| /// | |
| /// Defaults to false. | |
| /// | |
| /// If true, the following statement is valid: `DESCRIBE TABLE my_table` | |
| /// If false, the following statements are valid: `DESCRIBE my_table` and `DESCRIBE table` | |
| fn describe_requires_table_keyword(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect allows the `EXTRACT` function to words other than [`Keyword`]. | |
| fn allow_extract_custom(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect allows the `EXTRACT` function to use single quotes in the part being extracted. | |
| fn allow_extract_single_quotes(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `EXTRACT` function | |
| /// with a comma separator instead of `FROM`. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT EXTRACT(YEAR, date_column) FROM table; | |
| /// ``` | |
| /// | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions/extract) | |
| fn supports_extract_comma_syntax(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports a subquery passed to a function | |
| /// as the only argument without enclosing parentheses. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT FLATTEN(SELECT * FROM tbl); | |
| /// ``` | |
| /// | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions/flatten) | |
| fn supports_subquery_as_function_arg(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `COMMENT` clause in | |
| /// `CREATE VIEW` statements using the `COMMENT = 'comment'` syntax. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// CREATE VIEW v COMMENT = 'my comment' AS SELECT 1; | |
| /// ``` | |
| /// | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-view#optional-parameters) | |
| fn supports_create_view_comment_syntax(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if a data type can carry a collation, including inside a | |
| /// nested type such as `MAP<STRING COLLATE UTF8_BINARY, STRING>`. | |
| fn supports_data_type_collation(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `ARRAY` type without | |
| /// specifying an element type. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// CREATE TABLE t (a ARRAY); | |
| /// ``` | |
| /// | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/data-types-semistructured#array) | |
| fn supports_array_typedef_without_element_type(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `ARRAY(element_type)` syntax. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// CREATE TABLE t (a ARRAY(VARCHAR)); | |
| /// ``` | |
| fn supports_array_typedef_with_parentheses(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports `NOT NULL` on an element type in | |
| /// an `ARRAY(element_type)` definition. | |
| fn supports_array_element_not_null(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `MAP(key_type, value_type)` syntax. | |
| fn supports_map_typedef_with_parentheses(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports `NOT NULL` on the value type in | |
| /// a `MAP(key_type, value_type)` definition. | |
| fn supports_map_value_not_null(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports extra parentheses around | |
| /// lone table names or derived tables in the `FROM` clause. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * FROM (mytable); | |
| /// SELECT * FROM ((SELECT 1)); | |
| /// SELECT * FROM (mytable) AS alias; | |
| /// ``` | |
| /// | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/constructs/from), | |
| /// [SQLite](https://www.sqlite.org/syntax/table-or-subquery.html) | |
| fn supports_parens_around_table_factor(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports `VALUES` as a table factor | |
| /// without requiring parentheses around the entire clause. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * FROM VALUES (1, 'a'), (2, 'b') AS t (col1, col2); | |
| /// ``` | |
| /// | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/constructs/values) | |
| /// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-values.html) | |
| fn supports_values_as_table_factor(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect allows dollar placeholders | |
| /// e.g. `SELECT $var` (SQLite) | |
| fn supports_dollar_placeholder(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports `$` as a prefix for money literals | |
| /// e.g. `SELECT $123.45` (SQL Server) | |
| fn supports_dollar_as_money_prefix(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support with clause in create index statement? | |
| /// e.g. `CREATE INDEX idx ON t WITH (key = value, key2)` | |
| fn supports_create_index_with_clause(&self) -> bool { | |
| false | |
| } | |
| /// Whether `INTERVAL` expressions require units (called "qualifiers" in the ANSI SQL spec) to be specified, | |
| /// e.g. `INTERVAL 1 DAY` vs `INTERVAL 1`. | |
| /// | |
| /// Expressions within intervals (e.g. `INTERVAL '1' + '1' DAY`) are only allowed when units are required. | |
| /// | |
| /// See <https://github.com/sqlparser-rs/sqlparser-rs/pull/1398> for more information. | |
| /// | |
| /// When `true`: | |
| /// * `INTERVAL '1' DAY` is VALID | |
| /// * `INTERVAL 1 + 1 DAY` is VALID | |
| /// * `INTERVAL '1' + '1' DAY` is VALID | |
| /// * `INTERVAL '1'` is INVALID | |
| /// | |
| /// When `false`: | |
| /// * `INTERVAL '1'` is VALID | |
| /// * `INTERVAL '1' DAY` is VALID — unit is not required, but still allowed | |
| /// * `INTERVAL 1 + 1 DAY` is INVALID | |
| fn require_interval_qualifier(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `EXPLAIN` statements with utility options | |
| /// e.g. `EXPLAIN (ANALYZE TRUE, BUFFERS TRUE) SELECT * FROM tbl;` | |
| fn supports_explain_with_utility_options(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `ASC` and `DESC` in column definitions | |
| /// e.g. `CREATE TABLE t (a INT ASC, b INT DESC);` | |
| fn supports_asc_desc_in_column_definition(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect allows string literals as column names in `CREATE TABLE`. | |
| /// SQLite's grammar rule `nm ::= id | STRING | JOIN_KW` permits this. | |
| fn supports_string_literal_column_names(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `a!` expressions | |
| fn supports_factorial_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `<<` and `>>` shift operators. | |
| fn supports_bitwise_shift_operators(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports nested comments | |
| /// e.g. `/* /* nested */ */` | |
| fn supports_nested_comments(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `#` as the start of a single line comment, | |
| /// e.g. `SELECT 1 # comment` | |
| fn supports_hash_comment(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports inline data for | |
| /// `COPY ... FROM STDIN`, captured verbatim by the tokenizer. | |
| fn supports_copy_from_stdin_data(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if `#` starts a single line comment only when followed by an ASCII | |
| /// space or `!`, so `# comment` and `#! comment` are comments but `#comment` is not. | |
| fn requires_hash_comment_space(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports optimizer hints in multiline comments | |
| /// e.g. `/*!50110 KEY_BLOCK_SIZE = 1024*/` | |
| fn supports_multiline_comment_hints(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect accepts an unterminated block comment at end of input | |
| /// e.g. `SELECT 1 /* comment` | |
| /// | |
| /// [SQLite](https://www.sqlite.org/lang_comment.html) | |
| fn supports_unterminated_block_comments(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports treating the equals operator `=` within a `SelectItem` | |
| /// as an alias assignment operator, rather than a boolean expression. | |
| /// For example: the following statements are equivalent for such a dialect: | |
| /// ```sql | |
| /// SELECT col_alias = col FROM tbl; | |
| /// SELECT col_alias AS col FROM tbl; | |
| /// ``` | |
| fn supports_eq_alias_assignment(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `TRY_CONVERT` function | |
| fn supports_try_convert(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `!a` syntax for boolean `NOT` expressions. | |
| fn supports_bang_not_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `LISTEN`, `UNLISTEN` and `NOTIFY` statements | |
| fn supports_listen_notify(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `EXCLUDE` table constraints, e.g. | |
| /// `EXCLUDE USING gist (c WITH &&)` in `CREATE TABLE`/`ALTER TABLE`. | |
| /// See <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-EXCLUDE>. | |
| fn supports_exclude_constraint(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `LOAD DATA` statement | |
| fn supports_load_data(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `LOAD extension` statement | |
| fn supports_load_extension(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect expects the `TOP` option | |
| /// before the `ALL`/`DISTINCT` options in a `SELECT` statement. | |
| fn supports_top_before_distinct(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports boolean literals (`true` and `false`). | |
| /// For example, in MSSQL these are treated as identifiers rather than boolean literals. | |
| fn supports_boolean_literals(&self) -> bool { | |
| true | |
| } | |
| /// Returns true if this dialect supports the `LIKE 'pattern'` option in | |
| /// a `SHOW` statement before the `IN` option | |
| fn supports_show_like_before_in(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `COMMENT` statement | |
| fn supports_comment_on(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `CREATE TABLE SELECT` statement | |
| fn supports_create_table_select(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports `PROC` as an abbreviation for | |
| /// `PROCEDURE` in a `CREATE` statement. | |
| fn supports_create_proc_syntax(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect accepts a comma-separated list of table-level | |
| /// options placed between the table name and the column-list parenthesis, e.g. | |
| /// | |
| /// ```sql | |
| /// CREATE TABLE foo, NO FALLBACK, NO BEFORE JOURNAL (col INTEGER) | |
| /// ``` | |
| fn supports_leading_comma_before_table_options(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports PartiQL for querying semi-structured data | |
| /// <https://partiql.org/index.html> | |
| fn supports_partiql(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports object-unpivot table factors in the FROM clause. | |
| /// | |
| /// Syntax: | |
| /// ```sql | |
| /// SELECT * FROM T UNPIVOT expression AS value_alias [AT attribute_alias] | |
| /// ``` | |
| fn supports_unpivot_expr(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `CONSTRAINT` keyword without a name | |
| /// in table constraint definitions. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// CREATE TABLE t (a INT, CONSTRAINT CHECK (a > 0)) | |
| /// ``` | |
| /// | |
| /// This is a MySQL extension; the SQL standard requires a name after `CONSTRAINT`. | |
| /// When the name is omitted, the output normalizes to just the constraint type | |
| /// without the `CONSTRAINT` keyword (e.g., `CHECK (a > 0)`). | |
| /// | |
| /// <https://dev.mysql.com/doc/refman/8.4/en/create-table.html> | |
| fn supports_constraint_keyword_without_name(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `KEY` keyword as part of | |
| /// column-level constraints in a `CREATE TABLE` statement. | |
| /// | |
| /// When enabled, the parser accepts these MySQL-specific column options: | |
| /// - `UNIQUE [KEY]` — optional `KEY` after `UNIQUE` | |
| /// - `[PRIMARY] KEY` — standalone `KEY` as shorthand for `PRIMARY KEY` | |
| /// | |
| /// <https://dev.mysql.com/doc/refman/8.4/en/create-table.html> | |
| fn supports_key_column_option(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the specified keyword is reserved and cannot be | |
| /// used as an identifier without special handling like quoting. | |
| fn is_reserved_for_identifier(&self, kw: Keyword) -> bool { | |
| keywords::RESERVED_FOR_IDENTIFIER.contains(&kw) | |
| } | |
| /// Returns reserved keywords that may prefix a select item expression | |
| /// e.g. `SELECT CONNECT_BY_ROOT name FROM Tbl2` (Snowflake) | |
| fn get_reserved_keywords_for_select_item_operator(&self) -> &[Keyword] { | |
| &[] | |
| } | |
| /// Returns grantee types that should be treated as identifiers | |
| fn get_reserved_grantees_types(&self) -> &[GranteesType] { | |
| &[] | |
| } | |
| /// Returns true if this dialect supports the `TABLESAMPLE` option | |
| /// before the table alias option. For example: | |
| /// | |
| /// Table sample before alias: `SELECT * FROM tbl AS t TABLESAMPLE (10)` | |
| /// Table sample after alias: `SELECT * FROM tbl TABLESAMPLE (10) AS t` | |
| /// | |
| /// <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#_7_6_table_reference> | |
| fn supports_table_sample_before_alias(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `INSERT INTO ... SET col1 = 1, ...` syntax. | |
| /// | |
| /// MySQL: <https://dev.mysql.com/doc/refman/8.4/en/insert.html> | |
| fn supports_insert_set(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support table function in insertion? | |
| fn supports_insert_table_function(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support table queries in insertion? | |
| /// | |
| /// e.g. `SELECT INTO (<query>) ...` | |
| fn supports_insert_table_query(&self) -> bool { | |
| false | |
| } | |
| /// Does the dialect support insert formats, e.g. `INSERT INTO ... FORMAT <format>` | |
| fn supports_insert_format(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports `INSERT INTO t [[AS] alias] ...`. | |
| fn supports_insert_table_alias(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect accepts `VALUE` as a synonym of `VALUES`. | |
| /// | |
| /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/insert.html) | |
| fn supports_singular_value_keyword(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports `SET` statements without an explicit | |
| /// assignment operator such as `=`. For example: `SET SHOWPLAN_XML ON`. | |
| fn supports_set_stmt_without_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the specified keyword should be parsed as a column identifier. | |
| /// See [keywords::RESERVED_FOR_COLUMN_ALIAS] | |
| fn is_column_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool { | |
| !keywords::RESERVED_FOR_COLUMN_ALIAS.contains(kw) | |
| } | |
| /// Returns true if the specified keyword should be parsed as a select item alias. | |
| /// When explicit is true, the keyword is preceded by an `AS` word. Parser is provided | |
| /// to enable looking ahead if needed. | |
| fn is_select_item_alias(&self, explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool { | |
| explicit || self.is_column_alias(kw, parser) | |
| } | |
| /// Returns true if `OVER` without a following `(` or plain-identifier window name | |
| /// may be used as an implicit column alias rather than starting a window clause. | |
| /// | |
| /// SQLite reads `OVER` as a keyword only when `(` or a name follows, see | |
| /// `analyzeOverKeyword` in [tokenize.c](https://sqlite.org/src/file?name=src/tokenize.c) | |
| fn supports_over_keyword_as_column_alias(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the specified keyword should be parsed as a table factor identifier. | |
| /// See [keywords::RESERVED_FOR_TABLE_FACTOR] | |
| fn is_table_factor(&self, kw: &Keyword, _parser: &mut Parser) -> bool { | |
| !keywords::RESERVED_FOR_TABLE_FACTOR.contains(kw) | |
| } | |
| /// Returns true if the specified keyword should be parsed as a table factor alias. | |
| /// See [keywords::RESERVED_FOR_TABLE_ALIAS] | |
| fn is_table_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool { | |
| !keywords::RESERVED_FOR_TABLE_ALIAS.contains(kw) | |
| } | |
| /// Returns true if the specified keyword should be parsed as a table factor alias. | |
| /// When explicit is true, the keyword is preceded by an `AS` word. Parser is provided | |
| /// to enable looking ahead if needed. | |
| fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool { | |
| explicit || self.is_table_alias(kw, parser) | |
| } | |
| /// Returns true if a single-quoted string can name a select item, as in `SELECT 1 AS 'x'`. | |
| /// `explicit` is true when the alias follows `AS`. | |
| /// | |
| /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/problems-with-alias.html), | |
| /// [SQLite](https://www.sqlite.org/lang_keywords.html) | |
| fn supports_string_literal_column_alias(&self, _explicit: bool) -> bool { | |
| true | |
| } | |
| /// Returns true if a single-quoted string can name a table, as in `FROM t AS 'x'`. | |
| /// `explicit` is true when the alias follows `AS`. | |
| /// | |
| /// [SQLite](https://www.sqlite.org/lang_keywords.html) | |
| fn supports_string_literal_table_alias(&self, _explicit: bool) -> bool { | |
| true | |
| } | |
| /// Returns true if this dialect supports querying historical table data | |
| /// by specifying which version of the data to query. | |
| fn supports_table_versioning(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the E'...' syntax for string literals | |
| /// | |
| /// Postgres: <https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE> | |
| fn supports_string_escape_constant(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the table hints in the `FROM` clause. | |
| fn supports_table_hints(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports SQLite's `INDEXED BY` and `NOT INDEXED` index-selection clauses. | |
| /// | |
| /// See <https://www.sqlite.org/lang_indexedby.html> | |
| fn supports_indexed_by(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect requires a whitespace character after `--` to start a single line comment. | |
| /// | |
| /// MySQL: <https://dev.mysql.com/doc/refman/8.4/en/ansi-diff-comments.html> | |
| /// e.g. UPDATE account SET balance=balance--1 | |
| // WHERE account_id=5752 ^^^ will be interpreted as two minus signs instead of a comment | |
| fn requires_single_line_comment_whitespace(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports array type definition with brackets with | |
| /// an optional size. For example: | |
| /// ```CREATE TABLE my_table (arr1 INT[], arr2 INT[3])``` | |
| /// ```SELECT x::INT[]``` | |
| fn supports_array_typedef_with_brackets(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports geometric types. | |
| /// | |
| /// Postgres: <https://www.postgresql.org/docs/9.5/functions-geometry.html> | |
| /// e.g. @@ circle '((0,0),10)' | |
| fn supports_geometric_types(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `ORDER BY ALL`. | |
| /// `ALL` which means all columns of the SELECT clause. | |
| /// | |
| /// For example: ```SELECT * FROM addresses ORDER BY ALL;```. | |
| fn supports_order_by_all(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports PostgreSQL-style ordering operators: | |
| /// `ORDER BY expr USING <operator>`. | |
| /// | |
| /// For example: `SELECT * FROM t ORDER BY a USING <`. | |
| fn supports_order_by_using_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `SET NAMES <charset_name> [COLLATE <collation_name>]`. | |
| /// | |
| /// - [MySQL](https://dev.mysql.com/doc/refman/8.4/en/set-names.html) | |
| /// - [PostgreSQL](https://www.postgresql.org/docs/17/sql-set.html) | |
| /// | |
| /// Note: Postgres doesn't support the `COLLATE` clause, but we permissively parse it anyway. | |
| fn supports_set_names(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports space-separated column options | |
| /// in a `CREATE TABLE` statement. For example: | |
| /// ```sql | |
| /// CREATE TABLE tbl ( | |
| /// col INT NOT NULL DEFAULT 0 | |
| /// ); | |
| /// ``` | |
| fn supports_space_separated_column_options(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `USING` clause in an `ALTER COLUMN` statement. | |
| /// Example: | |
| /// ```sql | |
| /// ALTER TABLE tbl ALTER COLUMN col SET DATA TYPE <type> USING <exp>` | |
| /// ``` | |
| fn supports_alter_column_type_using(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `ALTER TABLE tbl DROP COLUMN c1, ..., cn` | |
| fn supports_comma_separated_drop_column_list(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports a `FIRST` or `AFTER col` column | |
| /// position in `ALTER TABLE ... ADD | CHANGE | MODIFY COLUMN`. | |
| /// Example: | |
| /// ```sql | |
| /// ALTER TABLE tbl ADD COLUMN c INT AFTER b | |
| /// ``` | |
| fn supports_alter_column_position(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `ALTER TABLE ... ADD [COLUMN] IF NOT EXISTS <col>`. | |
| /// Example: | |
| /// ```sql | |
| /// ALTER TABLE tbl ADD COLUMN IF NOT EXISTS c INT | |
| /// ``` | |
| fn supports_add_column_if_not_exists(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect considers the specified ident as a function | |
| /// that returns an identifier. Typically used to generate identifiers | |
| /// programmatically. | |
| /// | |
| /// - [Snowflake](https://docs.snowflake.com/en/sql-reference/identifier-literal) | |
| fn is_identifier_generating_function_name( | |
| &self, | |
| _ident: &Ident, | |
| _name_parts: &[ObjectNamePart], | |
| ) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `x NOTNULL` | |
| /// operator expression. | |
| fn supports_notnull_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect allows an optional `SIGNED` suffix after integer data types. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// CREATE TABLE t (i INT(20) SIGNED); | |
| /// ``` | |
| /// | |
| /// Note that this is canonicalized to `INT(20)`. | |
| fn supports_data_type_signed_suffix(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect allows an optional `ZEROFILL` suffix after numeric data types. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// CREATE TABLE t (i INT(10) UNSIGNED ZEROFILL); | |
| /// ``` | |
| /// | |
| /// <https://dev.mysql.com/doc/refman/8.4/en/numeric-type-syntax.html> | |
| fn supports_data_type_zerofill_suffix(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `INTERVAL` data type with [Postgres]-style options. | |
| /// | |
| /// Examples: | |
| /// ```sql | |
| /// CREATE TABLE t (i INTERVAL YEAR TO MONTH); | |
| /// SELECT '1 second'::INTERVAL HOUR TO SECOND(3); | |
| /// ``` | |
| /// | |
| /// See [`crate::ast::DataType::Interval`] and [`crate::ast::IntervalFields`]. | |
| /// | |
| /// [Postgres]: https://www.postgresql.org/docs/17/datatype-datetime.html | |
| fn supports_interval_options(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports specifying which table to copy | |
| /// the schema from inside parenthesis. | |
| /// | |
| /// Not parenthesized: | |
| /// '''sql | |
| /// CREATE TABLE new LIKE old ... | |
| /// ''' | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-table#label-create-table-like) | |
| /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_table_like) | |
| /// | |
| /// Parenthesized: | |
| /// '''sql | |
| /// CREATE TABLE new (LIKE old ...) | |
| /// ''' | |
| /// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html) | |
| fn supports_create_table_like_parenthesized(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `SEMANTIC_VIEW()` table functions. | |
| /// | |
| /// ```sql | |
| /// SELECT * FROM SEMANTIC_VIEW( | |
| /// model_name | |
| /// DIMENSIONS customer.name, customer.region | |
| /// METRICS orders.revenue, orders.count | |
| /// WHERE customer.active = true | |
| /// ) | |
| /// ``` | |
| fn supports_semantic_view_table_factor(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports Snowflake-style stages in table factors (e.g. `@stage`). | |
| /// | |
| /// [Snowflake](https://docs.snowflake.com/en/user-guide/querying-stage) | |
| fn supports_stages(&self) -> bool { | |
| false | |
| } | |
| /// Support quote delimited string literals, e.g. `Q'{...}'` | |
| /// | |
| /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA) | |
| fn supports_quote_delimited_string(&self) -> bool { | |
| false | |
| } | |
| /// Returns `true` if the dialect supports query optimizer hints in the | |
| /// format of single and multi line comments immediately following a | |
| /// `SELECT`, `INSERT`, `REPLACE`, `DELETE`, or `MERGE` keyword. | |
| /// | |
| /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html) | |
| /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Comments.html#SQLRF-GUID-D316D545-89E2-4D54-977F-FC97815CD62E) | |
| fn supports_comment_optimizer_hint(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect considers the `&&` operator as a boolean AND operator. | |
| fn supports_double_ampersand_operator(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports casting an expression to a binary type | |
| /// using the `BINARY <expr>` syntax. | |
| fn supports_binary_kw_as_cast(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `REPLACE` option in a | |
| /// `SELECT *` wildcard expression. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * REPLACE (col1 AS col1_alias) FROM table; | |
| /// ``` | |
| /// | |
| /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select_replace) | |
| /// [ClickHouse](https://clickhouse.com/docs/sql-reference/statements/select#replace) | |
| /// [DuckDB](https://duckdb.org/docs/sql/query_syntax/select#replace-clause) | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/select#parameters) | |
| fn supports_select_wildcard_replace(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `ILIKE` option in a | |
| /// `SELECT *` wildcard expression. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * ILIKE '%pattern%' FROM table; | |
| /// ``` | |
| /// | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/select#parameters) | |
| fn supports_select_wildcard_ilike(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `RENAME` option in a | |
| /// `SELECT *` wildcard expression. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * RENAME col1 AS col1_alias FROM table; | |
| /// ``` | |
| /// | |
| /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/select#parameters) | |
| fn supports_select_wildcard_rename(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports aliasing a wildcard select item. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT t.* alias FROM t | |
| /// SELECT t.* AS alias FROM t | |
| /// ``` | |
| fn supports_select_wildcard_with_alias(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `OPTIMIZE TABLE` statement. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// OPTIMIZE TABLE table_name; | |
| /// ``` | |
| /// | |
| /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize) | |
| fn supports_optimize_table(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `INSTALL` statement. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// INSTALL extension_name; | |
| /// ``` | |
| /// | |
| /// [DuckDB](https://duckdb.org/docs/extensions/overview) | |
| fn supports_install(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `DETACH` statement. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// DETACH DATABASE db_name; | |
| /// ``` | |
| /// | |
| /// [DuckDB](https://duckdb.org/docs/sql/statements/attach#detach-syntax) | |
| fn supports_detach(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `PREWHERE` clause | |
| /// in `SELECT` statements. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * FROM table PREWHERE col > 0 WHERE col < 100; | |
| /// ``` | |
| /// | |
| /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select/prewhere) | |
| fn supports_prewhere(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `WITH FILL` clause | |
| /// in `ORDER BY` expressions. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * FROM table ORDER BY col WITH FILL FROM 1 TO 10 STEP 1; | |
| /// ``` | |
| /// | |
| /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier) | |
| fn supports_with_fill(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `LIMIT BY` clause. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * FROM table LIMIT 10 BY col; | |
| /// ``` | |
| /// | |
| /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select/limit-by) | |
| fn supports_limit_by(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `INTERPOLATE` clause | |
| /// in `ORDER BY` expressions. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * FROM table ORDER BY col WITH FILL INTERPOLATE (col2 AS col2 + 1); | |
| /// ``` | |
| /// | |
| /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier) | |
| fn supports_interpolate(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `SETTINGS` clause. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * FROM table SETTINGS max_threads = 4; | |
| /// ``` | |
| /// | |
| /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select#settings-in-select-query) | |
| fn supports_settings(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `PARTITION` clause on a table factor, | |
| /// restricting a query to an explicit list of partitions. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * FROM employees PARTITION (p0, p1) | |
| /// ``` | |
| /// | |
| /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/partitioning-selection.html) | |
| fn supports_table_partitions(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if this dialect supports the `FORMAT` clause in `SELECT` statements. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT * FROM table FORMAT JSON; | |
| /// ``` | |
| /// | |
| /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select/format) | |
| fn supports_select_format(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the two-argument comma-separated | |
| /// form of the `TRIM` function: `TRIM(expr, characters)`. | |
| fn supports_comma_separated_trim(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports the `AS` keyword being | |
| /// optional in a CTE definition. For example: | |
| /// ```sql | |
| /// WITH cte_name (SELECT ...) | |
| /// ``` | |
| /// | |
| /// [Databricks](https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-qry-select-cte) | |
| fn supports_cte_without_as(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports parenthesized multi-column | |
| /// aliases in SELECT items. For example: | |
| /// ```sql | |
| /// SELECT stack(2, 'a', 'b') AS (col1, col2) | |
| /// ``` | |
| /// | |
| /// [Spark SQL](https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select.html) | |
| fn supports_select_item_multi_column_alias(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports XML-related expressions | |
| /// such as `xml '<foo/>'` typed strings, XML functions like | |
| /// `XMLCONCAT`, `XMLELEMENT`, etc. | |
| /// | |
| /// When this returns false, `xml` is treated as a regular identifier. | |
| /// | |
| /// [PostgreSQL](https://www.postgresql.org/docs/current/functions-xml.html) | |
| fn supports_xml_expressions(&self) -> bool { | |
| false | |
| } | |
| /// Whether a custom type name may introduce a typed string. | |
| fn is_custom_type_typed_string(&self, _first_keyword: Keyword, _qualified: bool) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports aliased function arguments, | |
| /// e.g. `XMLFOREST(a AS x)` in PostgreSQL. | |
| fn supports_aliased_function_args(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `USING <format>` in `CREATE TABLE`. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// CREATE TABLE t (i INT) USING PARQUET | |
| /// ``` | |
| /// | |
| /// [Spark SQL](https://spark.apache.org/docs/latest/sql-ref-syntax-ddl-create-table-datasource.html) | |
| fn supports_create_table_using(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect treats `LONG` as an alias for `BIGINT`. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// CREATE TABLE t (id LONG) | |
| /// ``` | |
| /// | |
| /// [Spark SQL](https://spark.apache.org/docs/latest/sql-ref-datatypes.html) | |
| fn supports_long_type_as_bigint(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `MAP<K, V>` angle-bracket syntax for the MAP data type. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// CREATE TABLE t (m MAP<STRING, INT>) | |
| /// ``` | |
| /// | |
| /// [Spark SQL](https://spark.apache.org/docs/latest/sql-ref-datatypes.html) | |
| fn supports_map_literal_with_angle_brackets(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect accepts `==` as an alternative to `=` in `UPDATE SET` assignments. | |
| /// | |
| /// See <https://www.sqlite.org/lang_update.html> | |
| fn supports_double_eq_assignment(&self) -> bool { | |
| false | |
| } | |
| /// Returns true if the dialect supports `CAST(expr AS)` with an empty type name. | |
| /// | |
| /// Example: | |
| /// ```sql | |
| /// SELECT CAST(a AS) | |
| /// ``` | |
| /// | |
| /// [SQLite](https://www.sqlite.org/lang_expr.html) | |
| fn supports_cast_empty_data_type_to_unspecified(&self) -> bool { | |
| false | |
| } | |
| } | |
| /// Operators for which precedence must be defined. | |
| /// | |
| /// Higher number -> higher precedence. | |
| /// See expression parsing for how these values are used. | |
| #[derive(Debug, Clone, Copy)] | |
| pub enum Precedence { | |
| /// Member access operator `.` (highest precedence). | |
| Period, | |
| /// Postgres style type cast `::`. | |
| DoubleColon, | |
| /// Timezone operator (e.g. `AT TIME ZONE`). | |
| AtTz, | |
| /// Multiplication / Division / Modulo operators (`*`, `/`, `%`). | |
| MulDivModOp, | |
| /// Addition / Subtraction (`+`, `-`). | |
| PlusMinus, | |
| /// Bitwise `XOR` operator (`^`). | |
| Xor, | |
| /// Bitwise `AND` operator (`&`). | |
| Ampersand, | |
| /// Bitwise `CARET` (^) for some dialects. | |
| Caret, | |
| /// Bitwise `OR` / pipe operator (`|`). | |
| Pipe, | |
| /// `:` operator for json/variant access. | |
| Colon, | |
| /// `BETWEEN` operator. | |
| Between, | |
| /// Equality operator (`=`). | |
| Eq, | |
| /// Pattern matching (`LIKE`). | |
| Like, | |
| /// `IS` operator (e.g. `IS NULL`). | |
| Is, | |
| /// Other Postgres-specific operators. | |
| PgOther, | |
| /// Unary `NOT`. | |
| UnaryNot, | |
| /// Logical `AND`. | |
| And, | |
| /// Logical `OR` (lowest precedence). | |
| Or, | |
| } | |
| impl dyn Dialect { | |
| /// Returns true if `self` is the concrete dialect `T`. | |
| #[inline] | |
| pub fn is<T: Dialect>(&self) -> bool { | |
| // borrowed from `Any` implementation | |
| TypeId::of::<T>() == self.dialect() | |
| } | |
| } | |
| /// Returns the built in [`Dialect`] corresponding to `dialect_name`. | |
| /// | |
| /// See [`Dialect`] documentation for an example. | |
| pub fn dialect_from_str(dialect_name: impl AsRef<str>) -> Option<Box<dyn Dialect>> { | |
| let dialect_name = dialect_name.as_ref(); | |
| match dialect_name.to_lowercase().as_str() { | |
| "generic" => Some(Box::new(GenericDialect)), | |
| "mysql" => Some(Box::new(MySqlDialect {})), | |
| "postgresql" | "postgres" => Some(Box::new(PostgreSqlDialect {})), | |
| "hive" => Some(Box::new(HiveDialect {})), | |
| "sqlite" => Some(Box::new(SQLiteDialect {})), | |
| "snowflake" => Some(Box::new(SnowflakeDialect)), | |
| "redshift" => Some(Box::new(RedshiftSqlDialect {})), | |
| "mssql" => Some(Box::new(MsSqlDialect {})), | |
| "clickhouse" => Some(Box::new(ClickHouseDialect {})), | |
| "bigquery" => Some(Box::new(BigQueryDialect)), | |
| "ansi" => Some(Box::new(AnsiDialect {})), | |
| "duckdb" => Some(Box::new(DuckDbDialect {})), | |
| "databricks" => Some(Box::new(DatabricksDialect {})), | |
| "spark" | "sparksql" => Some(Box::new(SparkSqlDialect {})), | |
| "oracle" => Some(Box::new(OracleDialect {})), | |
| "teradata" => Some(Box::new(TeradataDialect {})), | |
| _ => None, | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| struct DialectHolder<'a> { | |
| dialect: &'a dyn Dialect, | |
| } | |
| #[test] | |
| fn test_is_dialect() { | |
| let generic_dialect: &dyn Dialect = &GenericDialect {}; | |
| let ansi_dialect: &dyn Dialect = &AnsiDialect {}; | |
| let generic_holder = DialectHolder { | |
| dialect: generic_dialect, | |
| }; | |
| let ansi_holder = DialectHolder { | |
| dialect: ansi_dialect, | |
| }; | |
| assert!(dialect_of!(generic_holder is GenericDialect | AnsiDialect),); | |
| assert!(!dialect_of!(generic_holder is AnsiDialect)); | |
| assert!(dialect_of!(ansi_holder is AnsiDialect)); | |
| assert!(dialect_of!(ansi_holder is GenericDialect | AnsiDialect)); | |
| assert!(!dialect_of!(ansi_holder is GenericDialect | MsSqlDialect)); | |
| } | |
| #[test] | |
| fn test_dialect_from_str() { | |
| assert!(parse_dialect("generic").is::<GenericDialect>()); | |
| assert!(parse_dialect("mysql").is::<MySqlDialect>()); | |
| assert!(parse_dialect("MySql").is::<MySqlDialect>()); | |
| assert!(parse_dialect("postgresql").is::<PostgreSqlDialect>()); | |
| assert!(parse_dialect("postgres").is::<PostgreSqlDialect>()); | |
| assert!(parse_dialect("hive").is::<HiveDialect>()); | |
| assert!(parse_dialect("sqlite").is::<SQLiteDialect>()); | |
| assert!(parse_dialect("snowflake").is::<SnowflakeDialect>()); | |
| assert!(parse_dialect("SnowFlake").is::<SnowflakeDialect>()); | |
| assert!(parse_dialect("MsSql").is::<MsSqlDialect>()); | |
| assert!(parse_dialect("clickhouse").is::<ClickHouseDialect>()); | |
| assert!(parse_dialect("ClickHouse").is::<ClickHouseDialect>()); | |
| assert!(parse_dialect("bigquery").is::<BigQueryDialect>()); | |
| assert!(parse_dialect("BigQuery").is::<BigQueryDialect>()); | |
| assert!(parse_dialect("ansi").is::<AnsiDialect>()); | |
| assert!(parse_dialect("ANSI").is::<AnsiDialect>()); | |
| assert!(parse_dialect("duckdb").is::<DuckDbDialect>()); | |
| assert!(parse_dialect("DuckDb").is::<DuckDbDialect>()); | |
| assert!(parse_dialect("DataBricks").is::<DatabricksDialect>()); | |
| assert!(parse_dialect("databricks").is::<DatabricksDialect>()); | |
| assert!(parse_dialect("teradata").is::<TeradataDialect>()); | |
| assert!(parse_dialect("Teradata").is::<TeradataDialect>()); | |
| // error cases | |
| assert!(dialect_from_str("Unknown").is_none()); | |
| assert!(dialect_from_str("").is_none()); | |
| } | |
| fn parse_dialect(v: &str) -> Box<dyn Dialect> { | |
| dialect_from_str(v).unwrap() | |
| } | |
| #[test] | |
| #[cfg(feature = "derive-dialect")] | |
| fn test_dialect_override() { | |
| derive_dialect!(EnhancedGenericDialect, GenericDialect, | |
| preserve_type_id = true, | |
| overrides = { | |
| supports_order_by_all = true, | |
| supports_nested_comments = true, | |
| supports_triple_quoted_string = true, | |
| }, | |
| ); | |
| let dialect = EnhancedGenericDialect::new(); | |
| assert!(dialect.supports_order_by_all()); | |
| assert!(dialect.supports_nested_comments()); | |
| assert!(dialect.supports_triple_quoted_string()); | |
| let d: &dyn Dialect = &dialect; | |
| assert!(d.is::<GenericDialect>()); | |
| } | |
| #[test] | |
| fn identifier_quote_style() { | |
| let tests: Vec<(&dyn Dialect, &str, Option<char>)> = vec![ | |
| (&AnsiDialect {}, "id", Some('"')), | |
| (&BigQueryDialect {}, "id", Some('`')), | |
| (&ClickHouseDialect {}, "id", Some('`')), | |
| (&DatabricksDialect {}, "id", Some('`')), | |
| (&DuckDbDialect {}, "id", Some('"')), | |
| (&GenericDialect {}, "id", None), | |
| (&HiveDialect {}, "id", Some('`')), | |
| (&MsSqlDialect {}, "id", Some('[')), | |
| (&MySqlDialect {}, "id", Some('`')), | |
| (&OracleDialect {}, "id", Some('"')), | |
| (&PostgreSqlDialect {}, "id", Some('"')), | |
| (&RedshiftSqlDialect {}, "id", Some('"')), | |
| (&SnowflakeDialect {}, "id", Some('"')), | |
| (&SQLiteDialect {}, "id", Some('`')), | |
| (&SparkSqlDialect {}, "id", Some('`')), | |
| (&TeradataDialect {}, "id", Some('"')), | |
| ]; | |
| for (dialect, ident, expected) in tests { | |
| let actual = dialect.identifier_quote_style(ident); | |
| assert_eq!(actual, expected); | |
| } | |
| } | |
| #[test] | |
| fn parse_with_wrapped_dialect() { | |
| /// Wrapper for a dialect. In a real-world example, this wrapper | |
| /// would tweak the behavior of the dialect. For the test case, | |
| /// it wraps all methods unaltered. | |
| #[derive(Debug)] | |
| struct WrappedDialect(MySqlDialect); | |
| impl Dialect for WrappedDialect { | |
| fn dialect(&self) -> std::any::TypeId { | |
| self.0.dialect() | |
| } | |
| fn is_identifier_start(&self, ch: char) -> bool { | |
| self.0.is_identifier_start(ch) | |
| } | |
| fn is_delimited_identifier_start(&self, ch: char) -> bool { | |
| self.0.is_delimited_identifier_start(ch) | |
| } | |
| fn is_nested_delimited_identifier_start(&self, ch: char) -> bool { | |
| self.0.is_nested_delimited_identifier_start(ch) | |
| } | |
| fn peek_nested_delimited_identifier_quotes( | |
| &self, | |
| chars: std::iter::Peekable<std::str::Chars<'_>>, | |
| ) -> Option<(char, Option<char>)> { | |
| self.0.peek_nested_delimited_identifier_quotes(chars) | |
| } | |
| fn identifier_quote_style(&self, identifier: &str) -> Option<char> { | |
| self.0.identifier_quote_style(identifier) | |
| } | |
| fn supports_string_literal_backslash_escape(&self) -> bool { | |
| self.0.supports_string_literal_backslash_escape() | |
| } | |
| fn supports_filter_during_aggregation(&self) -> bool { | |
| self.0.supports_filter_during_aggregation() | |
| } | |
| fn supports_within_after_array_aggregation(&self) -> bool { | |
| self.0.supports_within_after_array_aggregation() | |
| } | |
| fn supports_group_by_expr(&self) -> bool { | |
| self.0.supports_group_by_expr() | |
| } | |
| fn supports_in_empty_list(&self) -> bool { | |
| self.0.supports_in_empty_list() | |
| } | |
| fn supports_in_unparenthesized_expr(&self) -> bool { | |
| self.0.supports_in_unparenthesized_expr() | |
| } | |
| fn convert_type_before_value(&self) -> bool { | |
| self.0.convert_type_before_value() | |
| } | |
| fn parse_prefix( | |
| &self, | |
| parser: &mut sqlparser::parser::Parser, | |
| ) -> Option<Result<Expr, sqlparser::parser::ParserError>> { | |
| self.0.parse_prefix(parser) | |
| } | |
| fn parse_infix( | |
| &self, | |
| parser: &mut sqlparser::parser::Parser, | |
| expr: &Expr, | |
| precedence: u8, | |
| ) -> Option<Result<Expr, sqlparser::parser::ParserError>> { | |
| self.0.parse_infix(parser, expr, precedence) | |
| } | |
| fn get_next_precedence( | |
| &self, | |
| parser: &sqlparser::parser::Parser, | |
| ) -> Option<Result<u8, sqlparser::parser::ParserError>> { | |
| self.0.get_next_precedence(parser) | |
| } | |
| fn parse_statement( | |
| &self, | |
| parser: &mut sqlparser::parser::Parser, | |
| ) -> Option<Result<Statement, sqlparser::parser::ParserError>> { | |
| self.0.parse_statement(parser) | |
| } | |
| fn is_identifier_part(&self, ch: char) -> bool { | |
| self.0.is_identifier_part(ch) | |
| } | |
| } | |
| let statement = r"SELECT 'Wayne\'s World'"; | |
| let res1 = Parser::parse_sql(&MySqlDialect {}, statement); | |
| let res2 = Parser::parse_sql(&WrappedDialect(MySqlDialect {}), statement); | |
| assert_eq!(res1.unwrap(), res2.unwrap()); | |
| } | |
| } |