| // 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. |
| |
| //! Tests for the `derive_dialect!` macro. |
| |
| use sqlparser::ast::{ |
| BinaryOperator, Expr, FunctionArg, FunctionArgExpr, FunctionArguments, LambdaSyntax, Statement, |
| }; |
| use sqlparser::derive_dialect; |
| use sqlparser::dialect::{Dialect, GenericDialect, MySqlDialect, PostgreSqlDialect}; |
| use sqlparser::parser::Parser; |
| use sqlparser::test_utils::{expr_from_projection, only}; |
| |
| #[test] |
| fn test_method_overrides() { |
| derive_dialect!(EnhancedGenericDialect, GenericDialect, overrides = { |
| supports_order_by_all = true, |
| supports_triple_quoted_string = true, |
| }); |
| let dialect = EnhancedGenericDialect::new(); |
| |
| // Overridden methods |
| assert!(dialect.supports_order_by_all()); |
| assert!(dialect.supports_triple_quoted_string()); |
| |
| // Non-overridden retains base behavior |
| assert!(!dialect.supports_factorial_operator()); |
| |
| // Parsing works with the overrides |
| let result = Parser::new(&dialect) |
| .try_with_sql("SELECT '''value''' FROM t ORDER BY ALL") |
| .unwrap() |
| .parse_statements(); |
| |
| assert!(result.is_ok()); |
| } |
| |
| #[test] |
| fn test_preserve_type_id() { |
| // Check the override works and the parser recognizes it as the base type |
| derive_dialect!( |
| PreservedTypeDialect, |
| GenericDialect, |
| preserve_type_id = true, |
| overrides = { supports_order_by_all = true } |
| ); |
| let dialect = PreservedTypeDialect::new(); |
| let d: &dyn Dialect = &dialect; |
| |
| assert!(dialect.supports_order_by_all()); |
| assert!(d.is::<GenericDialect>()); |
| } |
| |
| #[test] |
| fn test_different_base_dialects() { |
| derive_dialect!( |
| EnhancedMySqlDialect, |
| MySqlDialect, |
| overrides = { supports_order_by_all = true } |
| ); |
| derive_dialect!(UniquePostgreSqlDialect, PostgreSqlDialect); |
| |
| let pg = UniquePostgreSqlDialect::new(); |
| let mysql = EnhancedMySqlDialect::new(); |
| |
| // Inherit different base behaviors |
| assert!(pg.supports_filter_during_aggregation()); // PostgreSQL feature |
| assert!(mysql.supports_string_literal_backslash_escape()); // MySQL feature |
| assert!(mysql.supports_order_by_all()); // Override |
| |
| // Each has unique TypeId |
| let pg_ref: &dyn Dialect = &pg; |
| let mysql_ref: &dyn Dialect = &mysql; |
| assert!(pg_ref.is::<UniquePostgreSqlDialect>()); |
| assert!(!pg_ref.is::<PostgreSqlDialect>()); |
| assert!(mysql_ref.is::<EnhancedMySqlDialect>()); |
| } |
| |
| #[test] |
| fn test_identifier_quote_style_overrides() { |
| derive_dialect!( |
| BacktickGenericDialect, |
| GenericDialect, |
| overrides = { identifier_quote_style = '`' } |
| ); |
| derive_dialect!( |
| AnotherBacktickDialect, |
| GenericDialect, |
| overrides = { identifier_quote_style = '[' } |
| ); |
| derive_dialect!( |
| QuotelessPostgreSqlDialect, |
| PostgreSqlDialect, |
| preserve_type_id = true, |
| overrides = { identifier_quote_style = None } |
| ); |
| |
| // Char literal (auto-wrapped in Some) |
| assert_eq!( |
| BacktickGenericDialect::new().identifier_quote_style("x"), |
| Some('`') |
| ); |
| // Another char literal |
| assert_eq!( |
| AnotherBacktickDialect::new().identifier_quote_style("x"), |
| Some('[') |
| ); |
| // None (overrides PostgreSQL's default '"') |
| assert_eq!( |
| QuotelessPostgreSqlDialect::new().identifier_quote_style("x"), |
| None |
| ); |
| } |
| |
| #[test] |
| fn test_lambda_keyword_syntax_with_json_arrow_operator() { |
| // A custom dialect can opt into the `LAMBDA` keyword spelling of lambda |
| // functions without giving up `->` as JSON member access. The two meet in |
| // a single expression below: a lambda whose body is a JSON access. |
| // |
| // PostgreSqlDialect is used only as a convenient base that already gives |
| // `->` its JSON meaning; nothing here is specific to PostgreSQL. |
| derive_dialect!( |
| LambdaPostgreSqlDialect, |
| PostgreSqlDialect, |
| overrides = { supports_lambda_keyword_syntax = true } |
| ); |
| let dialect = LambdaPostgreSqlDialect::new(); |
| |
| // Only the keyword spelling is enabled; the arrow spelling stays off. |
| assert!(dialect.supports_lambda_keyword_syntax()); |
| assert!(!dialect.supports_lambda_functions()); |
| |
| let sql = "SELECT transform(xs, lambda x : (x -> 'a')::INT + 1)"; |
| let ast = Parser::parse_sql(&dialect, sql).unwrap(); |
| assert_eq!(sql, ast[0].to_string()); |
| |
| // Round-tripping alone would not distinguish a JSON access from a nested |
| // lambda, since both print as `x -> 'a'`, so check the parsed shape. |
| let Statement::Query(query) = &ast[0] else { |
| panic!("unexpected statement {}", ast[0]); |
| }; |
| let Expr::Function(func) = |
| expr_from_projection(only(&query.body.as_select().unwrap().projection)) |
| else { |
| panic!("expected a function call"); |
| }; |
| let FunctionArguments::List(args) = &func.args else { |
| panic!("expected an argument list"); |
| }; |
| let [_, FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Lambda(lambda)))] = &args.args[..] |
| else { |
| panic!("expected the second argument to be a lambda"); |
| }; |
| |
| // The lambda came from the `LAMBDA` keyword, not from `->`. |
| assert_eq!(LambdaSyntax::LambdaKeyword, lambda.syntax); |
| |
| // And the `->` in its body is still JSON member access. |
| let Expr::BinaryOp { |
| left, |
| op: BinaryOperator::Plus, |
| .. |
| } = lambda.body.as_ref() |
| else { |
| panic!("expected the lambda body to be an addition"); |
| }; |
| let Expr::Cast { expr, .. } = left.as_ref() else { |
| panic!("expected the left operand to be a cast"); |
| }; |
| let Expr::Nested(json_access) = expr.as_ref() else { |
| panic!("expected the cast operand to be parenthesized"); |
| }; |
| let Expr::BinaryOp { op, .. } = json_access.as_ref() else { |
| panic!("expected `->` to stay a binary operator"); |
| }; |
| assert_eq!(&BinaryOperator::Arrow, op); |
| } |