From 6021eccb712c45021155504be0ef7b30ff2320e7 Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Fri, 6 Mar 2026 18:36:00 +0100 Subject: [PATCH 1/5] Expr:Function(Box) --- src/ast/mod.rs | 21 +- src/parser/mod.rs | 16 +- src/test_utils.rs | 2 +- tests/sqlparser_bigquery.rs | 2 +- tests/sqlparser_clickhouse.rs | 16 +- tests/sqlparser_common.rs | 143 +++++----- tests/sqlparser_duckdb.rs | 4 +- tests/sqlparser_hive.rs | 4 +- tests/sqlparser_mssql.rs | 478 +++++++++++++++------------------- tests/sqlparser_mysql.rs | 16 +- tests/sqlparser_postgres.rs | 56 ++-- tests/sqlparser_redshift.rs | 4 +- tests/sqlparser_snowflake.rs | 107 ++++---- tests/sqlparser_sqlite.rs | 2 +- 14 files changed, 402 insertions(+), 469 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c4d1b50cd5..768e49c689 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1025,6 +1025,7 @@ pub enum Expr { expr: Box, }, /// CONVERT a value to a different data type or character encoding. e.g. `CONVERT(foo USING utf8mb4)` + // XXX too big Convert { /// CONVERT (false) or TRY_CONVERT (true) /// @@ -1043,6 +1044,7 @@ pub enum Expr { styles: Vec, }, /// `CAST` an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))` + // XXX too big Cast { /// The cast kind (e.g., `CAST`, `TRY_CAST`). kind: CastKind, @@ -1192,14 +1194,17 @@ pub enum Expr { /// A constant of form ` 'value'`. /// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`), /// as well as constants of other types (a non-standard PostgreSQL extension). + // XXX too big TypedString(TypedString), /// Scalar function call e.g. `LEFT(foo, 5)` - Function(Function), + // XXX too big + Function(Box), /// `CASE [] WHEN THEN ... [ELSE ] END` /// /// Note we only recognize a complete single expression as ``, /// not `< 0` nor `1, 2, 3` as allowed in a `` per /// + // XXX too big Case { /// The attached `CASE` token (keeps original spacing/comments). case_token: AttachedToken, @@ -1277,6 +1282,7 @@ pub enum Expr { /// An array expression e.g. `ARRAY[1, 2]` Array(Array), /// An interval expression e.g. `INTERVAL '1' YEAR` + // XXX too big Interval(Interval), /// `MySQL` specific text search function [(1)]. /// @@ -1328,6 +1334,7 @@ pub enum Expr { /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/functions#higher-order-functions---operator-and-lambdaparams-expr-function) /// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html) /// [DuckDB](https://duckdb.org/docs/stable/sql/functions/lambda) + // XXX too big Lambda(LambdaFunction), /// Checks membership of a value in a JSON array MemberOf(MemberOf), @@ -1338,6 +1345,16 @@ impl Expr { pub fn value(value: impl Into) -> Self { Expr::Value(value.into()) } + + /// Convenience method to retrieve `Expr::Function`'s value if `self` is a + /// function expression. + pub fn as_function(&self) -> Option<&Function> { + if let Expr::Function(f) = self { + Some(&**f) + } else { + None + } + } } /// The contents inside the `[` and `]` in a subscript expression. @@ -10887,7 +10904,7 @@ pub enum TableObject { /// INSERT INTO TABLE FUNCTION remote('localhost', default.simple_table) /// ``` /// [Clickhouse](https://clickhouse.com/docs/en/sql-reference/table-functions) - TableFunction(Function), + TableFunction(Box), } impl fmt::Display for TableObject { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 6adecb0c66..e4f30806b1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1520,7 +1520,7 @@ impl<'a> Parser<'a> { filter: None, over: None, within_group: vec![], - }))) + }.into()))) } Keyword::CURRENT_TIMESTAMP | Keyword::CURRENT_TIME @@ -1582,7 +1582,7 @@ impl<'a> Parser<'a> { null_treatment: None, over: None, within_group: vec![], - }))) + }.into()))) } Keyword::NOT => Ok(Some(self.parse_not()?)), Keyword::MATCH if self.dialect.supports_match_against() => { @@ -2408,7 +2408,7 @@ impl<'a> Parser<'a> { self.parse_function_call(name).map(Expr::Function) } - fn parse_function_call(&mut self, name: ObjectName) -> Result { + fn parse_function_call(&mut self, name: ObjectName) -> Result, ParserError> { self.expect_token(&Token::LParen)?; // Snowflake permits a subquery to be passed as an argument without @@ -2425,7 +2425,7 @@ impl<'a> Parser<'a> { null_treatment: None, over: None, within_group: vec![], - }); + }.into()); } let mut args = self.parse_function_argument_list()?; @@ -2493,7 +2493,7 @@ impl<'a> Parser<'a> { filter, over, within_group, - }) + }.into()) } /// Optionally parses a null treatment clause. @@ -2528,7 +2528,7 @@ impl<'a> Parser<'a> { over: None, null_treatment: None, within_group: vec![], - })) + }.into())) } /// Parse window frame `UNITS` clause: `ROWS`, `RANGE`, or `GROUPS`. @@ -11095,7 +11095,7 @@ impl<'a> Parser<'a> { let object_name = self.parse_object_name(false)?; if self.peek_token_ref().token == Token::LParen { match self.parse_function(object_name)? { - Expr::Function(f) => Ok(Statement::Call(f)), + Expr::Function(f) => Ok(Statement::Call(*f)), other => parser_err!( format!("Expected a simple procedure call but found: {other}"), self.peek_token_ref().span.start @@ -13839,7 +13839,7 @@ impl<'a> Parser<'a> { let function_expr = self.parse_function(function_name)?; if let Expr::Function(function) = function_expr { let alias = self.parse_identifier_optional_alias()?; - pipe_operators.push(PipeOperator::Call { function, alias }); + pipe_operators.push(PipeOperator::Call { function: *function, alias }); } else { return Err(ParserError::ParserError( "Expected function call after CALL".to_string(), diff --git a/src/test_utils.rs b/src/test_utils.rs index 9ba5960e83..88c1b96298 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -451,7 +451,7 @@ pub fn call(function: &str, args: impl IntoIterator) -> Expr { null_treatment: None, over: None, within_group: vec![], - }) + }.into()) } /// Gets the first index column (mysql calls it a key part) of the first index found in a diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 79db34b06e..6a8acb12ee 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -2247,7 +2247,7 @@ fn parse_map_access_expr() { over: None, within_group: vec![], uses_odbc_syntax: false, - }), + }.into()), }), AccessExpr::Dot(Expr::Identifier(Ident::with_span( Span::new(Location::of(1, 24), Location::of(1, 25)), diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 82f79577b9..02beda8919 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -191,7 +191,7 @@ fn parse_delimited_identifiers() { expr_from_projection(&select.projection[0]), ); assert_eq!( - &Expr::Function(Function { + Some(&Function { name: ObjectName::from(vec![Ident::with_quote('"', "myfun")]), uses_odbc_syntax: false, parameters: FunctionArguments::None, @@ -205,7 +205,7 @@ fn parse_delimited_identifiers() { over: None, within_group: vec![], }), - expr_from_projection(&select.projection[1]), + expr_from_projection(&select.projection[1]).as_function(), ); match &select.projection[2] { SelectItem::ExprWithAlias { expr, alias } => { @@ -826,7 +826,7 @@ fn parse_create_table_with_variant_default_expressions() { data_type: DataType::Datetime(None), options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Materialized(Expr::Function(Function { + option: ColumnOption::Materialized(Expr::Function(Box::new(Function { name: ObjectName::from(vec![Ident::new("now")]), uses_odbc_syntax: false, args: FunctionArguments::List(FunctionArgumentList { @@ -839,7 +839,7 @@ fn parse_create_table_with_variant_default_expressions() { filter: None, over: None, within_group: vec![], - })) + }))) }], }, ColumnDef { @@ -847,7 +847,7 @@ fn parse_create_table_with_variant_default_expressions() { data_type: DataType::Datetime(None), options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Ephemeral(Some(Expr::Function(Function { + option: ColumnOption::Ephemeral(Some(Expr::Function(Box::new(Function { name: ObjectName::from(vec![Ident::new("now")]), uses_odbc_syntax: false, args: FunctionArguments::List(FunctionArgumentList { @@ -860,7 +860,7 @@ fn parse_create_table_with_variant_default_expressions() { filter: None, over: None, within_group: vec![], - }))) + })))) }], }, ColumnDef { @@ -876,7 +876,7 @@ fn parse_create_table_with_variant_default_expressions() { data_type: DataType::String(None), options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Alias(Expr::Function(Function { + option: ColumnOption::Alias(Expr::Function(Box::new(Function { name: ObjectName::from(vec![Ident::new("toString")]), uses_odbc_syntax: false, args: FunctionArguments::List(FunctionArgumentList { @@ -891,7 +891,7 @@ fn parse_create_table_with_variant_default_expressions() { filter: None, over: None, within_group: vec![], - })) + }))) }], } ] diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 08fb6107fc..7f45bdc7d1 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1349,7 +1349,7 @@ fn parse_select_count_wildcard() { filter: None, over: None, within_group: vec![] - }), + }.into()), expr_from_projection(only(&select.projection)) ); } @@ -1375,7 +1375,7 @@ fn parse_select_count_distinct() { within_group: vec![], filter: None, over: None - }), + }.into()), expr_from_projection(only(&select.projection)) ); @@ -1719,11 +1719,11 @@ fn parse_json_object() { Box::new(PostgreSqlDialect {}), ]); let select = dialects.verified_only_select("SELECT JSON_OBJECT('name' : 'value', 'type' : 1)"); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { + match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + Function { args: FunctionArguments::List(FunctionArgumentList { args, .. }), .. - }) => assert_eq!( + } => assert_eq!( &[ FunctionArg::ExprNamed { name: Expr::Value((Value::SingleQuotedString("name".into())).with_empty_span()), @@ -1744,11 +1744,11 @@ fn parse_json_object() { } let select = dialects .verified_only_select("SELECT JSON_OBJECT('name' : 'value', 'type' : NULL ABSENT ON NULL)"); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { + match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + Function { args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. - }) => { + } => { assert_eq!( &[ FunctionArg::ExprNamed { @@ -1780,11 +1780,11 @@ fn parse_json_object() { _ => unreachable!(), } let select = dialects.verified_only_select("SELECT JSON_OBJECT(NULL ON NULL)"); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { + match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + Function { args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. - }) => { + } => { assert!(args.is_empty()); assert_eq!( &[FunctionArgumentClause::JsonNullClause( @@ -1796,11 +1796,11 @@ fn parse_json_object() { _ => unreachable!(), } let select = dialects.verified_only_select("SELECT JSON_OBJECT(ABSENT ON NULL)"); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { + match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + Function { args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. - }) => { + } => { assert!(args.is_empty()); assert_eq!( &[FunctionArgumentClause::JsonNullClause( @@ -1814,11 +1814,11 @@ fn parse_json_object() { let select = dialects.verified_only_select( "SELECT JSON_OBJECT('name' : 'value', 'type' : JSON_ARRAY(1, 2) ABSENT ON NULL)", ); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { + match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + Function { args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. - }) => { + } => { assert_eq!( &FunctionArg::ExprNamed { name: Expr::Value((Value::SingleQuotedString("name".into())).with_empty_span()), @@ -1852,41 +1852,35 @@ fn parse_json_object() { let select = dialects.verified_only_select( "SELECT JSON_OBJECT('name' : 'value', 'type' : JSON_OBJECT('type_id' : 1, 'name' : 'a') NULL ON NULL)", ); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), - .. - }) => { - assert_eq!( - &FunctionArg::ExprNamed { - name: Expr::Value((Value::SingleQuotedString("name".into())).with_empty_span()), - arg: FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString("value".into())).with_empty_span() - )), - operator: FunctionArgOperator::Colon - }, - &args[0] - ); - assert!(matches!( - args[1], - FunctionArg::ExprNamed { - name: Expr::Value(ValueWithSpan { - value: Value::SingleQuotedString(_), - span: _ - }), - arg: FunctionArgExpr::Expr(Expr::Function(_)), - operator: FunctionArgOperator::Colon - } - )); - assert_eq!( - &[FunctionArgumentClause::JsonNullClause( - JsonNullClause::NullOnNull - )], - &clauses[..] - ); + let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + assert_eq!( + &FunctionArg::ExprNamed { + name: Expr::Value((Value::SingleQuotedString("name".into())).with_empty_span()), + arg: FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString("value".into())).with_empty_span() + )), + operator: FunctionArgOperator::Colon + }, + &args[0] + ); + assert!(matches!( + args[1], + FunctionArg::ExprNamed { + name: Expr::Value(ValueWithSpan { + value: Value::SingleQuotedString(_), + span: _ + }), + arg: FunctionArgExpr::Expr(Expr::Function(_)), + operator: FunctionArgOperator::Colon } - _ => unreachable!(), - } + )); + assert_eq!( + &[FunctionArgumentClause::JsonNullClause( + JsonNullClause::NullOnNull + )], + &clauses[..] + ); } #[test] @@ -2979,7 +2973,7 @@ fn parse_select_having() { filter: None, over: None, within_group: vec![] - })), + }.into())), op: BinaryOperator::Gt, right: Box::new(Expr::value(number("1"))), }), @@ -3022,7 +3016,7 @@ fn parse_select_qualify() { window_frame: None, })), within_group: vec![] - })), + }.into())), op: BinaryOperator::Eq, right: Box::new(Expr::value(number("1"))), }), @@ -3477,7 +3471,7 @@ fn parse_listagg() { with_fill: None, }, ] - }), + }.into()), expr_from_projection(only(&select.projection)) ); @@ -5626,7 +5620,7 @@ fn parse_named_argument_function() { filter: None, over: None, within_group: vec![] - }), + }.into()), expr_from_projection(only(&select.projection)) ); } @@ -5666,7 +5660,7 @@ fn parse_named_argument_function_with_eq_operator() { filter: None, over: None, within_group: vec![], - }), + }.into()), expr_from_projection(only(&select.projection)) ); @@ -5738,14 +5732,14 @@ fn parse_window_functions() { window_frame: None, })), within_group: vec![], - }), + }.into()), expr_from_projection(&select.projection[0]) ); for i in 0..EXPECTED_PROJ_QTY { assert!(matches!( - expr_from_projection(&select.projection[i]), - Expr::Function(Function { + expr_from_projection(&select.projection[i]).as_function(), + Some(Function { over: Some(WindowType::WindowSpec(WindowSpec { window_name: None, .. @@ -5781,8 +5775,8 @@ fn parse_named_window_functions() { const EXPECTED_WIN_NAMES: [&str; 2] = ["w", "win"]; for (i, win_name) in EXPECTED_WIN_NAMES.iter().enumerate() { assert!(matches!( - expr_from_projection(&select.projection[i]), - Expr::Function(Function { + expr_from_projection(&select.projection[i]).as_function(), + Some(Function { over: Some(WindowType::WindowSpec(WindowSpec { window_name: Some(Ident { value, .. }), .. @@ -5878,7 +5872,7 @@ fn test_parse_named_window() { span: Span::empty(), })), within_group: vec![], - }), + }.into()), alias: Ident { value: "min1".to_string(), quote_style: None, @@ -5913,7 +5907,7 @@ fn test_parse_named_window() { span: Span::empty(), })), within_group: vec![], - }), + }.into()), alias: Ident { value: "max1".to_string(), quote_style: None, @@ -10604,7 +10598,7 @@ fn parse_time_functions() { within_group: vec![], }; assert_eq!( - &Expr::Function(select_localtime_func_call_ast.clone()), + &Expr::Function(select_localtime_func_call_ast.clone().into()), expr_from_projection(&select.projection[0]) ); @@ -10613,7 +10607,7 @@ fn parse_time_functions() { let mut ast_without_parens = select_localtime_func_call_ast; ast_without_parens.args = FunctionArguments::None; assert_eq!( - &Expr::Function(ast_without_parens), + &Expr::Function(ast_without_parens.into()), expr_from_projection(&verified_only_select(&sql_without_parens).projection[0]) ); } @@ -12705,7 +12699,7 @@ fn parse_map_access_expr() { over: None, within_group: vec![], uses_odbc_syntax: false, - }), + }.into()), }), ], }; @@ -13046,7 +13040,7 @@ fn test_selective_aggregation() { over: None, within_group: vec![], null_treatment: None - })), + }.into())), SelectItem::ExprWithAlias { expr: Expr::Function(Function { name: ObjectName::from(vec![Ident::new("ARRAY_AGG")]), @@ -13071,7 +13065,7 @@ fn test_selective_aggregation() { null_treatment: None, over: None, within_group: vec![] - }), + }.into()), alias: Ident::new("agg2") }, ] @@ -13528,15 +13522,12 @@ fn insert_into_with_parentheses() { #[test] fn parse_odbc_scalar_function() { let select = verified_only_select("SELECT {fn my_func(1, 2)}"); - let Expr::Function(Function { + let Function { name, uses_odbc_syntax, args, .. - }) = expr_from_projection(only(&select.projection)) - else { - unreachable!("expected function") - }; + } = expr_from_projection(only(&select.projection)).as_function().expect("not a function"); assert_eq!(name, &ObjectName::from(vec![Ident::new("my_func")])); assert!(uses_odbc_syntax); matches!(args, FunctionArguments::List(l) if l.args.len() == 2); @@ -15440,7 +15431,7 @@ fn parse_composite_access_expr() { filter: None, over: None, within_group: vec![] - })), + }.into())), access_chain: vec![AccessExpr::Dot(Expr::Identifier(Ident::new("b")))] } ); @@ -15464,7 +15455,7 @@ fn parse_composite_access_expr() { filter: None, over: None, within_group: vec![] - })), + }.into())), access_chain: vec![ AccessExpr::Dot(Expr::Identifier(Ident::new("b"))), AccessExpr::Dot(Expr::Identifier(Ident::new("c"))), @@ -15490,7 +15481,7 @@ fn parse_composite_access_expr() { filter: None, over: None, within_group: vec![], - })), + }.into())), access_chain: vec![AccessExpr::Dot(Expr::Identifier(Ident::new("b")))], }; diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index 671e92b979..1946d7d4a1 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -634,7 +634,7 @@ fn test_duckdb_named_argument_function_with_assignment_operator() { let sql = "SELECT FUN(a := '1', b := '2') FROM foo"; let select = duckdb_and_generic().verified_only_select(sql); assert_eq!( - &Expr::Function(Function { + Some(&Function { name: ObjectName::from(vec![Ident::new("FUN")]), uses_odbc_syntax: false, parameters: FunctionArguments::None, @@ -663,7 +663,7 @@ fn test_duckdb_named_argument_function_with_assignment_operator() { over: None, within_group: vec![], }), - expr_from_projection(only(&select.projection)) + expr_from_projection(only(&select.projection)).as_function() ); } diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 1b09485185..1623be1a34 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -493,7 +493,7 @@ fn parse_delimited_identifiers() { expr_from_projection(&select.projection[0]), ); assert_eq!( - &Expr::Function(Function { + Some(&Function { name: ObjectName::from(vec![Ident::with_quote('"', "myfun")]), uses_odbc_syntax: false, parameters: FunctionArguments::None, @@ -507,7 +507,7 @@ fn parse_delimited_identifiers() { over: None, within_group: vec![], }), - expr_from_projection(&select.projection[1]), + expr_from_projection(&select.projection[1]).as_function(), ); match &select.projection[2] { SelectItem::ExprWithAlias { expr, alias } => { diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 07dd0fcb61..a82f27137a 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -887,7 +887,7 @@ fn parse_delimited_identifiers() { expr_from_projection(&select.projection[0]), ); assert_eq!( - &Expr::Function(Function { + Some(&Function { name: ObjectName::from(vec![Ident::with_quote('"', "myfun")]), uses_odbc_syntax: false, parameters: FunctionArguments::None, @@ -901,7 +901,7 @@ fn parse_delimited_identifiers() { over: None, within_group: vec![], }), - expr_from_projection(&select.projection[1]), + expr_from_projection(&select.projection[1]).as_function(), ); match &select.projection[2] { SelectItem::ExprWithAlias { expr, alias } => { @@ -979,236 +979,195 @@ fn parse_mssql_json_object() { let select = ms().verified_only_select( "SELECT JSON_OBJECT('user_name' : USER_NAME(), LOWER(@id_key) : @id_value, 'sid' : (SELECT @@SPID) ABSENT ON NULL)", ); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), - .. - }) => { - assert!(matches!( - args[0], - FunctionArg::ExprNamed { - name: Expr::Value(ValueWithSpan { - value: Value::SingleQuotedString(_), - span: _ - }), - arg: FunctionArgExpr::Expr(Expr::Function(_)), - operator: FunctionArgOperator::Colon - } - )); - assert!(matches!( - args[1], - FunctionArg::ExprNamed { - name: Expr::Function(_), - arg: FunctionArgExpr::Expr(Expr::Identifier(_)), - operator: FunctionArgOperator::Colon - } - )); - assert!(matches!( - args[2], - FunctionArg::ExprNamed { - name: Expr::Value(ValueWithSpan { - value: Value::SingleQuotedString(_), - span: _ - }), - arg: FunctionArgExpr::Expr(Expr::Subquery(_)), - operator: FunctionArgOperator::Colon - } - )); - assert_eq!( - &[FunctionArgumentClause::JsonNullClause( - JsonNullClause::AbsentOnNull - )], - &clauses[..] - ); + let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function-arg-list"); }; + assert!(matches!( + args[0], + FunctionArg::ExprNamed { + name: Expr::Value(ValueWithSpan { + value: Value::SingleQuotedString(_), + span: _ + }), + arg: FunctionArgExpr::Expr(Expr::Function(_)), + operator: FunctionArgOperator::Colon } - _ => unreachable!(), - } + )); + assert!(matches!( + args[1], + FunctionArg::ExprNamed { + name: Expr::Function(_), + arg: FunctionArgExpr::Expr(Expr::Identifier(_)), + operator: FunctionArgOperator::Colon + } + )); + assert!(matches!( + args[2], + FunctionArg::ExprNamed { + name: Expr::Value(ValueWithSpan { + value: Value::SingleQuotedString(_), + span: _ + }), + arg: FunctionArgExpr::Expr(Expr::Subquery(_)), + operator: FunctionArgOperator::Colon + } + )); + assert_eq!( + &[FunctionArgumentClause::JsonNullClause( + JsonNullClause::AbsentOnNull + )], + &clauses[..] + ); + let select = ms().verified_only_select( "SELECT s.session_id, JSON_OBJECT('security_id' : s.security_id, 'login' : s.login_name, 'status' : s.status) AS info \ FROM sys.dm_exec_sessions AS s \ WHERE s.is_user_process = 1", ); - match &select.projection[1] { - SelectItem::ExprWithAlias { - expr: - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, .. }), - .. - }), - .. - } => { - assert!(matches!( - args[0], - FunctionArg::ExprNamed { - name: Expr::Value(ValueWithSpan { - value: Value::SingleQuotedString(_), - span: _ - }), - arg: FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)), - operator: FunctionArgOperator::Colon - } - )); - assert!(matches!( - args[1], - FunctionArg::ExprNamed { - name: Expr::Value(ValueWithSpan { - value: Value::SingleQuotedString(_), - span: _ - }), - arg: FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)), - operator: FunctionArgOperator::Colon - } - )); - assert!(matches!( - args[2], - FunctionArg::ExprNamed { - name: Expr::Value(ValueWithSpan { - value: Value::SingleQuotedString(_), - span: _ - }), - arg: FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)), - operator: FunctionArgOperator::Colon - } - )); + + let SelectItem::ExprWithAlias { expr, .. } = &select.projection[1] else { panic!("not an expr-with-alias"); }; + let Function { args, .. } = expr.as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { panic!(" not an function-arg-list"); }; + assert!(matches!( + args[0], + FunctionArg::ExprNamed { + name: Expr::Value(ValueWithSpan { + value: Value::SingleQuotedString(_), + span: _ + }), + arg: FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)), + operator: FunctionArgOperator::Colon } - _ => unreachable!(), - } + )); + assert!(matches!( + args[1], + FunctionArg::ExprNamed { + name: Expr::Value(ValueWithSpan { + value: Value::SingleQuotedString(_), + span: _ + }), + arg: FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)), + operator: FunctionArgOperator::Colon + } + )); + assert!(matches!( + args[2], + FunctionArg::ExprNamed { + name: Expr::Value(ValueWithSpan { + value: Value::SingleQuotedString(_), + span: _ + }), + arg: FunctionArgExpr::Expr(Expr::CompoundIdentifier(_)), + operator: FunctionArgOperator::Colon + } + )); } #[test] fn parse_mssql_json_array() { let select = ms().verified_only_select("SELECT JSON_ARRAY('a', 1, NULL, 2 NULL ON NULL)"); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), - .. - }) => { - assert_eq!( - &[ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString("a".into())).with_empty_span() - ))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (number("1")).with_empty_span() - ))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (Value::Null).with_empty_span() - ))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (number("2")).with_empty_span() - ))), - ], - &args[..] - ); - assert_eq!( - &[FunctionArgumentClause::JsonNullClause( - JsonNullClause::NullOnNull - )], - &clauses[..] - ); - } - _ => unreachable!(), - } + let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function-arg-list"); }; + assert_eq!( + &[ + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString("a".into())).with_empty_span() + ))), + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (number("1")).with_empty_span() + ))), + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (Value::Null).with_empty_span() + ))), + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (number("2")).with_empty_span() + ))), + ], + &args[..] + ); + assert_eq!( + &[FunctionArgumentClause::JsonNullClause( + JsonNullClause::NullOnNull + )], + &clauses[..] + ); + let select = ms().verified_only_select("SELECT JSON_ARRAY('a', 1, NULL, 2 ABSENT ON NULL)"); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), - .. - }) => { - assert_eq!( - &[ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString("a".into())).with_empty_span() - ))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (number("1")).with_empty_span() - ))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (Value::Null).with_empty_span() - ))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (number("2")).with_empty_span() - ))), - ], - &args[..] - ); - assert_eq!( - &[FunctionArgumentClause::JsonNullClause( - JsonNullClause::AbsentOnNull - )], - &clauses[..] - ); - } - _ => unreachable!(), - } + let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + assert_eq!( + &[ + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString("a".into())).with_empty_span() + ))), + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (number("1")).with_empty_span() + ))), + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (Value::Null).with_empty_span() + ))), + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (number("2")).with_empty_span() + ))), + ], + &args[..] + ); + assert_eq!( + &[FunctionArgumentClause::JsonNullClause( + JsonNullClause::AbsentOnNull + )], + &clauses[..] + ); + let select = ms().verified_only_select("SELECT JSON_ARRAY(NULL ON NULL)"); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), - .. - }) => { - assert!(args.is_empty()); - assert_eq!( - &[FunctionArgumentClause::JsonNullClause( - JsonNullClause::NullOnNull - )], - &clauses[..] - ); - } - _ => unreachable!(), - } + let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + assert!(args.is_empty()); + assert_eq!( + &[FunctionArgumentClause::JsonNullClause( + JsonNullClause::NullOnNull + )], + &clauses[..] + ); + let select = ms().verified_only_select("SELECT JSON_ARRAY(ABSENT ON NULL)"); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), - .. - }) => { - assert!(args.is_empty()); - assert_eq!( - &[FunctionArgumentClause::JsonNullClause( - JsonNullClause::AbsentOnNull - )], - &clauses[..] - ); - } - _ => unreachable!(), - } + let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + assert!(args.is_empty()); + assert_eq!( + &[FunctionArgumentClause::JsonNullClause( + JsonNullClause::AbsentOnNull + )], + &clauses[..] + ); + let select = ms().verified_only_select( "SELECT JSON_ARRAY('a', JSON_OBJECT('name' : 'value', 'type' : 1) NULL ON NULL)", ); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), - .. - }) => { - assert_eq!( - &FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString("a".into())).with_empty_span() - ))), - &args[0] - ); - assert!(matches!( - args[1], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_))) - )); - assert_eq!( - &[FunctionArgumentClause::JsonNullClause( - JsonNullClause::NullOnNull - )], - &clauses[..] - ); - } - _ => unreachable!(), - } + let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + assert_eq!( + &FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString("a".into())).with_empty_span() + ))), + &args[0] + ); + assert!(matches!( + args[1], + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_))) + )); + assert_eq!( + &[FunctionArgumentClause::JsonNullClause( + JsonNullClause::NullOnNull + )], + &clauses[..] + ); + let select = ms().verified_only_select( "SELECT JSON_ARRAY('a', JSON_OBJECT('name' : 'value', 'type' : 1), JSON_ARRAY(1, NULL, 2 NULL ON NULL))", ); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, .. }), - .. - }) => { - assert_eq!( + let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { panic!("not a function arg list"); }; + assert_eq!( &FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( (Value::SingleQuotedString("a".into())).with_empty_span() ))), @@ -1222,67 +1181,52 @@ fn parse_mssql_json_array() { args[2], FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_))) )); - } - _ => unreachable!(), - } + let select = ms().verified_only_select("SELECT JSON_ARRAY(1, @id_value, (SELECT @@SPID))"); - match expr_from_projection(&select.projection[0]) { - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, .. }), - .. - }) => { - assert_eq!( - &FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (number("1")).with_empty_span() - ))), - &args[0] - ); - assert!(matches!( - args[1], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(_))) - )); - assert!(matches!( - args[2], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Subquery(_))) - )); - } - _ => unreachable!(), - } + let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { panic!("not a function-arg-list"); }; + assert_eq!( + &FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (number("1")).with_empty_span() + ))), + &args[0] + ); + assert!(matches!( + args[1], + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(_))) + )); + assert!(matches!( + args[2], + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Subquery(_))) + )); + let select = ms().verified_only_select( "SELECT s.session_id, JSON_ARRAY(s.host_name, s.program_name, s.client_interface_name NULL ON NULL) AS info \ FROM sys.dm_exec_sessions AS s \ WHERE s.is_user_process = 1", ); - match &select.projection[1] { - SelectItem::ExprWithAlias { - expr: - Expr::Function(Function { - args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), - .. - }), - .. - } => { - assert!(matches!( - args[0], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_))) - )); - assert!(matches!( - args[1], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_))) - )); - assert!(matches!( - args[2], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_))) - )); - assert_eq!( - &[FunctionArgumentClause::JsonNullClause( - JsonNullClause::NullOnNull - )], - &clauses[..] - ); - } - _ => unreachable!(), - } + + let SelectItem::ExprWithAlias { expr, ..}= &select.projection[1] else { panic!("not an expr-with-alias"); }; + let Function { args, .. } = expr.as_function().expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function-arg-list"); }; + assert!(matches!( + args[0], + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_))) + )); + assert!(matches!( + args[1], + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_))) + )); + assert!(matches!( + args[2], + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_))) + )); + assert_eq!( + &[FunctionArgumentClause::JsonNullClause( + JsonNullClause::NullOnNull + )], + &clauses[..] + ); } #[test] @@ -1909,7 +1853,7 @@ fn parse_create_table_with_valid_options() { null_treatment: None, over: None, within_group: vec![], - }, + }.into(), ), }, SqlOption::Ident("HEAP".into()), diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 6c59997c32..3e613068d5 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -832,17 +832,11 @@ fn parse_prefix_key_part() { "ALTER TABLE tab ADD FULLTEXT INDEX (textcol(10))", "CREATE TABLE t (textcol TEXT, INDEX idx_index (textcol(10)))", ] { - match index_column(mysql_and_generic().verified_stmt(sql)) { - Expr::Function(Function { - name, - args: FunctionArguments::List(FunctionArgumentList { args, .. }), - .. - }) => { - assert_eq!(name.to_string(), "textcol"); - assert_eq!(args, expected); - } - expr => panic!("unexpected expression {expr} for {sql}"), - } + let expr = index_column(mysql_and_generic().verified_stmt(sql)); + let Function { name, args, .. } = expr.as_function().expect("not a function"); + assert_eq!(name.to_string(), "textcol"); + let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { panic!("not a function arg list"); }; + assert_eq!(args, &expected); } } diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 6b4f35d78b..2a419e25c7 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -2776,7 +2776,7 @@ fn parse_create_indices_with_operator_classes() { null_treatment: None, over: None, within_group: vec![], - }), + }.into()), options: OrderByOptions { asc: None, nulls_first: None, @@ -3275,7 +3275,7 @@ fn parse_array_subquery_expr() { null_treatment: None, over: None, within_group: vec![] - }), + }.into()), expr_from_projection(only(&select.projection)), ); } @@ -3538,8 +3538,9 @@ fn test_json() { #[test] fn json_object_colon_syntax() { - match pg().verified_expr("JSON_OBJECT('name' : 'value')") { - Expr::Function(Function { + let expr = pg().verified_expr("JSON_OBJECT('name' : 'value')"); + match expr.as_function() { + Some(Function { args: FunctionArguments::List(FunctionArgumentList { args, .. }), .. }) => { @@ -3554,22 +3555,23 @@ fn json_object_colon_syntax() { "Invalid function argument: {args:?}" ); } - other => panic!( - "Expected: JSON_OBJECT('name' : 'value') to be parsed as a function, but got {other:?}" + _ => panic!( + "Expected: JSON_OBJECT('name' : 'value') to be parsed as a function, but got {expr:?}" ), } } #[test] fn json_object_value_syntax() { - match pg().verified_expr("JSON_OBJECT('name' VALUE 'value')") { - Expr::Function(Function { args: FunctionArguments::List(FunctionArgumentList { args, .. }), .. }) => { + let expr = pg().verified_expr("JSON_OBJECT('name' VALUE 'value')"); + match expr.as_function() { + Some(Function { args: FunctionArguments::List(FunctionArgumentList { args, .. }), .. }) => { assert!(matches!( &args[..], &[FunctionArg::ExprNamed { operator: FunctionArgOperator::Value, .. }] ), "Invalid function argument: {args:?}"); } - other => panic!("Expected: JSON_OBJECT('name' VALUE 'value') to be parsed as a function, but got {other:?}"), + _ => panic!("Expected: JSON_OBJECT('name' VALUE 'value') to be parsed as a function, but got {expr:?}"), } } @@ -3579,17 +3581,17 @@ fn parse_json_object() { let expr = pg().verified_expr(sql); assert!( matches!( - expr.clone(), - Expr::Function(Function { + expr.as_function(), + Some(Function { name: ObjectName(parts), args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. - }) if parts == vec![ObjectNamePart::Identifier(Ident::new("JSON_OBJECT"))] + }) if parts == &[ObjectNamePart::Identifier(Ident::new("JSON_OBJECT"))] && matches!( &args[..], &[FunctionArg::ExprNamed { operator: FunctionArgOperator::Value, .. }] ) - && clauses == vec![FunctionArgumentClause::JsonNullClause(JsonNullClause::NullOnNull)] + && clauses == &[FunctionArgumentClause::JsonNullClause(JsonNullClause::NullOnNull)] ), "Failed to parse JSON_OBJECT with expected structure, got: {expr:?}" ); @@ -3598,17 +3600,17 @@ fn parse_json_object() { let expr = pg().verified_expr(sql); assert!( matches!( - expr.clone(), - Expr::Function(Function { + expr.as_function(), + Some(Function { name: ObjectName(parts), args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. - }) if parts == vec![ObjectNamePart::Identifier(Ident::new("JSON_OBJECT"))] + }) if parts == &[ObjectNamePart::Identifier(Ident::new("JSON_OBJECT"))] && matches!( &args[..], &[FunctionArg::ExprNamed { operator: FunctionArgOperator::Value, .. }] ) - && clauses == vec![FunctionArgumentClause::JsonReturningClause(JsonReturningClause { data_type: DataType::JSONB })] + && clauses == &[FunctionArgumentClause::JsonReturningClause(JsonReturningClause { data_type: DataType::JSONB })] ), "Failed to parse JSON_OBJECT with expected structure, got: {expr:?}" ); @@ -3617,14 +3619,14 @@ fn parse_json_object() { let expr = pg().verified_expr(sql); assert!( matches!( - expr.clone(), - Expr::Function(Function { + expr.as_function(), + Some(Function { name: ObjectName(parts), args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. - }) if parts == vec![ObjectNamePart::Identifier(Ident::new("JSON_OBJECT"))] + }) if parts == &[ObjectNamePart::Identifier(Ident::new("JSON_OBJECT"))] && args.is_empty() - && clauses == vec![FunctionArgumentClause::JsonReturningClause(JsonReturningClause { data_type: DataType::JSONB })] + && clauses == &[FunctionArgumentClause::JsonReturningClause(JsonReturningClause { data_type: DataType::JSONB })] ), "Failed to parse JSON_OBJECT with expected structure, got: {expr:?}" ); @@ -3718,7 +3720,7 @@ fn test_composite_value() { filter: None, over: None, within_group: vec![], - })))), + }.into())))), access_chain: vec![AccessExpr::Dot(Expr::Identifier(Ident::new("n")))], }, expr_from_projection(&select.projection[0]) @@ -3885,7 +3887,7 @@ fn parse_current_functions() { filter: None, over: None, within_group: vec![], - }), + }.into()), expr_from_projection(&select.projection[0]) ); assert_eq!( @@ -3898,7 +3900,7 @@ fn parse_current_functions() { filter: None, over: None, within_group: vec![], - }), + }.into()), expr_from_projection(&select.projection[1]) ); assert_eq!( @@ -3911,7 +3913,7 @@ fn parse_current_functions() { filter: None, over: None, within_group: vec![], - }), + }.into()), expr_from_projection(&select.projection[2]) ); assert_eq!( @@ -3924,7 +3926,7 @@ fn parse_current_functions() { filter: None, over: None, within_group: vec![], - }), + }.into()), expr_from_projection(&select.projection[3]) ); } @@ -4376,7 +4378,7 @@ fn parse_delimited_identifiers() { filter: None, over: None, within_group: vec![], - }), + }.into()), expr_from_projection(&select.projection[1]), ); match &select.projection[2] { diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 184aa5b69a..b50bfd144d 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -135,7 +135,7 @@ fn parse_delimited_identifiers() { expr_from_projection(&select.projection[0]), ); assert_eq!( - &Expr::Function(Function { + Some(&Function { name: ObjectName::from(vec![Ident::with_quote('"', "myfun")]), uses_odbc_syntax: false, parameters: FunctionArguments::None, @@ -149,7 +149,7 @@ fn parse_delimited_identifiers() { over: None, within_group: vec![], }), - expr_from_projection(&select.projection[1]), + expr_from_projection(&select.projection[1]).as_function(), ); match &select.projection[2] { SelectItem::ExprWithAlias { expr, alias } => { diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 666876fa72..bf6efc0611 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -548,7 +548,7 @@ fn test_snowflake_create_table_cluster_by() { null_treatment: None, over: None, within_group: vec![], - }), + }.into()), ])), cluster_by ) @@ -1435,7 +1435,7 @@ fn parse_delimited_identifiers() { expr_from_projection(&select.projection[0]), ); assert_eq!( - &Expr::Function(Function { + Some(&Function { name: ObjectName::from(vec![Ident::with_quote('"', "myfun")]), uses_odbc_syntax: false, parameters: FunctionArguments::None, @@ -1449,7 +1449,7 @@ fn parse_delimited_identifiers() { over: None, within_group: vec![], }), - expr_from_projection(&select.projection[1]), + expr_from_projection(&select.projection[1]).as_function(), ); match &select.projection[2] { SelectItem::ExprWithAlias { expr, alias } => { @@ -1668,7 +1668,7 @@ fn test_alter_table_clustering() { null_treatment: None, over: None, within_group: vec![] - }) + }.into()) ], ); } @@ -4755,70 +4755,55 @@ fn test_snowflake_create_view_copy_grants() { #[test] fn test_snowflake_identifier_function() { // Using IDENTIFIER to reference a column - match &snowflake() + let SelectItem::UnnamedExpr(expr) = &snowflake() .verified_only_select("SELECT identifier('email') FROM customers") - .projection[0] - { - SelectItem::UnnamedExpr(Expr::Function(Function { name, args, .. })) => { - assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); - assert_eq!( - *args, - FunctionArguments::List(FunctionArgumentList { - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - Value::SingleQuotedString("email".to_string()).into() - )))], - clauses: vec![], - duplicate_treatment: None - }) - ); - } - _ => unreachable!(), - } + .projection[0] else { panic!("not an unnamed expression"); }; + let Function { name, args, .. } = expr.as_function().expect("not a function"); + assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); + assert_eq!( + *args, + FunctionArguments::List(FunctionArgumentList { + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + Value::SingleQuotedString("email".to_string()).into() + )))], + clauses: vec![], + duplicate_treatment: None + }) + ); // Using IDENTIFIER to reference a case-sensitive column - match &snowflake() + let SelectItem::UnnamedExpr(expr) = &snowflake() .verified_only_select(r#"SELECT identifier('"Email"') FROM customers"#) - .projection[0] - { - SelectItem::UnnamedExpr(Expr::Function(Function { name, args, .. })) => { - assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); - assert_eq!( - *args, - FunctionArguments::List(FunctionArgumentList { - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - Value::SingleQuotedString("\"Email\"".to_string()).into() - )))], - clauses: vec![], - duplicate_treatment: None - }) - ); - } - _ => unreachable!(), - } + .projection[0] else { panic!("not an unnamed expression"); }; + let Function { name, args, .. } = expr.as_function().expect("not a function"); + assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); + assert_eq!( + *args, + FunctionArguments::List(FunctionArgumentList { + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + Value::SingleQuotedString("\"Email\"".to_string()).into() + )))], + clauses: vec![], + duplicate_treatment: None + }) + ); // Using IDENTIFIER to reference an alias of a table - match &snowflake() + let SelectItem::QualifiedWildcard(SelectItemQualifiedWildcardKind::Expr(expr), _) = &snowflake() .verified_only_select("SELECT identifier('alias1').* FROM tbl AS alias1") - .projection[0] - { - SelectItem::QualifiedWildcard( - SelectItemQualifiedWildcardKind::Expr(Expr::Function(Function { name, args, .. })), - _, - ) => { - assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); - assert_eq!( - *args, - FunctionArguments::List(FunctionArgumentList { - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - Value::SingleQuotedString("alias1".to_string()).into() - )))], - clauses: vec![], - duplicate_treatment: None - }) - ); - } - _ => unreachable!(), - } + .projection[0] else { panic!("not a qualified wildcard"); }; + let Function { name, args, .. }= expr.as_function().expect("not a function"); + assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); + assert_eq!( + *args, + FunctionArguments::List(FunctionArgumentList { + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + Value::SingleQuotedString("alias1".to_string()).into() + )))], + clauses: vec![], + duplicate_treatment: None + }) + ); // Using IDENTIFIER to reference a database match snowflake().verified_stmt("CREATE DATABASE IDENTIFIER('tbl')") { diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 33c38fb0a6..4d8600569e 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -444,7 +444,7 @@ fn parse_window_function_with_filter() { })), filter: Some(Box::new(Expr::Identifier(Ident::new("y")))), within_group: vec![], - }))] + }.into()))] ); } } From f92bbd86f79f2eb8de67d5b18cde5023f5940471 Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Fri, 6 Mar 2026 19:40:04 +0100 Subject: [PATCH 2/5] Cargo fmt --- src/parser/mod.rs | 34 +- src/test_utils.rs | 37 +- tests/sqlparser_bigquery.rs | 41 +- tests/sqlparser_clickhouse.rs | 60 +-- tests/sqlparser_common.rs | 803 ++++++++++++++++++---------------- tests/sqlparser_mssql.rs | 110 +++-- tests/sqlparser_mysql.rs | 4 +- tests/sqlparser_postgres.rs | 418 +++++++++--------- tests/sqlparser_snowflake.rs | 92 ++-- tests/sqlparser_sqlite.rs | 45 +- 10 files changed, 909 insertions(+), 735 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e4f30806b1..d16bc4391d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2425,7 +2425,8 @@ impl<'a> Parser<'a> { null_treatment: None, over: None, within_group: vec![], - }.into()); + } + .into()); } let mut args = self.parse_function_argument_list()?; @@ -2493,7 +2494,8 @@ impl<'a> Parser<'a> { filter, over, within_group, - }.into()) + } + .into()) } /// Optionally parses a null treatment clause. @@ -2519,16 +2521,19 @@ impl<'a> Parser<'a> { } else { FunctionArguments::None }; - Ok(Expr::Function(Function { - name, - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args, - filter: None, - over: None, - null_treatment: None, - within_group: vec![], - }.into())) + Ok(Expr::Function( + Function { + name, + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args, + filter: None, + over: None, + null_treatment: None, + within_group: vec![], + } + .into(), + )) } /// Parse window frame `UNITS` clause: `ROWS`, `RANGE`, or `GROUPS`. @@ -13839,7 +13844,10 @@ impl<'a> Parser<'a> { let function_expr = self.parse_function(function_name)?; if let Expr::Function(function) = function_expr { let alias = self.parse_identifier_optional_alias()?; - pipe_operators.push(PipeOperator::Call { function: *function, alias }); + pipe_operators.push(PipeOperator::Call { + function: *function, + alias, + }); } else { return Err(ParserError::ParserError( "Expected function call after CALL".to_string(), diff --git a/src/test_utils.rs b/src/test_utils.rs index 88c1b96298..b0be41722a 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -435,23 +435,26 @@ pub fn join(relation: TableFactor) -> Join { } pub fn call(function: &str, args: impl IntoIterator) -> Expr { - Expr::Function(Function { - name: ObjectName::from(vec![Ident::new(function)]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: args - .into_iter() - .map(|arg| FunctionArg::Unnamed(FunctionArgExpr::Expr(arg))) - .collect(), - clauses: vec![], - }), - filter: None, - null_treatment: None, - over: None, - within_group: vec![], - }.into()) + Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new(function)]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: args + .into_iter() + .map(|arg| FunctionArg::Unnamed(FunctionArgExpr::Expr(arg))) + .collect(), + clauses: vec![], + }), + filter: None, + null_treatment: None, + over: None, + within_group: vec![], + } + .into(), + ) } /// Gets the first index column (mysql calls it a key part) of the first index found in a diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 6a8acb12ee..569a0bf689 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -2229,25 +2229,28 @@ fn parse_map_access_expr() { }, }), AccessExpr::Subscript(Subscript::Index { - index: Expr::Function(Function { - name: ObjectName::from(vec![Ident::with_span( - Span::new(Location::of(1, 11), Location::of(1, 22)), - "safe_offset", - )]), - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - number("2").with_empty_span(), - )))], - clauses: vec![], - }), - filter: None, - null_treatment: None, - over: None, - within_group: vec![], - uses_odbc_syntax: false, - }.into()), + index: Expr::Function( + Function { + name: ObjectName::from(vec![Ident::with_span( + Span::new(Location::of(1, 11), Location::of(1, 22)), + "safe_offset", + )]), + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + number("2").with_empty_span(), + )))], + clauses: vec![], + }), + filter: None, + null_treatment: None, + over: None, + within_group: vec![], + uses_odbc_syntax: false, + } + .into(), + ), }), AccessExpr::Dot(Expr::Identifier(Ident::with_span( Span::new(Location::of(1, 24), Location::of(1, 25)), diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 02beda8919..0688741f26 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -826,20 +826,22 @@ fn parse_create_table_with_variant_default_expressions() { data_type: DataType::Datetime(None), options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Materialized(Expr::Function(Box::new(Function { - name: ObjectName::from(vec![Ident::new("now")]), - uses_odbc_syntax: false, - args: FunctionArguments::List(FunctionArgumentList { - args: vec![], - duplicate_treatment: None, - clauses: vec![], - }), - parameters: FunctionArguments::None, - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - }))) + option: ColumnOption::Materialized(Expr::Function(Box::new( + Function { + name: ObjectName::from(vec![Ident::new("now")]), + uses_odbc_syntax: false, + args: FunctionArguments::List(FunctionArgumentList { + args: vec![], + duplicate_treatment: None, + clauses: vec![], + }), + parameters: FunctionArguments::None, + null_treatment: None, + filter: None, + over: None, + within_group: vec![], + } + ))) }], }, ColumnDef { @@ -847,20 +849,22 @@ fn parse_create_table_with_variant_default_expressions() { data_type: DataType::Datetime(None), options: vec![ColumnOptionDef { name: None, - option: ColumnOption::Ephemeral(Some(Expr::Function(Box::new(Function { - name: ObjectName::from(vec![Ident::new("now")]), - uses_odbc_syntax: false, - args: FunctionArguments::List(FunctionArgumentList { - args: vec![], - duplicate_treatment: None, - clauses: vec![], - }), - parameters: FunctionArguments::None, - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - })))) + option: ColumnOption::Ephemeral(Some(Expr::Function(Box::new( + Function { + name: ObjectName::from(vec![Ident::new("now")]), + uses_odbc_syntax: false, + args: FunctionArguments::List(FunctionArgumentList { + args: vec![], + duplicate_treatment: None, + clauses: vec![], + }), + parameters: FunctionArguments::None, + null_treatment: None, + filter: None, + over: None, + within_group: vec![], + } + )))) }], }, ColumnDef { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 7f45bdc7d1..de68c898f1 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1336,20 +1336,23 @@ fn parse_select_count_wildcard() { let sql = "SELECT COUNT(*) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("COUNT")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: None, - within_group: vec![] - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("COUNT")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: None, + within_group: vec![] + } + .into() + ), expr_from_projection(only(&select.projection)) ); } @@ -1359,23 +1362,26 @@ fn parse_select_count_distinct() { let sql = "SELECT COUNT(DISTINCT +x) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("COUNT")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: Some(DuplicateTreatment::Distinct), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::UnaryOp { - op: UnaryOperator::Plus, - expr: Box::new(Expr::Identifier(Ident::new("x"))), - }))], - clauses: vec![], - }), - null_treatment: None, - within_group: vec![], - filter: None, - over: None - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("COUNT")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: Some(DuplicateTreatment::Distinct), + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::UnaryOp { + op: UnaryOperator::Plus, + expr: Box::new(Expr::Identifier(Ident::new("x"))), + }))], + clauses: vec![], + }), + null_treatment: None, + within_group: vec![], + filter: None, + over: None + } + .into() + ), expr_from_projection(only(&select.projection)) ); @@ -1719,7 +1725,10 @@ fn parse_json_object() { Box::new(PostgreSqlDialect {}), ]); let select = dialects.verified_only_select("SELECT JSON_OBJECT('name' : 'value', 'type' : 1)"); - match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + match expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function") + { Function { args: FunctionArguments::List(FunctionArgumentList { args, .. }), .. @@ -1744,7 +1753,10 @@ fn parse_json_object() { } let select = dialects .verified_only_select("SELECT JSON_OBJECT('name' : 'value', 'type' : NULL ABSENT ON NULL)"); - match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + match expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function") + { Function { args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. @@ -1780,7 +1792,10 @@ fn parse_json_object() { _ => unreachable!(), } let select = dialects.verified_only_select("SELECT JSON_OBJECT(NULL ON NULL)"); - match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + match expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function") + { Function { args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. @@ -1796,7 +1811,10 @@ fn parse_json_object() { _ => unreachable!(), } let select = dialects.verified_only_select("SELECT JSON_OBJECT(ABSENT ON NULL)"); - match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + match expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function") + { Function { args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. @@ -1814,7 +1832,10 @@ fn parse_json_object() { let select = dialects.verified_only_select( "SELECT JSON_OBJECT('name' : 'value', 'type' : JSON_ARRAY(1, 2) ABSENT ON NULL)", ); - match expr_from_projection(&select.projection[0]).as_function().expect("not a function") { + match expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function") + { Function { args: FunctionArguments::List(FunctionArgumentList { args, clauses, .. }), .. @@ -1852,8 +1873,12 @@ fn parse_json_object() { let select = dialects.verified_only_select( "SELECT JSON_OBJECT('name' : 'value', 'type' : JSON_OBJECT('type_id' : 1, 'name' : 'a') NULL ON NULL)", ); - let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + let Function { args, .. } = expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { + panic!("not a function arg list"); + }; assert_eq!( &FunctionArg::ExprNamed { name: Expr::Value((Value::SingleQuotedString("name".into())).with_empty_span()), @@ -2960,20 +2985,23 @@ fn parse_select_having() { let select = verified_only_select(sql); assert_eq!( Some(Expr::BinaryOp { - left: Box::new(Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("COUNT")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: None, - within_group: vec![] - }.into())), + left: Box::new(Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("COUNT")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: None, + within_group: vec![] + } + .into() + )), op: BinaryOperator::Gt, right: Box::new(Expr::value(number("1"))), }), @@ -2991,32 +3019,35 @@ fn parse_select_qualify() { let select = verified_only_select(sql); assert_eq!( Some(Expr::BinaryOp { - left: Box::new(Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("ROW_NUMBER")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: Some(WindowType::WindowSpec(WindowSpec { - window_name: None, - partition_by: vec![Expr::Identifier(Ident::new("p"))], - order_by: vec![OrderByExpr { - expr: Expr::Identifier(Ident::new("o")), - options: OrderByOptions { - asc: None, - nulls_first: None, - }, - with_fill: None, - }], - window_frame: None, - })), - within_group: vec![] - }.into())), + left: Box::new(Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("ROW_NUMBER")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: Some(WindowType::WindowSpec(WindowSpec { + window_name: None, + partition_by: vec![Expr::Identifier(Ident::new("p"))], + order_by: vec![OrderByExpr { + expr: Expr::Identifier(Ident::new("o")), + options: OrderByOptions { + asc: None, + nulls_first: None, + }, + with_fill: None, + }], + window_frame: None, + })), + within_group: vec![] + } + .into() + )), op: BinaryOperator::Eq, right: Box::new(Expr::value(number("1"))), }), @@ -3419,59 +3450,62 @@ fn parse_listagg() { )); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("LISTAGG")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: Some(DuplicateTreatment::Distinct), - args: vec![ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(Ident::new( - "dateid" - )))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString(", ".to_owned())).with_empty_span() - ))) - ], - clauses: vec![FunctionArgumentClause::OnOverflow( - ListAggOnOverflow::Truncate { - filler: Some(Box::new(Expr::Value( - (Value::SingleQuotedString("%".to_string(),)).with_empty_span() - ))), - with_count: false, - } - )], - }), - filter: None, - null_treatment: None, - over: None, - within_group: vec![ - OrderByExpr { - expr: Expr::Identifier(Ident { - value: "id".to_string(), - quote_style: None, - span: Span::empty(), - }), - options: OrderByOptions { - asc: None, - nulls_first: None, + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("LISTAGG")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: Some(DuplicateTreatment::Distinct), + args: vec![ + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(Ident::new( + "dateid" + )))), + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString(", ".to_owned())).with_empty_span() + ))) + ], + clauses: vec![FunctionArgumentClause::OnOverflow( + ListAggOnOverflow::Truncate { + filler: Some(Box::new(Expr::Value( + (Value::SingleQuotedString("%".to_string(),)).with_empty_span() + ))), + with_count: false, + } + )], + }), + filter: None, + null_treatment: None, + over: None, + within_group: vec![ + OrderByExpr { + expr: Expr::Identifier(Ident { + value: "id".to_string(), + quote_style: None, + span: Span::empty(), + }), + options: OrderByOptions { + asc: None, + nulls_first: None, + }, + with_fill: None, }, - with_fill: None, - }, - OrderByExpr { - expr: Expr::Identifier(Ident { - value: "username".to_string(), - quote_style: None, - span: Span::empty(), - }), - options: OrderByOptions { - asc: None, - nulls_first: None, + OrderByExpr { + expr: Expr::Identifier(Ident { + value: "username".to_string(), + quote_style: None, + span: Span::empty(), + }), + options: OrderByOptions { + asc: None, + nulls_first: None, + }, + with_fill: None, }, - with_fill: None, - }, - ] - }.into()), + ] + } + .into() + ), expr_from_projection(only(&select.projection)) ); @@ -5592,35 +5626,38 @@ fn parse_named_argument_function() { let select = dialects.verified_only_select(sql); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("FUN")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![ - FunctionArg::Named { - name: Ident::new("a"), - arg: FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString("1".to_owned())).with_empty_span() - )), - operator: FunctionArgOperator::RightArrow - }, - FunctionArg::Named { - name: Ident::new("b"), - arg: FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString("2".to_owned())).with_empty_span() - )), - operator: FunctionArgOperator::RightArrow - }, - ], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: None, - within_group: vec![] - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("FUN")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![ + FunctionArg::Named { + name: Ident::new("a"), + arg: FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString("1".to_owned())).with_empty_span() + )), + operator: FunctionArgOperator::RightArrow + }, + FunctionArg::Named { + name: Ident::new("b"), + arg: FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString("2".to_owned())).with_empty_span() + )), + operator: FunctionArgOperator::RightArrow + }, + ], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: None, + within_group: vec![] + } + .into() + ), expr_from_projection(only(&select.projection)) ); } @@ -5632,35 +5669,38 @@ fn parse_named_argument_function_with_eq_operator() { let select = all_dialects_where(|d| d.supports_named_fn_args_with_eq_operator()) .verified_only_select(sql); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("FUN")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![ - FunctionArg::Named { - name: Ident::new("a"), - arg: FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString("1".to_owned())).with_empty_span() - )), - operator: FunctionArgOperator::Equals - }, - FunctionArg::Named { - name: Ident::new("b"), - arg: FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString("2".to_owned())).with_empty_span() - )), - operator: FunctionArgOperator::Equals - }, - ], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("FUN")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![ + FunctionArg::Named { + name: Ident::new("a"), + arg: FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString("1".to_owned())).with_empty_span() + )), + operator: FunctionArgOperator::Equals + }, + FunctionArg::Named { + name: Ident::new("b"), + arg: FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString("2".to_owned())).with_empty_span() + )), + operator: FunctionArgOperator::Equals + }, + ], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: None, + within_group: vec![], + } + .into() + ), expr_from_projection(only(&select.projection)) ); @@ -5707,32 +5747,35 @@ fn parse_window_functions() { assert_eq!(EXPECTED_PROJ_QTY, select.projection.len()); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("row_number")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: Some(WindowType::WindowSpec(WindowSpec { - window_name: None, - partition_by: vec![], - order_by: vec![OrderByExpr { - expr: Expr::Identifier(Ident::new("dt")), - options: OrderByOptions { - asc: Some(false), - nulls_first: None, - }, - with_fill: None, - }], - window_frame: None, - })), - within_group: vec![], - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("row_number")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: Some(WindowType::WindowSpec(WindowSpec { + window_name: None, + partition_by: vec![], + order_by: vec![OrderByExpr { + expr: Expr::Identifier(Ident::new("dt")), + options: OrderByOptions { + asc: Some(false), + nulls_first: None, + }, + with_fill: None, + }], + window_frame: None, + })), + within_group: vec![], + } + .into() + ), expr_from_projection(&select.projection[0]) ); @@ -5845,34 +5888,37 @@ fn test_parse_named_window() { top_before_distinct: false, projection: vec![ SelectItem::ExprWithAlias { - expr: Expr::Function(Function { - name: ObjectName::from(vec![Ident { - value: "MIN".to_string(), - quote_style: None, - span: Span::empty(), - }]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident { - value: "c12".to_string(), - quote_style: None, - span: Span::empty(), - }), - ))], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: Some(WindowType::NamedWindow(Ident { - value: "window1".to_string(), - quote_style: None, - span: Span::empty(), - })), - within_group: vec![], - }.into()), + expr: Expr::Function( + Function { + name: ObjectName::from(vec![Ident { + value: "MIN".to_string(), + quote_style: None, + span: Span::empty(), + }]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident { + value: "c12".to_string(), + quote_style: None, + span: Span::empty(), + }), + ))], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: Some(WindowType::NamedWindow(Ident { + value: "window1".to_string(), + quote_style: None, + span: Span::empty(), + })), + within_group: vec![], + } + .into(), + ), alias: Ident { value: "min1".to_string(), quote_style: None, @@ -5880,34 +5926,37 @@ fn test_parse_named_window() { }, }, SelectItem::ExprWithAlias { - expr: Expr::Function(Function { - name: ObjectName::from(vec![Ident { - value: "MAX".to_string(), - quote_style: None, - span: Span::empty(), - }]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident { - value: "c12".to_string(), - quote_style: None, - span: Span::empty(), - }), - ))], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: Some(WindowType::NamedWindow(Ident { - value: "window2".to_string(), - quote_style: None, - span: Span::empty(), - })), - within_group: vec![], - }.into()), + expr: Expr::Function( + Function { + name: ObjectName::from(vec![Ident { + value: "MAX".to_string(), + quote_style: None, + span: Span::empty(), + }]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident { + value: "c12".to_string(), + quote_style: None, + span: Span::empty(), + }), + ))], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: Some(WindowType::NamedWindow(Ident { + value: "window2".to_string(), + quote_style: None, + span: Span::empty(), + })), + within_group: vec![], + } + .into(), + ), alias: Ident { value: "max1".to_string(), quote_style: None, @@ -12681,25 +12730,28 @@ fn parse_map_access_expr() { }, }), AccessExpr::Subscript(Subscript::Index { - index: Expr::Function(Function { - name: ObjectName::from(vec![Ident::with_span( - Span::new(Location::of(1, 11), Location::of(1, 22)), - "safe_offset", - )]), - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (number("2")).with_empty_span(), - )))], - clauses: vec![], - }), - filter: None, - null_treatment: None, - over: None, - within_group: vec![], - uses_odbc_syntax: false, - }.into()), + index: Expr::Function( + Function { + name: ObjectName::from(vec![Ident::with_span( + Span::new(Location::of(1, 11), Location::of(1, 22)), + "safe_offset", + )]), + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (number("2")).with_empty_span(), + )))], + clauses: vec![], + }), + filter: None, + null_treatment: None, + over: None, + within_group: vec![], + uses_odbc_syntax: false, + } + .into(), + ), }), ], }; @@ -13023,26 +13075,8 @@ fn test_selective_aggregation() { assert_eq!( testing_dialects.verified_only_select(sql).projection, vec![ - SelectItem::UnnamedExpr(Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("ARRAY_AGG")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("name")) - ))], - clauses: vec![], - }), - filter: Some(Box::new(Expr::IsNotNull(Box::new(Expr::Identifier( - Ident::new("name") - ))))), - over: None, - within_group: vec![], - null_treatment: None - }.into())), - SelectItem::ExprWithAlias { - expr: Expr::Function(Function { + SelectItem::UnnamedExpr(Expr::Function( + Function { name: ObjectName::from(vec![Ident::new("ARRAY_AGG")]), uses_odbc_syntax: false, parameters: FunctionArguments::None, @@ -13053,19 +13087,43 @@ fn test_selective_aggregation() { ))], clauses: vec![], }), - filter: Some(Box::new(Expr::Like { - negated: false, - expr: Box::new(Expr::Identifier(Ident::new("name"))), - pattern: Box::new(Expr::Value( - (Value::SingleQuotedString("a%".to_owned())).with_empty_span() - )), - escape_char: None, - any: false, - })), - null_treatment: None, + filter: Some(Box::new(Expr::IsNotNull(Box::new(Expr::Identifier( + Ident::new("name") + ))))), over: None, - within_group: vec![] - }.into()), + within_group: vec![], + null_treatment: None + } + .into() + )), + SelectItem::ExprWithAlias { + expr: Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("ARRAY_AGG")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident::new("name")) + ))], + clauses: vec![], + }), + filter: Some(Box::new(Expr::Like { + negated: false, + expr: Box::new(Expr::Identifier(Ident::new("name"))), + pattern: Box::new(Expr::Value( + (Value::SingleQuotedString("a%".to_owned())).with_empty_span() + )), + escape_char: None, + any: false, + })), + null_treatment: None, + over: None, + within_group: vec![] + } + .into() + ), alias: Ident::new("agg2") }, ] @@ -13527,7 +13585,9 @@ fn parse_odbc_scalar_function() { uses_odbc_syntax, args, .. - } = expr_from_projection(only(&select.projection)).as_function().expect("not a function"); + } = expr_from_projection(only(&select.projection)) + .as_function() + .expect("not a function"); assert_eq!(name, &ObjectName::from(vec![Ident::new("my_func")])); assert!(uses_odbc_syntax); matches!(args, FunctionArguments::List(l) if l.args.len() == 2); @@ -15416,22 +15476,25 @@ fn parse_composite_access_expr() { assert_eq!( verified_expr("f(a).b"), Expr::CompoundFieldAccess { - root: Box::new(Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("f")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("a")) - ))], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: None, - within_group: vec![] - }.into())), + root: Box::new(Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("f")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident::new("a")) + ))], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: None, + within_group: vec![] + } + .into() + )), access_chain: vec![AccessExpr::Dot(Expr::Identifier(Ident::new("b")))] } ); @@ -15440,48 +15503,54 @@ fn parse_composite_access_expr() { assert_eq!( verified_expr("f(a).b.c"), Expr::CompoundFieldAccess { - root: Box::new(Expr::Function(Function { + root: Box::new(Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("f")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident::new("a")) + ))], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: None, + within_group: vec![] + } + .into() + )), + access_chain: vec![ + AccessExpr::Dot(Expr::Identifier(Ident::new("b"))), + AccessExpr::Dot(Expr::Identifier(Ident::new("c"))), + ] + } + ); + + // Composite Access in Select and Where Clauses + let stmt = verified_only_select("SELECT f(a).b FROM t WHERE f(a).b IS NOT NULL"); + let expr = Expr::CompoundFieldAccess { + root: Box::new(Expr::Function( + Function { name: ObjectName::from(vec![Ident::new("f")]), uses_odbc_syntax: false, parameters: FunctionArguments::None, args: FunctionArguments::List(FunctionArgumentList { duplicate_treatment: None, args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("a")) + Expr::Identifier(Ident::new("a")), ))], clauses: vec![], }), null_treatment: None, filter: None, over: None, - within_group: vec![] - }.into())), - access_chain: vec![ - AccessExpr::Dot(Expr::Identifier(Ident::new("b"))), - AccessExpr::Dot(Expr::Identifier(Ident::new("c"))), - ] - } - ); - - // Composite Access in Select and Where Clauses - let stmt = verified_only_select("SELECT f(a).b FROM t WHERE f(a).b IS NOT NULL"); - let expr = Expr::CompoundFieldAccess { - root: Box::new(Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("f")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("a")), - ))], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - }.into())), + within_group: vec![], + } + .into(), + )), access_chain: vec![AccessExpr::Dot(Expr::Identifier(Ident::new("b")))], }; diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index a82f27137a..1eaa3a8b08 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -979,8 +979,12 @@ fn parse_mssql_json_object() { let select = ms().verified_only_select( "SELECT JSON_OBJECT('user_name' : USER_NAME(), LOWER(@id_key) : @id_value, 'sid' : (SELECT @@SPID) ABSENT ON NULL)", ); - let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function-arg-list"); }; + let Function { args, .. } = expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { + panic!("not a function-arg-list"); + }; assert!(matches!( args[0], FunctionArg::ExprNamed { @@ -1023,10 +1027,14 @@ fn parse_mssql_json_object() { FROM sys.dm_exec_sessions AS s \ WHERE s.is_user_process = 1", ); - - let SelectItem::ExprWithAlias { expr, .. } = &select.projection[1] else { panic!("not an expr-with-alias"); }; + + let SelectItem::ExprWithAlias { expr, .. } = &select.projection[1] else { + panic!("not an expr-with-alias"); + }; let Function { args, .. } = expr.as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { panic!(" not an function-arg-list"); }; + let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { + panic!(" not an function-arg-list"); + }; assert!(matches!( args[0], FunctionArg::ExprNamed { @@ -1065,8 +1073,12 @@ fn parse_mssql_json_object() { #[test] fn parse_mssql_json_array() { let select = ms().verified_only_select("SELECT JSON_ARRAY('a', 1, NULL, 2 NULL ON NULL)"); - let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function-arg-list"); }; + let Function { args, .. } = expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { + panic!("not a function-arg-list"); + }; assert_eq!( &[ FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( @@ -1092,8 +1104,12 @@ fn parse_mssql_json_array() { ); let select = ms().verified_only_select("SELECT JSON_ARRAY('a', 1, NULL, 2 ABSENT ON NULL)"); - let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + let Function { args, .. } = expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { + panic!("not a function arg list"); + }; assert_eq!( &[ FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( @@ -1119,8 +1135,12 @@ fn parse_mssql_json_array() { ); let select = ms().verified_only_select("SELECT JSON_ARRAY(NULL ON NULL)"); - let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + let Function { args, .. } = expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { + panic!("not a function arg list"); + }; assert!(args.is_empty()); assert_eq!( &[FunctionArgumentClause::JsonNullClause( @@ -1130,8 +1150,12 @@ fn parse_mssql_json_array() { ); let select = ms().verified_only_select("SELECT JSON_ARRAY(ABSENT ON NULL)"); - let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + let Function { args, .. } = expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { + panic!("not a function arg list"); + }; assert!(args.is_empty()); assert_eq!( &[FunctionArgumentClause::JsonNullClause( @@ -1143,8 +1167,12 @@ fn parse_mssql_json_array() { let select = ms().verified_only_select( "SELECT JSON_ARRAY('a', JSON_OBJECT('name' : 'value', 'type' : 1) NULL ON NULL)", ); - let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function arg list"); }; + let Function { args, .. } = expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { + panic!("not a function arg list"); + }; assert_eq!( &FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( (Value::SingleQuotedString("a".into())).with_empty_span() @@ -1165,26 +1193,34 @@ fn parse_mssql_json_array() { let select = ms().verified_only_select( "SELECT JSON_ARRAY('a', JSON_OBJECT('name' : 'value', 'type' : 1), JSON_ARRAY(1, NULL, 2 NULL ON NULL))", ); - let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { panic!("not a function arg list"); }; + let Function { args, .. } = expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { + panic!("not a function arg list"); + }; assert_eq!( - &FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - (Value::SingleQuotedString("a".into())).with_empty_span() - ))), - &args[0] - ); - assert!(matches!( - args[1], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_))) - )); - assert!(matches!( - args[2], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_))) - )); + &FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + (Value::SingleQuotedString("a".into())).with_empty_span() + ))), + &args[0] + ); + assert!(matches!( + args[1], + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_))) + )); + assert!(matches!( + args[2], + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function(_))) + )); let select = ms().verified_only_select("SELECT JSON_ARRAY(1, @id_value, (SELECT @@SPID))"); - let Function { args, .. } = expr_from_projection(&select.projection[0]).as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { panic!("not a function-arg-list"); }; + let Function { args, .. } = expr_from_projection(&select.projection[0]) + .as_function() + .expect("not a function"); + let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { + panic!("not a function-arg-list"); + }; assert_eq!( &FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( (number("1")).with_empty_span() @@ -1205,10 +1241,14 @@ fn parse_mssql_json_array() { FROM sys.dm_exec_sessions AS s \ WHERE s.is_user_process = 1", ); - - let SelectItem::ExprWithAlias { expr, ..}= &select.projection[1] else { panic!("not an expr-with-alias"); }; + + let SelectItem::ExprWithAlias { expr, .. } = &select.projection[1] else { + panic!("not an expr-with-alias"); + }; let Function { args, .. } = expr.as_function().expect("not a function"); - let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { panic!("not a function-arg-list"); }; + let FunctionArguments::List(FunctionArgumentList { args, clauses, .. }) = args else { + panic!("not a function-arg-list"); + }; assert!(matches!( args[0], FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::CompoundIdentifier(_))) diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 3e613068d5..3f27f84d1e 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -835,7 +835,9 @@ fn parse_prefix_key_part() { let expr = index_column(mysql_and_generic().verified_stmt(sql)); let Function { name, args, .. } = expr.as_function().expect("not a function"); assert_eq!(name.to_string(), "textcol"); - let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { panic!("not a function arg list"); }; + let FunctionArguments::List(FunctionArgumentList { args, .. }) = args else { + panic!("not a function arg list"); + }; assert_eq!(args, &expected); } } diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 2a419e25c7..075e367878 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -2744,39 +2744,42 @@ fn parse_create_indices_with_operator_classes() { let expected_function_column = IndexColumn { column: OrderByExpr { - expr: Expr::Function(Function { - name: ObjectName(vec![ObjectNamePart::Identifier(Ident { - value: "concat_users_name".to_owned(), - quote_style: None, - span: Span::empty(), - })]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier( - Ident { - value: "first_name".to_owned(), - quote_style: None, - span: Span::empty(), - }, - ))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier( - Ident { - value: "last_name".to_owned(), - quote_style: None, - span: Span::empty(), - }, - ))), - ], - clauses: vec![], - }), - filter: None, - null_treatment: None, - over: None, - within_group: vec![], - }.into()), + expr: Expr::Function( + Function { + name: ObjectName(vec![ObjectNamePart::Identifier(Ident { + value: "concat_users_name".to_owned(), + quote_style: None, + span: Span::empty(), + })]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![ + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier( + Ident { + value: "first_name".to_owned(), + quote_style: None, + span: Span::empty(), + }, + ))), + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier( + Ident { + value: "last_name".to_owned(), + quote_style: None, + span: Span::empty(), + }, + ))), + ], + clauses: vec![], + }), + filter: None, + null_treatment: None, + over: None, + within_group: vec![], + } + .into(), + ), options: OrderByOptions { asc: None, nulls_first: None, @@ -3196,86 +3199,89 @@ fn parse_array_subquery_expr() { let sql = "SELECT ARRAY(SELECT 1 UNION SELECT 2)"; let select = pg().verified_only_select(sql); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("ARRAY")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::Subquery(Box::new(Query { - with: None, - body: Box::new(SetExpr::SetOperation { - op: SetOperator::Union, - set_quantifier: SetQuantifier::None, - left: Box::new(SetExpr::Select(Box::new(Select { - select_token: AttachedToken::empty(), - optimizer_hints: vec![], - distinct: None, - select_modifiers: None, - top: None, - top_before_distinct: false, - projection: vec![SelectItem::UnnamedExpr(Expr::Value( - (number("1")).with_empty_span() - ))], - exclude: None, - into: None, - from: vec![], - lateral_views: vec![], - prewhere: None, - selection: None, - group_by: GroupByExpr::Expressions(vec![], vec![]), - cluster_by: vec![], - distribute_by: vec![], - sort_by: vec![], - having: None, - named_window: vec![], - qualify: None, - window_before_qualify: false, - value_table_mode: None, - connect_by: vec![], - flavor: SelectFlavor::Standard, - }))), - right: Box::new(SetExpr::Select(Box::new(Select { - select_token: AttachedToken::empty(), - optimizer_hints: vec![], - distinct: None, - select_modifiers: None, - top: None, - top_before_distinct: false, - projection: vec![SelectItem::UnnamedExpr(Expr::Value( - (number("2")).with_empty_span() - ))], - exclude: None, - into: None, - from: vec![], - lateral_views: vec![], - prewhere: None, - selection: None, - group_by: GroupByExpr::Expressions(vec![], vec![]), - cluster_by: vec![], - distribute_by: vec![], - sort_by: vec![], - having: None, - named_window: vec![], - qualify: None, - window_before_qualify: false, - value_table_mode: None, - connect_by: vec![], - flavor: SelectFlavor::Standard, - }))), - }), - order_by: None, - limit_clause: None, - fetch: None, - locks: vec![], - for_clause: None, - settings: None, - format_clause: None, - pipe_operators: vec![], - })), - filter: None, - null_treatment: None, - over: None, - within_group: vec![] - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("ARRAY")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::Subquery(Box::new(Query { + with: None, + body: Box::new(SetExpr::SetOperation { + op: SetOperator::Union, + set_quantifier: SetQuantifier::None, + left: Box::new(SetExpr::Select(Box::new(Select { + select_token: AttachedToken::empty(), + optimizer_hints: vec![], + distinct: None, + select_modifiers: None, + top: None, + top_before_distinct: false, + projection: vec![SelectItem::UnnamedExpr(Expr::Value( + (number("1")).with_empty_span() + ))], + exclude: None, + into: None, + from: vec![], + lateral_views: vec![], + prewhere: None, + selection: None, + group_by: GroupByExpr::Expressions(vec![], vec![]), + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + qualify: None, + window_before_qualify: false, + value_table_mode: None, + connect_by: vec![], + flavor: SelectFlavor::Standard, + }))), + right: Box::new(SetExpr::Select(Box::new(Select { + select_token: AttachedToken::empty(), + optimizer_hints: vec![], + distinct: None, + select_modifiers: None, + top: None, + top_before_distinct: false, + projection: vec![SelectItem::UnnamedExpr(Expr::Value( + (number("2")).with_empty_span() + ))], + exclude: None, + into: None, + from: vec![], + lateral_views: vec![], + prewhere: None, + selection: None, + group_by: GroupByExpr::Expressions(vec![], vec![]), + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + qualify: None, + window_before_qualify: false, + value_table_mode: None, + connect_by: vec![], + flavor: SelectFlavor::Standard, + }))), + }), + order_by: None, + limit_clause: None, + fetch: None, + locks: vec![], + for_clause: None, + settings: None, + format_clause: None, + pipe_operators: vec![], + })), + filter: None, + null_treatment: None, + over: None, + within_group: vec![] + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -3692,35 +3698,40 @@ fn test_composite_value() { let select = pg().verified_only_select(sql); assert_eq!( &Expr::CompoundFieldAccess { - root: Box::new(Expr::Nested(Box::new(Expr::Function(Function { - name: ObjectName::from(vec![ - Ident::new("information_schema"), - Ident::new("_pg_expandarray") - ]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Array( - Array { - elem: vec![ - Expr::Value( - (Value::SingleQuotedString("i".to_string())).with_empty_span() - ), - Expr::Value( - (Value::SingleQuotedString("i".to_string())).with_empty_span() - ), - ], - named: true - } - )))], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - }.into())))), + root: Box::new(Expr::Nested(Box::new(Expr::Function( + Function { + name: ObjectName::from(vec![ + Ident::new("information_schema"), + Ident::new("_pg_expandarray") + ]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Array( + Array { + elem: vec![ + Expr::Value( + (Value::SingleQuotedString("i".to_string())) + .with_empty_span() + ), + Expr::Value( + (Value::SingleQuotedString("i".to_string())) + .with_empty_span() + ), + ], + named: true + } + )))], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: None, + within_group: vec![], + } + .into() + )))), access_chain: vec![AccessExpr::Dot(Expr::Identifier(Ident::new("n")))], }, expr_from_projection(&select.projection[0]) @@ -3878,55 +3889,67 @@ fn parse_current_functions() { let sql = "SELECT CURRENT_CATALOG, CURRENT_USER, SESSION_USER, USER"; let select = pg_and_generic().verified_only_select(sql); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("CURRENT_CATALOG")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::None, - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("CURRENT_CATALOG")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::None, + null_treatment: None, + filter: None, + over: None, + within_group: vec![], + } + .into() + ), expr_from_projection(&select.projection[0]) ); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("CURRENT_USER")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::None, - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("CURRENT_USER")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::None, + null_treatment: None, + filter: None, + over: None, + within_group: vec![], + } + .into() + ), expr_from_projection(&select.projection[1]) ); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("SESSION_USER")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::None, - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("SESSION_USER")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::None, + null_treatment: None, + filter: None, + over: None, + within_group: vec![], + } + .into() + ), expr_from_projection(&select.projection[2]) ); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("USER")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::None, - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("USER")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::None, + null_treatment: None, + filter: None, + over: None, + within_group: vec![], + } + .into() + ), expr_from_projection(&select.projection[3]) ); } @@ -4365,20 +4388,23 @@ fn parse_delimited_identifiers() { expr_from_projection(&select.projection[0]), ); assert_eq!( - &Expr::Function(Function { - name: ObjectName::from(vec![Ident::with_quote('"', "myfun")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![], - clauses: vec![], - }), - null_treatment: None, - filter: None, - over: None, - within_group: vec![], - }.into()), + &Expr::Function( + Function { + name: ObjectName::from(vec![Ident::with_quote('"', "myfun")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), + null_treatment: None, + filter: None, + over: None, + within_group: vec![], + } + .into() + ), expr_from_projection(&select.projection[1]), ); match &select.projection[2] { diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index bf6efc0611..58d050c8f8 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -533,22 +533,25 @@ fn test_snowflake_create_table_cluster_by() { Some(WrappedCollection::Parentheses(vec![ Expr::Identifier(Ident::new("a")), Expr::Identifier(Ident::new("b")), - Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("my_func")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("c")) - ))], - duplicate_treatment: None, - clauses: vec![], - }), - filter: None, - null_treatment: None, - over: None, - within_group: vec![], - }.into()), + Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("my_func")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident::new("c")) + ))], + duplicate_treatment: None, + clauses: vec![], + }), + filter: None, + null_treatment: None, + over: None, + within_group: vec![], + } + .into() + ), ])), cluster_by ) @@ -1653,22 +1656,25 @@ fn test_alter_table_clustering() { [ Expr::Identifier(Ident::new("c1")), Expr::Identifier(Ident::with_quote('"', "c2")), - Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("TO_DATE")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("c3")) - ))], - duplicate_treatment: None, - clauses: vec![], - }), - filter: None, - null_treatment: None, - over: None, - within_group: vec![] - }.into()) + Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new("TO_DATE")]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident::new("c3")) + ))], + duplicate_treatment: None, + clauses: vec![], + }), + filter: None, + null_treatment: None, + over: None, + within_group: vec![] + } + .into() + ) ], ); } @@ -4757,7 +4763,10 @@ fn test_snowflake_identifier_function() { // Using IDENTIFIER to reference a column let SelectItem::UnnamedExpr(expr) = &snowflake() .verified_only_select("SELECT identifier('email') FROM customers") - .projection[0] else { panic!("not an unnamed expression"); }; + .projection[0] + else { + panic!("not an unnamed expression"); + }; let Function { name, args, .. } = expr.as_function().expect("not a function"); assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); assert_eq!( @@ -4774,7 +4783,10 @@ fn test_snowflake_identifier_function() { // Using IDENTIFIER to reference a case-sensitive column let SelectItem::UnnamedExpr(expr) = &snowflake() .verified_only_select(r#"SELECT identifier('"Email"') FROM customers"#) - .projection[0] else { panic!("not an unnamed expression"); }; + .projection[0] + else { + panic!("not an unnamed expression"); + }; let Function { name, args, .. } = expr.as_function().expect("not a function"); assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); assert_eq!( @@ -4789,10 +4801,14 @@ fn test_snowflake_identifier_function() { ); // Using IDENTIFIER to reference an alias of a table - let SelectItem::QualifiedWildcard(SelectItemQualifiedWildcardKind::Expr(expr), _) = &snowflake() - .verified_only_select("SELECT identifier('alias1').* FROM tbl AS alias1") - .projection[0] else { panic!("not a qualified wildcard"); }; - let Function { name, args, .. }= expr.as_function().expect("not a function"); + let SelectItem::QualifiedWildcard(SelectItemQualifiedWildcardKind::Expr(expr), _) = + &snowflake() + .verified_only_select("SELECT identifier('alias1').* FROM tbl AS alias1") + .projection[0] + else { + panic!("not a qualified wildcard"); + }; + let Function { name, args, .. } = expr.as_function().expect("not a function"); assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); assert_eq!( *args, diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 4d8600569e..b5049a0a92 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -424,27 +424,30 @@ fn parse_window_function_with_filter() { assert_eq!(select.to_string(), sql); assert_eq!( select.projection, - vec![SelectItem::UnnamedExpr(Expr::Function(Function { - name: ObjectName::from(vec![Ident::new(func_name)]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - duplicate_treatment: None, - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("x")) - ))], - clauses: vec![], - }), - null_treatment: None, - over: Some(WindowType::WindowSpec(WindowSpec { - window_name: None, - partition_by: vec![], - order_by: vec![], - window_frame: None, - })), - filter: Some(Box::new(Expr::Identifier(Ident::new("y")))), - within_group: vec![], - }.into()))] + vec![SelectItem::UnnamedExpr(Expr::Function( + Function { + name: ObjectName::from(vec![Ident::new(func_name)]), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident::new("x")) + ))], + clauses: vec![], + }), + null_treatment: None, + over: Some(WindowType::WindowSpec(WindowSpec { + window_name: None, + partition_by: vec![], + order_by: vec![], + window_frame: None, + })), + filter: Some(Box::new(Expr::Identifier(Ident::new("y")))), + within_group: vec![], + } + .into() + ))] ); } } From ce91626caad249ada3aa469dce1239a5c6145250 Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Fri, 6 Mar 2026 21:07:40 +0100 Subject: [PATCH 3/5] Fix docs tests --- src/ast/visitor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/visitor.rs b/src/ast/visitor.rs index 5f9b374896..28d5d043df 100644 --- a/src/ast/visitor.rs +++ b/src/ast/visitor.rs @@ -598,7 +598,7 @@ where /// over: None, /// parameters: FunctionArguments::None, /// within_group: vec![], -/// }); +/// }.into()); /// } /// ControlFlow::<()>::Continue(()) /// }); From c3f6af2bc3ca1dcb631be99813a0f3f9e8908b85 Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Sun, 15 Mar 2026 10:57:33 +0100 Subject: [PATCH 4/5] Further reduce Expr to 112 bytes in size --- src/ast/mod.rs | 236 ++++++++++++++------------- src/ast/spans.rs | 82 +++++----- src/parser/mod.rs | 72 ++++----- tests/sqlparser_bigquery.rs | 38 ++--- tests/sqlparser_common.rs | 291 +++++++++++++++++----------------- tests/sqlparser_databricks.rs | 20 +-- tests/sqlparser_duckdb.rs | 8 +- tests/sqlparser_mssql.rs | 13 +- tests/sqlparser_mysql.rs | 16 +- tests/sqlparser_postgres.rs | 48 +++--- tests/sqlparser_snowflake.rs | 18 +-- 11 files changed, 426 insertions(+), 416 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 768e49c689..0e8f10220f 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -460,7 +460,7 @@ impl fmt::Display for Array { #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct Interval { /// The interval value expression (commonly a string literal). - pub value: Box, + pub value: Expr, /// Optional leading time unit (e.g., `HOUR`, `MINUTE`). pub leading_field: Option, /// Optional leading precision for the leading field. @@ -475,7 +475,7 @@ pub struct Interval { impl fmt::Display for Interval { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let value = self.value.as_ref(); + let value = &self.value; match ( &self.leading_field, self.leading_precision, @@ -1025,44 +1025,9 @@ pub enum Expr { expr: Box, }, /// CONVERT a value to a different data type or character encoding. e.g. `CONVERT(foo USING utf8mb4)` - // XXX too big - Convert { - /// CONVERT (false) or TRY_CONVERT (true) - /// - is_try: bool, - /// The expression to convert. - expr: Box, - /// The target data type, if provided. - data_type: Option, - /// Optional target character encoding (e.g., `utf8mb4`). - charset: Option, - /// `true` when target precedes the value (MSSQL syntax). - target_before_value: bool, - /// How to translate the expression. - /// - /// [MSSQL]: https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16#style - styles: Vec, - }, + Convert(Box), /// `CAST` an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))` - // XXX too big - Cast { - /// The cast kind (e.g., `CAST`, `TRY_CAST`). - kind: CastKind, - /// Expression being cast. - expr: Box, - /// Target data type. - data_type: DataType, - /// [MySQL] allows CAST(... AS type ARRAY) in functional index definitions for InnoDB - /// multi-valued indices. It's not really a datatype, and is only allowed in `CAST` in key - /// specifications, so it's a flag here. - /// - /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#function_cast - array: bool, - /// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery] - /// - /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax - format: Option, - }, + Cast(Box), /// AT a timestamp to a different timezone e.g. `FROM_UNIXTIME(0) AT TIME ZONE 'UTC-06:00'` AtTimeZone { /// Timestamp expression to shift. @@ -1194,29 +1159,15 @@ pub enum Expr { /// A constant of form ` 'value'`. /// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`), /// as well as constants of other types (a non-standard PostgreSQL extension). - // XXX too big - TypedString(TypedString), + TypedString(Box), /// Scalar function call e.g. `LEFT(foo, 5)` - // XXX too big Function(Box), /// `CASE [] WHEN THEN ... [ELSE ] END` /// /// Note we only recognize a complete single expression as ``, /// not `< 0` nor `1, 2, 3` as allowed in a `` per /// - // XXX too big - Case { - /// The attached `CASE` token (keeps original spacing/comments). - case_token: AttachedToken, - /// The attached `END` token (keeps original spacing/comments). - end_token: AttachedToken, - /// Optional operand expression after `CASE` (for simple CASE). - operand: Option>, - /// The `WHEN ... THEN` conditions and results. - conditions: Vec, - /// Optional `ELSE` result expression. - else_result: Option>, - }, + Case(Box), /// An exists expression `[ NOT ] EXISTS(SELECT ...)`, used in expressions like /// `WHERE [ NOT ] EXISTS (SELECT ...)`. Exists { @@ -1282,8 +1233,7 @@ pub enum Expr { /// An array expression e.g. `ARRAY[1, 2]` Array(Array), /// An interval expression e.g. `INTERVAL '1' YEAR` - // XXX too big - Interval(Interval), + Interval(Box), /// `MySQL` specific text search function [(1)]. /// /// Syntax: @@ -1334,8 +1284,7 @@ pub enum Expr { /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/functions#higher-order-functions---operator-and-lambdaparams-expr-function) /// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html) /// [DuckDB](https://duckdb.org/docs/stable/sql/functions/lambda) - // XXX too big - Lambda(LambdaFunction), + Lambda(Box), /// Checks membership of a value in a JSON array MemberOf(MemberOf), } @@ -1357,6 +1306,68 @@ impl Expr { } } +/// A [`CONVERT` expression](Expr::Convert) +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct ConvertExpr { + /// CONVERT (false) or TRY_CONVERT (true) + /// + pub is_try: bool, + /// The expression to convert. + pub expr: Expr, + /// The target data type, if provided. + pub data_type: Option, + /// Optional target character encoding (e.g., `utf8mb4`). + pub charset: Option, + /// `true` when target precedes the value (MSSQL syntax). + pub target_before_value: bool, + /// How to translate the expression. + /// + /// [MSSQL]: https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16#style + pub styles: Vec, +} + +/// A [`CAST` expression](Expr::Cast) +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct CastExpr { + /// The cast kind (e.g., `CAST`, `TRY_CAST`). + pub kind: CastKind, + /// Expression being cast. + pub expr: Expr, + /// Target data type. + pub data_type: DataType, + /// [MySQL] allows CAST(... AS type ARRAY) in functional index definitions for InnoDB + /// multi-valued indices. It's not really a datatype, and is only allowed in `CAST` in key + /// specifications, so it's a flag here. + /// + /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#function_cast + pub array: bool, + /// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery] + /// + /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax + pub format: Option, +} + +/// A [`CASE` expression](Expr::Case) +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct CaseExpr { + /// The attached `CASE` token (keeps original spacing/comments). + pub case_token: AttachedToken, + /// The attached `END` token (keeps original spacing/comments). + pub end_token: AttachedToken, + /// Optional operand expression after `CASE` (for simple CASE). + pub operand: Option>, + /// The `WHEN ... THEN` conditions and results. + pub conditions: Vec, + /// Optional `ELSE` result expression. + pub else_result: Option>, +} + /// The contents inside the `[` and `]` in a subscript expression. #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -1454,7 +1465,7 @@ pub struct LambdaFunction { /// The parameters to the lambda function. pub params: OneOrManyWithParens, /// The body of the lambda function. - pub body: Box, + pub body: Expr, /// The syntax style used to write the lambda function. pub syntax: LambdaSyntax, } @@ -1909,14 +1920,15 @@ impl fmt::Display for Expr { write!(f, "{op}{expr}") } } - Expr::Convert { - is_try, - expr, - target_before_value, - data_type, - charset, - styles, - } => { + Expr::Convert(convert) => { + let ConvertExpr { + is_try, + expr, + target_before_value, + data_type, + charset, + styles, + } = &**convert; write!(f, "{}CONVERT(", if *is_try { "TRY_" } else { "" })?; if let Some(data_type) = data_type { if let Some(charset) = charset { @@ -1936,40 +1948,43 @@ impl fmt::Display for Expr { } write!(f, ")") } - Expr::Cast { - kind, - expr, - data_type, - array, - format, - } => match kind { - CastKind::Cast => { - write!(f, "CAST({expr} AS {data_type}")?; - if *array { - write!(f, " ARRAY")?; + Expr::Cast(cast) => { + let CastExpr { + kind, + expr, + data_type, + array, + format, + } = &**cast; + match kind { + CastKind::Cast => { + write!(f, "CAST({expr} AS {data_type}")?; + if *array { + write!(f, " ARRAY")?; + } + if let Some(format) = format { + write!(f, " FORMAT {format}")?; + } + write!(f, ")") } - if let Some(format) = format { - write!(f, " FORMAT {format}")?; + CastKind::TryCast => { + if let Some(format) = format { + write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})") + } else { + write!(f, "TRY_CAST({expr} AS {data_type})") + } } - write!(f, ")") - } - CastKind::TryCast => { - if let Some(format) = format { - write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})") - } else { - write!(f, "TRY_CAST({expr} AS {data_type})") + CastKind::SafeCast => { + if let Some(format) = format { + write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})") + } else { + write!(f, "SAFE_CAST({expr} AS {data_type})") + } } - } - CastKind::SafeCast => { - if let Some(format) = format { - write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})") - } else { - write!(f, "SAFE_CAST({expr} AS {data_type})") + CastKind::DoubleColon => { + write!(f, "{expr}::{data_type}") } } - CastKind::DoubleColon => { - write!(f, "{expr}::{data_type}") - } }, Expr::Extract { field, @@ -2000,13 +2015,14 @@ impl fmt::Display for Expr { Expr::Prefixed { prefix, value } => write!(f, "{prefix} {value}"), Expr::TypedString(ts) => ts.fmt(f), Expr::Function(fun) => fun.fmt(f), - Expr::Case { - case_token: _, - end_token: _, - operand, - conditions, - else_result, - } => { + Expr::Case(case) => { + let CaseExpr { + case_token: _, + end_token: _, + operand, + conditions, + else_result, + } = &**case; f.write_str("CASE")?; if let Some(operand) = operand { f.write_str(" ")?; @@ -12343,28 +12359,28 @@ mod tests { #[test] fn test_interval_display() { let interval = Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( Value::SingleQuotedString(String::from("123:45.67")).with_empty_span(), - )), + ), leading_field: Some(DateTimeField::Minute), leading_precision: Some(10), last_field: Some(DateTimeField::Second), fractional_seconds_precision: Some(9), - }); + }.into()); assert_eq!( "INTERVAL '123:45.67' MINUTE (10) TO SECOND (9)", format!("{interval}"), ); let interval = Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( Value::SingleQuotedString(String::from("5")).with_empty_span(), - )), + ), leading_field: Some(DateTimeField::Second), leading_precision: Some(1), last_field: None, fractional_seconds_precision: Some(3), - }); + }.into()); assert_eq!("INTERVAL '5' SECOND (1, 3)", format!("{interval}")); } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 74f731a78a..fd1d10521b 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -28,26 +28,7 @@ use core::iter; use crate::tokenizer::Span; use super::{ - comments, dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation, - AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, - AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, - ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, - ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, - CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, - ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr, - FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, - IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join, - JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, - MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr, - MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName, - ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy, - OrderByExpr, OrderByKind, OutputClause, Partition, PartitionBoundValue, PivotValueSource, - ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction, - RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, - SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, - TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins, Update, - UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement, - WildcardAdditionalOptions, With, WithFill, + AccessExpr, AlterColumnOperation, AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, AttachedToken, BeginEndStatements, CaseExpr, CaseStatement, CastExpr, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, ConflictTarget, ConnectByKind, ConstraintCharacteristics, ConvertExpr, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Partition, PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill, comments, dcl::SecondaryRoles, value::ValueWithSpan }; /// Given an iterator of spans, return the [Span::union] of all spans. @@ -1530,7 +1511,10 @@ impl Spanned for Expr { .union(&union_spans(collation.0.iter().map(|i| i.span()))), Expr::Nested(expr) => expr.span(), Expr::Value(value) => value.span(), - Expr::TypedString(TypedString { value, .. }) => value.span(), + Expr::TypedString(s) => { + let TypedString { value, .. } = &**s; + value.span() + } Expr::Function(function) => function.span(), Expr::GroupingSets(vec) => { union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span()))) @@ -1553,25 +1537,31 @@ impl Spanned for Expr { right, } => left.span().union(&right.span()), Expr::UnaryOp { op: _, expr } => expr.span(), - Expr::Convert { - expr, - data_type: _, - charset, - target_before_value: _, - styles, - is_try: _, - } => union_spans( + Expr::Convert(convert) => { + let ConvertExpr { + expr, + data_type: _, + charset, + target_before_value: _, + styles, + is_try: _, + } = &**convert; + union_spans( core::iter::once(expr.span()) .chain(charset.as_ref().map(|i| i.span())) .chain(styles.iter().map(|i| i.span())), - ), - Expr::Cast { - kind: _, - expr, - data_type: _, - array: _, - format: _, - } => expr.span(), + ) + }, + Expr::Cast(cast) => { + let CastExpr { + kind: _, + expr, + data_type: _, + array: _, + format: _, + } = &**cast; + expr.span() + } Expr::AtTimeZone { timestamp, time_zone, @@ -1607,13 +1597,15 @@ impl Spanned for Expr { ), ), Expr::Prefixed { value, .. } => value.span(), - Expr::Case { - case_token, - end_token, - operand, - conditions, - else_result, - } => union_spans( + Expr::Case(case) => { + let CaseExpr { + case_token, + end_token, + operand, + conditions, + else_result, + } = &**case; + union_spans( iter::once(case_token.0.span) .chain( operand @@ -1626,7 +1618,7 @@ impl Spanned for Expr { .chain(else_result.as_ref().map(|i| i.span())), ) .chain(iter::once(end_token.0.span)), - ), + )}, Expr::Exists { subquery, .. } => subquery.span(), Expr::Subquery(query) => query.span(), Expr::Struct { .. } => Span::empty(), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d16bc4391d..2be5597403 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1660,9 +1660,9 @@ impl<'a> Parser<'a> { name: w.to_ident(w_span), data_type: None, }), - body: Box::new(self.parse_expr()?), + body: self.parse_expr()?, syntax: LambdaSyntax::Arrow, - })) + }.into())) } // An unreserved word (likely an identifier) that is followed by another word (likley a data type) // which is then followed by an arrow, which indicates a lambda function with a single, typed parameter. @@ -1678,9 +1678,9 @@ impl<'a> Parser<'a> { name: w.to_ident(w_span), data_type: Some(data_type), }), - body: Box::new(self.parse_expr()?), + body: self.parse_expr()?, syntax: LambdaSyntax::Arrow, - })) + }.into())) } _ => Ok(Expr::Identifier(w.to_ident(w_span))), } @@ -1723,19 +1723,19 @@ impl<'a> Parser<'a> { DataType::Custom(..) => parser_err!("dummy", loc), // MySQL supports using the `BINARY` keyword as a cast to binary type. DataType::Binary(..) if self.dialect.supports_binary_kw_as_cast() => { - Ok(Expr::Cast { + Ok(Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(parser.parse_expr()?), + expr: parser.parse_expr()?, data_type: DataType::Binary(None), array: false, format: None, - }) + }.into())) } data_type => Ok(Expr::TypedString(TypedString { data_type, value: parser.parse_value()?, uses_odbc_syntax: false, - })), + }.into())), } })?; @@ -1949,7 +1949,7 @@ impl<'a> Parser<'a> { data_type: DataType::GeometricType(kind), value: self.parse_value()?, uses_odbc_syntax: false, - })) + }.into())) } /// Try to parse an [Expr::CompoundFieldAccess] like `a.b.c` or `a.b[1].c`. @@ -2280,9 +2280,9 @@ impl<'a> Parser<'a> { let expr = p.parse_expr()?; Ok(Expr::Lambda(LambdaFunction { params: OneOrManyWithParens::Many(params), - body: Box::new(expr), + body: expr, syntax: LambdaSyntax::Arrow, - })) + }.into())) }) } @@ -2304,9 +2304,9 @@ impl<'a> Parser<'a> { let body = self.parse_expr()?; Ok(Expr::Lambda(LambdaFunction { params, - body: Box::new(body), + body, syntax: LambdaSyntax::LambdaKeyword, - })) + }.into())) } /// Parses the parameters of a lambda function with optional typing. @@ -2381,7 +2381,7 @@ impl<'a> Parser<'a> { data_type, value, uses_odbc_syntax: true, - })) + }.into())) }) } @@ -2681,13 +2681,13 @@ impl<'a> Parser<'a> { None }; let end_token = AttachedToken(self.expect_keyword(Keyword::END)?); - Ok(Expr::Case { + Ok(Expr::Case(CaseExpr { case_token, end_token, operand, conditions, else_result, - }) + }.into())) } /// Parse an optional `FORMAT` clause for `CAST` expressions. @@ -2724,14 +2724,14 @@ impl<'a> Parser<'a> { Default::default() }; self.expect_token(&Token::RParen)?; - Ok(Expr::Convert { + Ok(Expr::Convert(ConvertExpr { is_try, - expr: Box::new(expr), + expr, data_type: Some(data_type), charset: None, target_before_value: true, styles, - }) + }.into())) } /// Parse a SQL CONVERT function: @@ -2747,14 +2747,14 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::USING) { let charset = self.parse_object_name(false)?; self.expect_token(&Token::RParen)?; - return Ok(Expr::Convert { + return Ok(Expr::Convert(ConvertExpr { is_try, - expr: Box::new(expr), + expr, data_type: None, charset: Some(charset), target_before_value: false, styles: vec![], - }); + }.into())); } self.expect_token(&Token::Comma)?; let data_type = self.parse_data_type()?; @@ -2764,14 +2764,14 @@ impl<'a> Parser<'a> { None }; self.expect_token(&Token::RParen)?; - Ok(Expr::Convert { + Ok(Expr::Convert(ConvertExpr { is_try, - expr: Box::new(expr), + expr, data_type: Some(data_type), charset, target_before_value: false, styles: vec![], - }) + }.into())) } /// Parse a SQL CAST function e.g. `CAST(expr AS FLOAT)` @@ -2783,13 +2783,13 @@ impl<'a> Parser<'a> { let array = self.parse_keyword(Keyword::ARRAY); let format = self.parse_optional_cast_format()?; self.expect_token(&Token::RParen)?; - Ok(Expr::Cast { + Ok(Expr::Cast(CastExpr { kind, - expr: Box::new(expr), + expr, data_type, array, format, - }) + }.into())) } /// Parse a SQL EXISTS expression e.g. `WHERE EXISTS(SELECT ...)`. @@ -3307,12 +3307,12 @@ impl<'a> Parser<'a> { }; Ok(Expr::Interval(Interval { - value: Box::new(value), + value, leading_field, leading_precision, last_field, fractional_seconds_precision: fsec_precision, - })) + }.into())) } /// Peek at the next token and determine if it is a temporal unit @@ -4065,13 +4065,13 @@ impl<'a> Parser<'a> { ), } } else if Token::DoubleColon == *tok { - Ok(Expr::Cast { + Ok(Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(expr), + expr, data_type: self.parse_data_type()?, array: false, format: None, - }) + }.into())) } else if Token::ExclamationMark == *tok && self.dialect.supports_factorial_operator() { Ok(Expr::UnaryOp { op: UnaryOperator::PGPostfixFactorial, @@ -4313,13 +4313,13 @@ impl<'a> Parser<'a> { /// Parse a PostgreSQL casting style which is in the form of `expr::datatype`. pub fn parse_pg_cast(&mut self, expr: Expr) -> Result { - Ok(Expr::Cast { + Ok(Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(expr), + expr, data_type: self.parse_data_type()?, array: false, format: None, - }) + }.into())) } /// Get the precedence of the next token diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 569a0bf689..1a32d19904 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -923,7 +923,7 @@ fn parse_typed_struct_syntax_bigquery() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Datetime(None), @@ -961,14 +961,14 @@ fn parse_typed_struct_syntax_bigquery() { assert_eq!( &Expr::Struct { values: vec![Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( Value::SingleQuotedString("2".into()).with_empty_span() - )), + ), leading_field: Some(DateTimeField::Hour), leading_precision: None, last_field: None, fractional_seconds_precision: None - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Interval { @@ -991,7 +991,7 @@ fn parse_typed_struct_syntax_bigquery() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::JSON, @@ -1028,7 +1028,7 @@ fn parse_typed_struct_syntax_bigquery() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Timestamp(None, TimezoneInfo::None), @@ -1047,7 +1047,7 @@ fn parse_typed_struct_syntax_bigquery() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Time(None, TimezoneInfo::None), @@ -1069,7 +1069,7 @@ fn parse_typed_struct_syntax_bigquery() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Numeric(ExactNumberInfo::None), @@ -1087,7 +1087,7 @@ fn parse_typed_struct_syntax_bigquery() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::BigNumeric(ExactNumberInfo::None), @@ -1265,7 +1265,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Datetime(None), @@ -1303,14 +1303,14 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { assert_eq!( &Expr::Struct { values: vec![Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( Value::SingleQuotedString("1".into()).with_empty_span() - )), + ), leading_field: Some(DateTimeField::Month), leading_precision: None, last_field: None, fractional_seconds_precision: None - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Interval { @@ -1333,7 +1333,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::JSON, @@ -1370,7 +1370,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Timestamp(None, TimezoneInfo::None), @@ -1389,7 +1389,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Time(None, TimezoneInfo::None), @@ -1411,7 +1411,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::Numeric(ExactNumberInfo::None), @@ -1429,7 +1429,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { span: Span::empty(), }, uses_odbc_syntax: false - })], + }.into())], fields: vec![StructField { field_name: None, field_type: DataType::BigNumeric(ExactNumberInfo::None), @@ -2520,7 +2520,7 @@ fn test_triple_quote_typed_strings() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr ); } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index de68c898f1..5bb5c6ca12 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3087,26 +3087,26 @@ fn parse_cast() { let sql = "SELECT CAST(id AS BIGINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::BigInt(None), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS TINYINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::TinyInt(None), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); @@ -3129,107 +3129,107 @@ fn parse_cast() { let sql = "SELECT CAST(id AS NVARCHAR(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::Nvarchar(Some(CharacterLength::IntegerLength { length: 50, unit: None, })), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS CLOB) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::Clob(None), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS CLOB(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::Clob(Some(50)), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS BINARY(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::Binary(Some(50)), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS VARBINARY(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::Varbinary(Some(BinaryLength::IntegerLength { length: 50 })), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS BLOB) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::Blob(None), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS BLOB(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::Blob(Some(50)), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(details AS JSONB) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("details"))), + expr: Expr::Identifier(Ident::new("details")), data_type: DataType::JSONB, array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); } @@ -3239,13 +3239,13 @@ fn parse_try_cast() { let sql = "SELECT TRY_CAST(id AS BIGINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::TryCast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), + expr: Expr::Identifier(Ident::new("id")), data_type: DataType::BigInt(None), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); verified_stmt("SELECT TRY_CAST(id AS BIGINT) FROM customer"); @@ -6161,7 +6161,7 @@ fn parse_literal_date() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); } @@ -6178,7 +6178,7 @@ fn parse_literal_time() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); } @@ -6195,7 +6195,7 @@ fn parse_literal_datetime() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); } @@ -6212,7 +6212,7 @@ fn parse_literal_timestamp_without_time_zone() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); @@ -6231,7 +6231,7 @@ fn parse_literal_timestamp_with_time_zone() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); @@ -6246,14 +6246,14 @@ fn parse_interval_all() { let select = verified_only_select(sql); assert_eq!( &Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString(String::from("1-1"))).with_empty_span() - )), + ), leading_field: Some(DateTimeField::Year), leading_precision: None, last_field: Some(DateTimeField::Month), fractional_seconds_precision: None, - }), + }.into()), expr_from_projection(only(&select.projection)), ); @@ -6261,14 +6261,14 @@ fn parse_interval_all() { let select = verified_only_select(sql); assert_eq!( &Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString(String::from("01:01.01"))).with_empty_span() - )), + ), leading_field: Some(DateTimeField::Minute), leading_precision: Some(5), last_field: Some(DateTimeField::Second), fractional_seconds_precision: Some(5), - }), + }.into()), expr_from_projection(only(&select.projection)), ); @@ -6276,14 +6276,14 @@ fn parse_interval_all() { let select = verified_only_select(sql); assert_eq!( &Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString(String::from("1"))).with_empty_span() - )), + ), leading_field: Some(DateTimeField::Second), leading_precision: Some(5), last_field: None, fractional_seconds_precision: Some(4), - }), + }.into()), expr_from_projection(only(&select.projection)), ); @@ -6291,14 +6291,14 @@ fn parse_interval_all() { let select = verified_only_select(sql); assert_eq!( &Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString(String::from("10"))).with_empty_span() - )), + ), leading_field: Some(DateTimeField::Hour), leading_precision: None, last_field: None, fractional_seconds_precision: None, - }), + }.into()), expr_from_projection(only(&select.projection)), ); @@ -6306,12 +6306,12 @@ fn parse_interval_all() { let select = verified_only_select(sql); assert_eq!( &Expr::Interval(Interval { - value: Box::new(Expr::value(number("5"))), + value: Expr::value(number("5")), leading_field: Some(DateTimeField::Day), leading_precision: None, last_field: None, fractional_seconds_precision: None, - }), + }.into()), expr_from_projection(only(&select.projection)), ); @@ -6319,12 +6319,12 @@ fn parse_interval_all() { let select = verified_only_select(sql); assert_eq!( &Expr::Interval(Interval { - value: Box::new(Expr::value(number("5"))), + value: Expr::value(number("5")), leading_field: Some(DateTimeField::Days), leading_precision: None, last_field: None, fractional_seconds_precision: None, - }), + }.into()), expr_from_projection(only(&select.projection)), ); @@ -6332,14 +6332,14 @@ fn parse_interval_all() { let select = verified_only_select(sql); assert_eq!( &Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString(String::from("10"))).with_empty_span() - )), + ), leading_field: Some(DateTimeField::Hour), leading_precision: Some(1), last_field: None, fractional_seconds_precision: None, - }), + }.into()), expr_from_projection(only(&select.projection)), ); @@ -6403,14 +6403,14 @@ fn parse_interval_dont_require_unit() { let select = dialects.verified_only_select(sql); assert_eq!( &Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() - )), + ), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None, - }), + }.into()), expr_from_projection(only(&select.projection)), ); dialects.verified_only_select("SELECT INTERVAL '1 YEAR'"); @@ -6441,16 +6441,16 @@ fn parse_interval_require_qualifier() { assert_eq!( expr_from_projection(only(&select.projection)), &Expr::Interval(Interval { - value: Box::new(Expr::BinaryOp { + value: Expr::BinaryOp { left: Box::new(Expr::value(number("1"))), op: BinaryOperator::Plus, right: Box::new(Expr::value(number("1"))), - }), + }, leading_field: Some(DateTimeField::Day), leading_precision: None, last_field: None, fractional_seconds_precision: None, - }), + }.into()), ); let sql = "SELECT INTERVAL '1' + '1' DAY"; @@ -6458,7 +6458,7 @@ fn parse_interval_require_qualifier() { assert_eq!( expr_from_projection(only(&select.projection)), &Expr::Interval(Interval { - value: Box::new(Expr::BinaryOp { + value: Expr::BinaryOp { left: Box::new(Expr::Value( (Value::SingleQuotedString("1".to_string())).with_empty_span() )), @@ -6466,12 +6466,12 @@ fn parse_interval_require_qualifier() { right: Box::new(Expr::Value( (Value::SingleQuotedString("1".to_string())).with_empty_span() )), - }), + }, leading_field: Some(DateTimeField::Day), leading_precision: None, last_field: None, fractional_seconds_precision: None, - }), + }.into()), ); let sql = "SELECT INTERVAL '1' + '2' - '3' DAY"; @@ -6479,7 +6479,7 @@ fn parse_interval_require_qualifier() { assert_eq!( expr_from_projection(only(&select.projection)), &Expr::Interval(Interval { - value: Box::new(Expr::BinaryOp { + value: Expr::BinaryOp { left: Box::new(Expr::BinaryOp { left: Box::new(Expr::Value( (Value::SingleQuotedString("1".to_string())).with_empty_span() @@ -6493,12 +6493,12 @@ fn parse_interval_require_qualifier() { right: Box::new(Expr::Value( (Value::SingleQuotedString("3".to_string())).with_empty_span() )), - }), + }, leading_field: Some(DateTimeField::Day), leading_precision: None, last_field: None, fractional_seconds_precision: None, - }), + }.into()), ); } @@ -6511,14 +6511,14 @@ fn parse_interval_disallow_interval_expr() { assert_eq!( expr_from_projection(only(&select.projection)), &Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() - )), + ), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None, - }), + }.into()), ); dialects.verified_only_select("SELECT INTERVAL '1 YEAR'"); @@ -6534,24 +6534,24 @@ fn parse_interval_disallow_interval_expr() { expr_from_projection(only(&select.projection)), &Expr::BinaryOp { left: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() - )), + ), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None, - })), + }.into())), op: BinaryOperator::Gt, right: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString(String::from("1 SECOND"))).with_empty_span() - )), + ), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None, - })) + }.into())) } ); } @@ -6564,14 +6564,14 @@ fn interval_disallow_interval_expr_gt() { expr, Expr::BinaryOp { left: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString("1 second".to_string())).with_empty_span() - )), + ), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None, - },)), + }.into(),)), op: BinaryOperator::Gt, right: Box::new(Expr::Identifier(Ident { value: "x".to_string(), @@ -6588,21 +6588,21 @@ fn interval_disallow_interval_expr_double_colon() { let expr = dialects.verified_expr("INTERVAL '1 second'::TEXT"); assert_eq!( expr, - Expr::Cast { + Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( + expr: Expr::Interval(Interval { + value: Expr::Value( (Value::SingleQuotedString("1 second".to_string())).with_empty_span() - )), + ), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None, - })), + }.into()), data_type: DataType::Text, array: false, format: None, - } + }.into()) ) } @@ -6657,14 +6657,14 @@ fn parse_interval_and_or_xor() { })), op: BinaryOperator::Plus, right: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString("5 days".to_string())).with_empty_span(), - )), + ), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None, - })), + }.into())), }), }), op: BinaryOperator::And, @@ -6683,14 +6683,14 @@ fn parse_interval_and_or_xor() { })), op: BinaryOperator::Plus, right: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( (Value::SingleQuotedString("3 days".to_string())).with_empty_span(), - )), + ), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None, - })), + }.into())), }), }), }), @@ -6833,7 +6833,7 @@ fn parse_json_keyword() { span: Span::empty(), }, uses_odbc_syntax: false, - }), + }.into()), expr_from_projection(only(&select.projection)), ); } @@ -6849,16 +6849,17 @@ fn parse_typed_strings() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr ); - if let Expr::TypedString(TypedString { - data_type, - value, - uses_odbc_syntax: false, - }) = expr - { + if let Expr::TypedString(s) = expr { + let TypedString { + data_type, + value, + uses_odbc_syntax, + } = *s; + assert_eq!(false, uses_odbc_syntax); assert_eq!(DataType::JSON, data_type); assert_eq!(r#"{"foo":"bar"}"#, value.into_string().unwrap()); } @@ -6876,7 +6877,7 @@ fn parse_bignumeric_keyword() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '0'"); @@ -6891,7 +6892,7 @@ fn parse_bignumeric_keyword() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '123456'"); @@ -6906,7 +6907,7 @@ fn parse_bignumeric_keyword() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '-3.14'"); @@ -6921,7 +6922,7 @@ fn parse_bignumeric_keyword() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '-0.54321'"); @@ -6936,7 +6937,7 @@ fn parse_bignumeric_keyword() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '1.23456e05'"); @@ -6951,7 +6952,7 @@ fn parse_bignumeric_keyword() { span: Span::empty(), }, uses_odbc_syntax: false - }), + }.into()), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '-9.876e-3'"); @@ -7232,7 +7233,7 @@ fn parse_searched_case_expr() { use self::Expr::{BinaryOp, Case, Identifier, IsNull}; let select = verified_only_select(sql); assert_eq!( - &Case { + &Case(CaseExpr { case_token: AttachedToken::empty(), end_token: AttachedToken::empty(), operand: None, @@ -7261,7 +7262,7 @@ fn parse_searched_case_expr() { else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( "<0".to_string() )))), - }, + }.into()), expr_from_projection(only(&select.projection)), ); } @@ -7273,7 +7274,7 @@ fn parse_simple_case_expr() { let select = verified_only_select(sql); use self::Expr::{Case, Identifier}; assert_eq!( - &Case { + &Case(CaseExpr { case_token: AttachedToken::empty(), end_token: AttachedToken::empty(), operand: Some(Box::new(Identifier(Ident::new("foo")))), @@ -7284,7 +7285,7 @@ fn parse_simple_case_expr() { else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( "N".to_string() )))), - }, + }.into()), expr_from_projection(only(&select.projection)), ); } @@ -9344,16 +9345,16 @@ fn parse_double_colon_cast_at_timezone() { assert_eq!( &Expr::AtTimeZone { - timestamp: Box::new(Expr::Cast { + timestamp: Box::new(Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( + expr: Expr::Value( (Value::SingleQuotedString("2001-01-01T00:00:00.000Z".to_string())) .with_empty_span() - )), + ), data_type: DataType::Timestamp(None, TimezoneInfo::None), array: false, format: None - }), + }.into())), time_zone: Box::new(Expr::Value( (Value::SingleQuotedString("Europe/Brussels".to_string())).with_empty_span() )), @@ -13639,27 +13640,27 @@ fn test_dictionary_syntax() { Expr::Dictionary(vec![ DictionaryField { key: Ident::with_quote('\'', "start"), - value: Box::new(Expr::Cast { + value: Box::new(Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Value( + expr: Expr::Value( (Value::SingleQuotedString("2023-04-01".to_owned())).with_empty_span(), - )), + ), data_type: DataType::Timestamp(None, TimezoneInfo::None), array: false, format: None, - }), + }.into())), }, DictionaryField { key: Ident::with_quote('\'', "end"), - value: Box::new(Expr::Cast { + value: Box::new(Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Value( + expr: Expr::Value( (Value::SingleQuotedString("2023-04-05".to_owned())).with_empty_span(), - )), + ), data_type: DataType::Timestamp(None, TimezoneInfo::None), array: false, format: None, - }), + }.into())), }, ]), ) @@ -13945,18 +13946,18 @@ fn test_extract_seconds_ok() { Expr::Extract { field: Seconds, syntax: ExtractSyntax::From, - expr: Box::new(Expr::Cast { + expr: Box::new(Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( + expr: Expr::Value( (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() - )), + ), data_type: DataType::Interval { fields: None, precision: None }, array: false, format: None, - }), + }.into())), } ); @@ -13976,18 +13977,18 @@ fn test_extract_seconds_ok() { projection: vec![UnnamedExpr(Expr::Extract { field: Seconds, syntax: ExtractSyntax::From, - expr: Box::new(Expr::Cast { + expr: Box::new(Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( + expr: Expr::Value( (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span(), - )), + ), data_type: DataType::Interval { fields: None, precision: None, }, array: false, format: None, - }), + }.into())), })], exclude: None, into: None, @@ -14034,18 +14035,18 @@ fn test_extract_seconds_single_quote_ok() { span: Span::empty(), }), syntax: ExtractSyntax::From, - expr: Box::new(Expr::Cast { + expr: Box::new(Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( + expr: Expr::Value( (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() - )), + ), data_type: DataType::Interval { fields: None, precision: None }, array: false, format: None, - }), + }.into())), } ) } @@ -16071,7 +16072,7 @@ fn test_lambdas() { data_type: None } ]), - body: Box::new(Expr::Case { + body: Expr::Case(CaseExpr { case_token: AttachedToken::empty(), end_token: AttachedToken::empty(), operand: None, @@ -16103,9 +16104,9 @@ fn test_lambdas() { }, ], else_result: Some(Box::new(Expr::value(number("1")))), - }), + }.into()), syntax: LambdaSyntax::Arrow, - }) + }.into()) ] )), dialects.verified_only_select(sql).projection[0] @@ -16251,7 +16252,7 @@ fn test_geometry_type() { span: Span::empty(), }, uses_odbc_syntax: false - }) + }.into()) ); let sql = "line '1,2,3,4'"; @@ -16264,7 +16265,7 @@ fn test_geometry_type() { span: Span::empty(), }, uses_odbc_syntax: false - }) + }.into()) ); let sql = "path '1,2,3,4'"; @@ -16277,7 +16278,7 @@ fn test_geometry_type() { span: Span::empty(), }, uses_odbc_syntax: false - }) + }.into()) ); let sql = "box '1,2,3,4'"; assert_eq!( @@ -16289,7 +16290,7 @@ fn test_geometry_type() { span: Span::empty(), }, uses_odbc_syntax: false - }) + }.into()) ); let sql = "circle '1,2,3'"; @@ -16302,7 +16303,7 @@ fn test_geometry_type() { span: Span::empty(), }, uses_odbc_syntax: false - }) + }.into()) ); let sql = "polygon '1,2,3,4'"; @@ -16315,7 +16316,7 @@ fn test_geometry_type() { span: Span::empty(), }, uses_odbc_syntax: false - }) + }.into()) ); let sql = "lseg '1,2,3,4'"; assert_eq!( @@ -16327,7 +16328,7 @@ fn test_geometry_type() { span: Span::empty(), }, uses_odbc_syntax: false - }) + }.into()) ); } #[test] diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 79b3d0654d..1e2292ffc0 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -76,9 +76,9 @@ fn test_databricks_exists() { name: Ident::new("x"), data_type: None }), - body: Box::new(Expr::IsNull(Box::new(Expr::Identifier(Ident::new("x"))))), + body: Expr::IsNull(Box::new(Expr::Identifier(Ident::new("x")))), syntax: LambdaSyntax::Arrow, - }) + }.into()) ] ), ); @@ -122,7 +122,7 @@ fn test_databricks_lambdas() { data_type: None } ]), - body: Box::new(Expr::Case { + body: Expr::Case(CaseExpr { case_token: AttachedToken::empty(), end_token: AttachedToken::empty(), operand: None, @@ -154,9 +154,9 @@ fn test_databricks_lambdas() { }, ], else_result: Some(Box::new(Expr::value(number("1")))) - }), + }.into()), syntax: LambdaSyntax::Arrow, - }) + }.into()) ] )), databricks().verified_only_select(sql).projection[0] @@ -349,21 +349,21 @@ fn data_type_timestamp_ntz() { span: Span::empty(), }, uses_odbc_syntax: false - }) + }.into()) ); // Cast assert_eq!( databricks().verified_expr("(created_at)::TIMESTAMP_NTZ"), - Expr::Cast { + Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Nested(Box::new(Expr::Identifier( + expr: Expr::Nested(Box::new(Expr::Identifier( "created_at".into() - )))), + ))), data_type: DataType::TimestampNtz(None), array: false, format: None - } + }.into()) ); // Column definition diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index 1946d7d4a1..9be39628b0 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -382,15 +382,15 @@ fn test_duckdb_specific_int_types() { let sql = format!("SELECT 123::{dtype_string}"); let select = duckdb().verified_only_select(&sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( + expr: Expr::Value( Value::Number("123".parse().unwrap(), false).with_empty_span() - )), + ), data_type: data_type.clone(), array: false, format: None, - }, + }.into()), expr_from_projection(&select.projection[0]) ); } diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 1eaa3a8b08..542cef4f60 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -1284,19 +1284,20 @@ fn parse_cast_varchar_max() { #[test] fn parse_convert() { let sql = "CONVERT(INT, 1, 2, 3, NULL)"; - let Expr::Convert { + let Expr::Convert(convert) = ms().verified_expr(sql) + else { + unreachable!() + }; + let ConvertExpr { is_try, expr, data_type, charset, target_before_value, styles, - } = ms().verified_expr(sql) - else { - unreachable!() - }; + } = *convert; assert!(!is_try); - assert_eq!(Expr::value(number("1")), *expr); + assert_eq!(Expr::value(number("1")), expr); assert_eq!(Some(DataType::Int(None)), data_type); assert!(charset.is_none()); assert!(target_before_value); diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 3f27f84d1e..b333b2d90a 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -860,37 +860,37 @@ fn test_functional_key_part() { index_column(mysql_and_generic().verified_stmt( r#"CREATE TABLE t (jsoncol JSON, PRIMARY KEY ((CAST(col ->> '$.id' AS UNSIGNED)) ASC))"# )), - Expr::Nested(Box::new(Expr::Cast { + Expr::Nested(Box::new(Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::BinaryOp { + expr: Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("col"))), op: BinaryOperator::LongArrow, right: Box::new(Expr::Value( Value::SingleQuotedString("$.id".to_string()).with_empty_span() )), - }), + }, data_type: DataType::Unsigned, array: false, format: None, - })), + }.into()))), ); assert_eq!( index_column(mysql_and_generic().verified_stmt( r#"CREATE TABLE t (jsoncol JSON, PRIMARY KEY ((CAST(col ->> '$.fields' AS UNSIGNED ARRAY)) ASC))"# )), - Expr::Nested(Box::new(Expr::Cast { + Expr::Nested(Box::new(Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::BinaryOp { + expr: Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("col"))), op: BinaryOperator::LongArrow, right: Box::new(Expr::Value( Value::SingleQuotedString("$.fields".to_string()).with_empty_span() )), - }), + }, data_type: DataType::Unsigned, array: true, format: None, - })), + }.into()))), ); } diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 075e367878..dfb2455181 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1845,27 +1845,27 @@ fn parse_execute() { has_parentheses: false, using: vec![ ExprWithAlias { - expr: Expr::Cast { + expr: Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Value( + expr: Expr::Value( (Value::Number("1337".parse().unwrap(), false)).with_empty_span() - )), + ), data_type: DataType::SmallInt(None), array: false, format: None - }, + }.into()), alias: None }, ExprWithAlias { - expr: Expr::Cast { + expr: Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Value( + expr: Expr::Value( (Value::Number("7331".parse().unwrap(), false)).with_empty_span() - )), + ), data_type: DataType::SmallInt(None), array: false, format: None - }, + }.into()), alias: None }, ], @@ -2474,15 +2474,15 @@ fn parse_array_index_expr() { let select = pg_and_generic().verified_only_select(sql); assert_eq!( &Expr::CompoundFieldAccess { - root: Box::new(Expr::Nested(Box::new(Expr::Cast { + root: Box::new(Expr::Nested(Box::new(Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Array(Array { + expr: Expr::Array(Array { elem: vec![Expr::Array(Array { elem: vec![num[2].clone(), num[3].clone(),], named: true, })], named: true, - })), + }), data_type: DataType::Array(ArrayElemTypeDef::SquareBracket( Box::new(DataType::Array(ArrayElemTypeDef::SquareBracket( Box::new(DataType::Int(None)), @@ -2492,7 +2492,7 @@ fn parse_array_index_expr() { )), array: false, format: None, - }))), + }.into())))), access_chain: vec![ AccessExpr::Subscript(Subscript::Index { index: num[1].clone() @@ -5859,27 +5859,27 @@ fn parse_at_time_zone() { span: Span::empty(), }, uses_odbc_syntax: false, - })), - time_zone: Box::new(Expr::Cast { + }.into())), + time_zone: Box::new(Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( + expr: Expr::Value( Value::SingleQuotedString("America/Los_Angeles".to_owned()).with_empty_span(), - )), + ), data_type: DataType::Text, array: false, format: None, - }), + }.into())), }), op: BinaryOperator::Plus, right: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( + value: Expr::Value( Value::SingleQuotedString("23 hours".to_owned()).with_empty_span(), - )), + ), leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None, - })), + }.into())), }; pretty_assertions::assert_eq!( pg_and_generic().verified_expr( @@ -6688,15 +6688,15 @@ fn arrow_cast_precedence() { span: Span::empty(), })), op: BinaryOperator::Arrow, - right: Box::new(Expr::Cast { + right: Box::new(Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( + expr: Expr::Value( (Value::SingleQuotedString("bar".to_string())).with_empty_span() - )), + ), data_type: DataType::Text, array: false, format: None, - }), + }.into())), } ) } diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 58d050c8f8..fed9ae9146 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -1275,13 +1275,13 @@ fn parse_array() { let sql = "SELECT CAST(a AS ARRAY) FROM customer"; let select = snowflake().verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("a"))), + expr: Expr::Identifier(Ident::new("a")), data_type: DataType::Array(ArrayElemTypeDef::None), array: false, format: None, - }, + }.into()), expr_from_projection(only(&select.projection)) ); } @@ -1377,9 +1377,9 @@ fn parse_semi_structured_data_traversal() { assert_eq!( snowflake().verified_expr("a:b::ARRAY[1]"), Expr::JsonAccess { - value: Box::new(Expr::Cast { + value: Box::new(Expr::Cast(CastExpr { kind: CastKind::DoubleColon, - expr: Box::new(Expr::JsonAccess { + expr: Expr::JsonAccess { value: Box::new(Expr::Identifier(Ident::new("a"))), path: JsonPath { path: vec![JsonPathElem::Dot { @@ -1387,11 +1387,11 @@ fn parse_semi_structured_data_traversal() { quoted: false }] } - }), + }, data_type: DataType::Array(ArrayElemTypeDef::None), array: false, format: None, - }), + }.into())), path: JsonPath { path: vec![JsonPathElem::Bracket { key: Expr::value(number("1")) @@ -4947,8 +4947,8 @@ fn test_timestamp_ntz_with_precision() { let select = snowflake().verified_only_select("SELECT CAST('2024-01-01 01:00:00' AS TIMESTAMP_NTZ(9))"); match expr_from_projection(only(&select.projection)) { - Expr::Cast { data_type, .. } => { - assert_eq!(*data_type, DataType::TimestampNtz(Some(9))); + Expr::Cast(cast) => { + assert_eq!(cast.data_type, DataType::TimestampNtz(Some(9))); } _ => unreachable!(), } From 1581e945e28a6b6b4f936066e33138710c8390c3 Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Sun, 15 Mar 2026 11:05:35 +0100 Subject: [PATCH 5/5] Cargo fmt --- src/ast/mod.rs | 44 +- src/ast/spans.rs | 56 +- src/parser/mod.rs | 264 ++++--- tests/sqlparser_bigquery.rs | 301 ++++---- tests/sqlparser_common.rs | 1328 +++++++++++++++++++-------------- tests/sqlparser_databricks.rs | 153 ++-- tests/sqlparser_duckdb.rs | 21 +- tests/sqlparser_mssql.rs | 3 +- tests/sqlparser_mysql.rs | 29 +- tests/sqlparser_postgres.rs | 162 ++-- tests/sqlparser_snowflake.rs | 50 +- 11 files changed, 1376 insertions(+), 1035 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 0e8f10220f..def3ad8818 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1949,7 +1949,7 @@ impl fmt::Display for Expr { write!(f, ")") } Expr::Cast(cast) => { - let CastExpr { + let CastExpr { kind, expr, data_type, @@ -1985,7 +1985,7 @@ impl fmt::Display for Expr { write!(f, "{expr}::{data_type}") } } - }, + } Expr::Extract { field, syntax, @@ -12358,29 +12358,33 @@ mod tests { #[test] fn test_interval_display() { - let interval = Expr::Interval(Interval { - value: Expr::Value( - Value::SingleQuotedString(String::from("123:45.67")).with_empty_span(), - ), - leading_field: Some(DateTimeField::Minute), - leading_precision: Some(10), - last_field: Some(DateTimeField::Second), - fractional_seconds_precision: Some(9), - }.into()); + let interval = Expr::Interval( + Interval { + value: Expr::Value( + Value::SingleQuotedString(String::from("123:45.67")).with_empty_span(), + ), + leading_field: Some(DateTimeField::Minute), + leading_precision: Some(10), + last_field: Some(DateTimeField::Second), + fractional_seconds_precision: Some(9), + } + .into(), + ); assert_eq!( "INTERVAL '123:45.67' MINUTE (10) TO SECOND (9)", format!("{interval}"), ); - let interval = Expr::Interval(Interval { - value: Expr::Value( - Value::SingleQuotedString(String::from("5")).with_empty_span(), - ), - leading_field: Some(DateTimeField::Second), - leading_precision: Some(1), - last_field: None, - fractional_seconds_precision: Some(3), - }.into()); + let interval = Expr::Interval( + Interval { + value: Expr::Value(Value::SingleQuotedString(String::from("5")).with_empty_span()), + leading_field: Some(DateTimeField::Second), + leading_precision: Some(1), + last_field: None, + fractional_seconds_precision: Some(3), + } + .into(), + ); assert_eq!("INTERVAL '5' SECOND (1, 3)", format!("{interval}")); } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index fd1d10521b..789fe67061 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -28,7 +28,26 @@ use core::iter; use crate::tokenizer::Span; use super::{ - AccessExpr, AlterColumnOperation, AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, AttachedToken, BeginEndStatements, CaseExpr, CaseStatement, CastExpr, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, ConflictTarget, ConnectByKind, ConstraintCharacteristics, ConvertExpr, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Partition, PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill, comments, dcl::SecondaryRoles, value::ValueWithSpan + comments, dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation, + AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, + AttachedToken, BeginEndStatements, CaseExpr, CaseStatement, CastExpr, CloseCursor, + ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConditionalStatementBlock, + ConditionalStatements, ConflictTarget, ConnectByKind, ConstraintCharacteristics, ConvertExpr, + CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, + ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, + Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, + FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert, + Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, + LateralView, LimitClause, MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, + MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, + NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction, + OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Partition, + PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement, + RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement, + ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, + SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject, + TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Value, Values, + ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill, }; /// Given an iterator of spans, return the [Span::union] of all spans. @@ -1547,11 +1566,11 @@ impl Spanned for Expr { is_try: _, } = &**convert; union_spans( - core::iter::once(expr.span()) - .chain(charset.as_ref().map(|i| i.span())) - .chain(styles.iter().map(|i| i.span())), + core::iter::once(expr.span()) + .chain(charset.as_ref().map(|i| i.span())) + .chain(styles.iter().map(|i| i.span())), ) - }, + } Expr::Cast(cast) => { let CastExpr { kind: _, @@ -1606,19 +1625,20 @@ impl Spanned for Expr { else_result, } = &**case; union_spans( - iter::once(case_token.0.span) - .chain( - operand - .as_ref() - .map(|i| i.span()) - .into_iter() - .chain(conditions.iter().flat_map(|case_when| { - [case_when.condition.span(), case_when.result.span()] - })) - .chain(else_result.as_ref().map(|i| i.span())), - ) - .chain(iter::once(end_token.0.span)), - )}, + iter::once(case_token.0.span) + .chain( + operand + .as_ref() + .map(|i| i.span()) + .into_iter() + .chain(conditions.iter().flat_map(|case_when| { + [case_when.condition.span(), case_when.result.span()] + })) + .chain(else_result.as_ref().map(|i| i.span())), + ) + .chain(iter::once(end_token.0.span)), + ) + } Expr::Exists { subquery, .. } => subquery.span(), Expr::Subquery(query) => query.span(), Expr::Struct { .. } => Span::empty(), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 2be5597403..311b50b56b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1655,14 +1655,17 @@ impl<'a> Parser<'a> { // For example: `a -> a * 2`. Token::Arrow if self.dialect.supports_lambda_functions() => { self.expect_token(&Token::Arrow)?; - Ok(Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::One(LambdaFunctionParameter { - name: w.to_ident(w_span), - data_type: None, - }), - body: self.parse_expr()?, - syntax: LambdaSyntax::Arrow, - }.into())) + Ok(Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::One(LambdaFunctionParameter { + name: w.to_ident(w_span), + data_type: None, + }), + body: self.parse_expr()?, + syntax: LambdaSyntax::Arrow, + } + .into(), + )) } // An unreserved word (likely an identifier) that is followed by another word (likley a data type) // which is then followed by an arrow, which indicates a lambda function with a single, typed parameter. @@ -1673,14 +1676,17 @@ impl<'a> Parser<'a> { { let data_type = self.parse_data_type()?; self.expect_token(&Token::Arrow)?; - Ok(Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::One(LambdaFunctionParameter { - name: w.to_ident(w_span), - data_type: Some(data_type), - }), - body: self.parse_expr()?, - syntax: LambdaSyntax::Arrow, - }.into())) + Ok(Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::One(LambdaFunctionParameter { + name: w.to_ident(w_span), + data_type: Some(data_type), + }), + body: self.parse_expr()?, + syntax: LambdaSyntax::Arrow, + } + .into(), + )) } _ => Ok(Expr::Identifier(w.to_ident(w_span))), } @@ -1723,19 +1729,25 @@ impl<'a> Parser<'a> { DataType::Custom(..) => parser_err!("dummy", loc), // MySQL supports using the `BINARY` keyword as a cast to binary type. DataType::Binary(..) if self.dialect.supports_binary_kw_as_cast() => { - Ok(Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: parser.parse_expr()?, - data_type: DataType::Binary(None), - array: false, - format: None, - }.into())) - } - data_type => Ok(Expr::TypedString(TypedString { - data_type, - value: parser.parse_value()?, - uses_odbc_syntax: false, - }.into())), + Ok(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: parser.parse_expr()?, + data_type: DataType::Binary(None), + array: false, + format: None, + } + .into(), + )) + } + data_type => Ok(Expr::TypedString( + TypedString { + data_type, + value: parser.parse_value()?, + uses_odbc_syntax: false, + } + .into(), + )), } })?; @@ -1945,11 +1957,14 @@ impl<'a> Parser<'a> { } fn parse_geometric_type(&mut self, kind: GeometricTypeKind) -> Result { - Ok(Expr::TypedString(TypedString { - data_type: DataType::GeometricType(kind), - value: self.parse_value()?, - uses_odbc_syntax: false, - }.into())) + Ok(Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(kind), + value: self.parse_value()?, + uses_odbc_syntax: false, + } + .into(), + )) } /// Try to parse an [Expr::CompoundFieldAccess] like `a.b.c` or `a.b[1].c`. @@ -2278,11 +2293,14 @@ impl<'a> Parser<'a> { p.expect_token(&Token::RParen)?; p.expect_token(&Token::Arrow)?; let expr = p.parse_expr()?; - Ok(Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::Many(params), - body: expr, - syntax: LambdaSyntax::Arrow, - }.into())) + Ok(Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::Many(params), + body: expr, + syntax: LambdaSyntax::Arrow, + } + .into(), + )) }) } @@ -2302,11 +2320,14 @@ impl<'a> Parser<'a> { self.expect_token(&Token::Colon)?; // Parse the body expression let body = self.parse_expr()?; - Ok(Expr::Lambda(LambdaFunction { - params, - body, - syntax: LambdaSyntax::LambdaKeyword, - }.into())) + Ok(Expr::Lambda( + LambdaFunction { + params, + body, + syntax: LambdaSyntax::LambdaKeyword, + } + .into(), + )) } /// Parses the parameters of a lambda function with optional typing. @@ -2377,11 +2398,14 @@ impl<'a> Parser<'a> { _ => return p.expected("ODBC datetime keyword (t, d, or ts)", token), }; let value = p.parse_value()?; - Ok(Expr::TypedString(TypedString { - data_type, - value, - uses_odbc_syntax: true, - }.into())) + Ok(Expr::TypedString( + TypedString { + data_type, + value, + uses_odbc_syntax: true, + } + .into(), + )) }) } @@ -2681,13 +2705,16 @@ impl<'a> Parser<'a> { None }; let end_token = AttachedToken(self.expect_keyword(Keyword::END)?); - Ok(Expr::Case(CaseExpr { - case_token, - end_token, - operand, - conditions, - else_result, - }.into())) + Ok(Expr::Case( + CaseExpr { + case_token, + end_token, + operand, + conditions, + else_result, + } + .into(), + )) } /// Parse an optional `FORMAT` clause for `CAST` expressions. @@ -2724,14 +2751,17 @@ impl<'a> Parser<'a> { Default::default() }; self.expect_token(&Token::RParen)?; - Ok(Expr::Convert(ConvertExpr { - is_try, - expr, - data_type: Some(data_type), - charset: None, - target_before_value: true, - styles, - }.into())) + Ok(Expr::Convert( + ConvertExpr { + is_try, + expr, + data_type: Some(data_type), + charset: None, + target_before_value: true, + styles, + } + .into(), + )) } /// Parse a SQL CONVERT function: @@ -2747,14 +2777,17 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::USING) { let charset = self.parse_object_name(false)?; self.expect_token(&Token::RParen)?; - return Ok(Expr::Convert(ConvertExpr { - is_try, - expr, - data_type: None, - charset: Some(charset), - target_before_value: false, - styles: vec![], - }.into())); + return Ok(Expr::Convert( + ConvertExpr { + is_try, + expr, + data_type: None, + charset: Some(charset), + target_before_value: false, + styles: vec![], + } + .into(), + )); } self.expect_token(&Token::Comma)?; let data_type = self.parse_data_type()?; @@ -2764,14 +2797,17 @@ impl<'a> Parser<'a> { None }; self.expect_token(&Token::RParen)?; - Ok(Expr::Convert(ConvertExpr { - is_try, - expr, - data_type: Some(data_type), - charset, - target_before_value: false, - styles: vec![], - }.into())) + Ok(Expr::Convert( + ConvertExpr { + is_try, + expr, + data_type: Some(data_type), + charset, + target_before_value: false, + styles: vec![], + } + .into(), + )) } /// Parse a SQL CAST function e.g. `CAST(expr AS FLOAT)` @@ -2783,13 +2819,16 @@ impl<'a> Parser<'a> { let array = self.parse_keyword(Keyword::ARRAY); let format = self.parse_optional_cast_format()?; self.expect_token(&Token::RParen)?; - Ok(Expr::Cast(CastExpr { - kind, - expr, - data_type, - array, - format, - }.into())) + Ok(Expr::Cast( + CastExpr { + kind, + expr, + data_type, + array, + format, + } + .into(), + )) } /// Parse a SQL EXISTS expression e.g. `WHERE EXISTS(SELECT ...)`. @@ -3306,13 +3345,16 @@ impl<'a> Parser<'a> { } }; - Ok(Expr::Interval(Interval { - value, - leading_field, - leading_precision, - last_field, - fractional_seconds_precision: fsec_precision, - }.into())) + Ok(Expr::Interval( + Interval { + value, + leading_field, + leading_precision, + last_field, + fractional_seconds_precision: fsec_precision, + } + .into(), + )) } /// Peek at the next token and determine if it is a temporal unit @@ -4065,13 +4107,16 @@ impl<'a> Parser<'a> { ), } } else if Token::DoubleColon == *tok { - Ok(Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr, - data_type: self.parse_data_type()?, - array: false, - format: None, - }.into())) + Ok(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr, + data_type: self.parse_data_type()?, + array: false, + format: None, + } + .into(), + )) } else if Token::ExclamationMark == *tok && self.dialect.supports_factorial_operator() { Ok(Expr::UnaryOp { op: UnaryOperator::PGPostfixFactorial, @@ -4313,13 +4358,16 @@ impl<'a> Parser<'a> { /// Parse a PostgreSQL casting style which is in the form of `expr::datatype`. pub fn parse_pg_cast(&mut self, expr: Expr) -> Result { - Ok(Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr, - data_type: self.parse_data_type()?, - array: false, - format: None, - }.into())) + Ok(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr, + data_type: self.parse_data_type()?, + array: false, + format: None, + } + .into(), + )) } /// Get the precedence of the next token diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 1a32d19904..6648e13119 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -916,14 +916,17 @@ fn parse_typed_struct_syntax_bigquery() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Datetime(None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Datetime(None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Datetime(None), @@ -960,15 +963,16 @@ fn parse_typed_struct_syntax_bigquery() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::Interval(Interval { - value: Expr::Value( - Value::SingleQuotedString("2".into()).with_empty_span() - ), - leading_field: Some(DateTimeField::Hour), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None - }.into())], + values: vec![Expr::Interval( + Interval { + value: Expr::Value(Value::SingleQuotedString("2".into()).with_empty_span()), + leading_field: Some(DateTimeField::Hour), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Interval { @@ -982,16 +986,19 @@ fn parse_typed_struct_syntax_bigquery() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::SingleQuotedString( - r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::SingleQuotedString( + r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::JSON, @@ -1019,16 +1026,19 @@ fn parse_typed_struct_syntax_bigquery() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString( - "2008-12-25 15:30:00 America/Los_Angeles".into() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString( + "2008-12-25 15:30:00 America/Los_Angeles".into() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Timestamp(None, TimezoneInfo::None), @@ -1040,14 +1050,17 @@ fn parse_typed_struct_syntax_bigquery() { assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Time(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("15:30:00".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Time(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("15:30:00".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Time(None, TimezoneInfo::None), @@ -1062,14 +1075,17 @@ fn parse_typed_struct_syntax_bigquery() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Numeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Numeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Numeric(ExactNumberInfo::None), @@ -1080,14 +1096,17 @@ fn parse_typed_struct_syntax_bigquery() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::BigNumeric(ExactNumberInfo::None), @@ -1258,14 +1277,17 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Datetime(None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Datetime(None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Datetime(None), @@ -1302,15 +1324,16 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::Interval(Interval { - value: Expr::Value( - Value::SingleQuotedString("1".into()).with_empty_span() - ), - leading_field: Some(DateTimeField::Month), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None - }.into())], + values: vec![Expr::Interval( + Interval { + value: Expr::Value(Value::SingleQuotedString("1".into()).with_empty_span()), + leading_field: Some(DateTimeField::Month), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Interval { @@ -1324,16 +1347,19 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::SingleQuotedString( - r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::SingleQuotedString( + r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::JSON, @@ -1361,16 +1387,19 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString( - "2008-12-25 15:30:00 America/Los_Angeles".into() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString( + "2008-12-25 15:30:00 America/Los_Angeles".into() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Timestamp(None, TimezoneInfo::None), @@ -1382,14 +1411,17 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Time(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("15:30:00".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Time(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("15:30:00".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Time(None, TimezoneInfo::None), @@ -1404,14 +1436,17 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Numeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Numeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Numeric(ExactNumberInfo::None), @@ -1422,14 +1457,17 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into())], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::BigNumeric(ExactNumberInfo::None), @@ -2513,14 +2551,17 @@ fn test_triple_quote_typed_strings() { let expr = bigquery().verified_expr(r#"JSON """{"foo":"bar's"}""""#); assert_eq!( - Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::TripleDoubleQuotedString(r#"{"foo":"bar's"}"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::TripleDoubleQuotedString(r#"{"foo":"bar's"}"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr ); } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 5bb5c6ca12..408ab269f0 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3087,26 +3087,32 @@ fn parse_cast() { let sql = "SELECT CAST(id AS BIGINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::BigInt(None), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::BigInt(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS TINYINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::TinyInt(None), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::TinyInt(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); @@ -3129,107 +3135,131 @@ fn parse_cast() { let sql = "SELECT CAST(id AS NVARCHAR(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::Nvarchar(Some(CharacterLength::IntegerLength { - length: 50, - unit: None, - })), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Nvarchar(Some(CharacterLength::IntegerLength { + length: 50, + unit: None, + })), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS CLOB) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::Clob(None), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Clob(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS CLOB(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::Clob(Some(50)), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Clob(Some(50)), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS BINARY(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::Binary(Some(50)), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Binary(Some(50)), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS VARBINARY(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::Varbinary(Some(BinaryLength::IntegerLength { length: 50 })), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Varbinary(Some(BinaryLength::IntegerLength { length: 50 })), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS BLOB) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::Blob(None), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Blob(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS BLOB(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::Blob(Some(50)), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Blob(Some(50)), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(details AS JSONB) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("details")), - data_type: DataType::JSONB, - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("details")), + data_type: DataType::JSONB, + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); } @@ -3239,13 +3269,16 @@ fn parse_try_cast() { let sql = "SELECT TRY_CAST(id AS BIGINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::TryCast, - expr: Expr::Identifier(Ident::new("id")), - data_type: DataType::BigInt(None), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::TryCast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::BigInt(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); verified_stmt("SELECT TRY_CAST(id AS BIGINT) FROM customer"); @@ -6154,14 +6187,17 @@ fn parse_literal_date() { let sql = "SELECT DATE '1999-01-01'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Date, - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::Date, + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -6171,14 +6207,17 @@ fn parse_literal_time() { let sql = "SELECT TIME '01:23:34'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Time(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("01:23:34".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::Time(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("01:23:34".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -6188,14 +6227,17 @@ fn parse_literal_datetime() { let sql = "SELECT DATETIME '1999-01-01 01:23:34.45'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Datetime(None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::Datetime(None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -6205,14 +6247,17 @@ fn parse_literal_timestamp_without_time_zone() { let sql = "SELECT TIMESTAMP '1999-01-01 01:23:34'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); @@ -6224,14 +6269,17 @@ fn parse_literal_timestamp_with_time_zone() { let sql = "SELECT TIMESTAMPTZ '1999-01-01 01:23:34Z'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::Tz), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34Z".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::Tz), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34Z".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); @@ -6245,101 +6293,122 @@ fn parse_interval_all() { let sql = "SELECT INTERVAL '1-1' YEAR TO MONTH"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString(String::from("1-1"))).with_empty_span() - ), - leading_field: Some(DateTimeField::Year), - leading_precision: None, - last_field: Some(DateTimeField::Month), - fractional_seconds_precision: None, - }.into()), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1-1"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Year), + leading_precision: None, + last_field: Some(DateTimeField::Month), + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL '01:01.01' MINUTE (5) TO SECOND (5)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString(String::from("01:01.01"))).with_empty_span() - ), - leading_field: Some(DateTimeField::Minute), - leading_precision: Some(5), - last_field: Some(DateTimeField::Second), - fractional_seconds_precision: Some(5), - }.into()), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("01:01.01"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Minute), + leading_precision: Some(5), + last_field: Some(DateTimeField::Second), + fractional_seconds_precision: Some(5), + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL '1' SECOND (5, 4)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString(String::from("1"))).with_empty_span() - ), - leading_field: Some(DateTimeField::Second), - leading_precision: Some(5), - last_field: None, - fractional_seconds_precision: Some(4), - }.into()), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Second), + leading_precision: Some(5), + last_field: None, + fractional_seconds_precision: Some(4), + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL '10' HOUR"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString(String::from("10"))).with_empty_span() - ), - leading_field: Some(DateTimeField::Hour), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into()), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("10"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Hour), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL 5 DAY"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Expr::value(number("5")), - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into()), + &Expr::Interval( + Interval { + value: Expr::value(number("5")), + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL 5 DAYS"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Expr::value(number("5")), - leading_field: Some(DateTimeField::Days), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into()), + &Expr::Interval( + Interval { + value: Expr::value(number("5")), + leading_field: Some(DateTimeField::Days), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL '10' HOUR (1)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString(String::from("10"))).with_empty_span() - ), - leading_field: Some(DateTimeField::Hour), - leading_precision: Some(1), - last_field: None, - fractional_seconds_precision: None, - }.into()), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("10"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Hour), + leading_precision: Some(1), + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); @@ -6402,15 +6471,18 @@ fn parse_interval_dont_require_unit() { let sql = "SELECT INTERVAL '1 DAY'"; let select = dialects.verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() - ), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into()), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); dialects.verified_only_select("SELECT INTERVAL '1 YEAR'"); @@ -6440,65 +6512,74 @@ fn parse_interval_require_qualifier() { let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { - value: Expr::BinaryOp { - left: Box::new(Expr::value(number("1"))), - op: BinaryOperator::Plus, - right: Box::new(Expr::value(number("1"))), - }, - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into()), + &Expr::Interval( + Interval { + value: Expr::BinaryOp { + left: Box::new(Expr::value(number("1"))), + op: BinaryOperator::Plus, + right: Box::new(Expr::value(number("1"))), + }, + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), ); let sql = "SELECT INTERVAL '1' + '1' DAY"; let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { - value: Expr::BinaryOp { - left: Box::new(Expr::Value( - (Value::SingleQuotedString("1".to_string())).with_empty_span() - )), - op: BinaryOperator::Plus, - right: Box::new(Expr::Value( - (Value::SingleQuotedString("1".to_string())).with_empty_span() - )), - }, - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into()), + &Expr::Interval( + Interval { + value: Expr::BinaryOp { + left: Box::new(Expr::Value( + (Value::SingleQuotedString("1".to_string())).with_empty_span() + )), + op: BinaryOperator::Plus, + right: Box::new(Expr::Value( + (Value::SingleQuotedString("1".to_string())).with_empty_span() + )), + }, + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), ); let sql = "SELECT INTERVAL '1' + '2' - '3' DAY"; let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { - value: Expr::BinaryOp { - left: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Value( - (Value::SingleQuotedString("1".to_string())).with_empty_span() - )), - op: BinaryOperator::Plus, + &Expr::Interval( + Interval { + value: Expr::BinaryOp { + left: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Value( + (Value::SingleQuotedString("1".to_string())).with_empty_span() + )), + op: BinaryOperator::Plus, + right: Box::new(Expr::Value( + (Value::SingleQuotedString("2".to_string())).with_empty_span() + )), + }), + op: BinaryOperator::Minus, right: Box::new(Expr::Value( - (Value::SingleQuotedString("2".to_string())).with_empty_span() + (Value::SingleQuotedString("3".to_string())).with_empty_span() )), - }), - op: BinaryOperator::Minus, - right: Box::new(Expr::Value( - (Value::SingleQuotedString("3".to_string())).with_empty_span() - )), - }, - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into()), + }, + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), ); } @@ -6510,15 +6591,18 @@ fn parse_interval_disallow_interval_expr() { let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() - ), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into()), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), ); dialects.verified_only_select("SELECT INTERVAL '1 YEAR'"); @@ -6533,25 +6617,31 @@ fn parse_interval_disallow_interval_expr() { assert_eq!( expr_from_projection(only(&select.projection)), &Expr::BinaryOp { - left: Box::new(Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() - ), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into())), + left: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + )), op: BinaryOperator::Gt, - right: Box::new(Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString(String::from("1 SECOND"))).with_empty_span() - ), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into())) + right: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1 SECOND"))).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + )) } ); } @@ -6563,15 +6653,18 @@ fn interval_disallow_interval_expr_gt() { assert_eq!( expr, Expr::BinaryOp { - left: Box::new(Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString("1 second".to_string())).with_empty_span() - ), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into(),)), + left: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString("1 second".to_string())).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into(), + )), op: BinaryOperator::Gt, right: Box::new(Expr::Identifier(Ident { value: "x".to_string(), @@ -6588,21 +6681,27 @@ fn interval_disallow_interval_expr_double_colon() { let expr = dialects.verified_expr("INTERVAL '1 second'::TEXT"); assert_eq!( expr, - Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString("1 second".to_string())).with_empty_span() + Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString("1 second".to_string())).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() ), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into()), - data_type: DataType::Text, - array: false, - format: None, - }.into()) + data_type: DataType::Text, + array: false, + format: None, + } + .into() + ) ) } @@ -6656,15 +6755,19 @@ fn parse_interval_and_or_xor() { span: Span::empty(), })), op: BinaryOperator::Plus, - right: Box::new(Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString("5 days".to_string())).with_empty_span(), - ), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into())), + right: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString("5 days".to_string())) + .with_empty_span(), + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into(), + )), }), }), op: BinaryOperator::And, @@ -6682,15 +6785,19 @@ fn parse_interval_and_or_xor() { span: Span::empty(), })), op: BinaryOperator::Plus, - right: Box::new(Expr::Interval(Interval { - value: Expr::Value( - (Value::SingleQuotedString("3 days".to_string())).with_empty_span(), - ), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into())), + right: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString("3 days".to_string())) + .with_empty_span(), + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into(), + )), }), }), }), @@ -6804,11 +6911,12 @@ fn parse_json_keyword() { }'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::SingleQuotedString( - r#"{ + &Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::SingleQuotedString( + r#"{ "id": 10, "type": "fruit", "name": "apple", @@ -6828,12 +6936,14 @@ fn parse_json_keyword() { ] } }"# - .to_string() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false, - }.into()), + .to_string() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false, + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -6842,14 +6952,17 @@ fn parse_json_keyword() { fn parse_typed_strings() { let expr = verified_expr(r#"JSON '{"foo":"bar"}'"#); assert_eq!( - Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"{"foo":"bar"}"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"{"foo":"bar"}"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr ); @@ -6870,14 +6983,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '0'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"0"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"0"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '0'"); @@ -6885,14 +7001,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '123456'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"123456"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"123456"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '123456'"); @@ -6900,14 +7019,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '-3.14'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"-3.14"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"-3.14"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '-3.14'"); @@ -6915,14 +7037,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '-0.54321'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"-0.54321"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"-0.54321"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '-0.54321'"); @@ -6930,14 +7055,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '1.23456e05'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"1.23456e05"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"1.23456e05"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '1.23456e05'"); @@ -6945,14 +7073,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '-9.876e-3'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"-9.876e-3"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"-9.876e-3"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '-9.876e-3'"); @@ -7233,36 +7364,39 @@ fn parse_searched_case_expr() { use self::Expr::{BinaryOp, Case, Identifier, IsNull}; let select = verified_only_select(sql); assert_eq!( - &Case(CaseExpr { - case_token: AttachedToken::empty(), - end_token: AttachedToken::empty(), - operand: None, - conditions: vec![ - CaseWhen { - condition: IsNull(Box::new(Identifier(Ident::new("bar")))), - result: Expr::value(Value::SingleQuotedString("null".to_string())), - }, - CaseWhen { - condition: BinaryOp { - left: Box::new(Identifier(Ident::new("bar"))), - op: Eq, - right: Box::new(Expr::value(number("0"))), + &Case( + CaseExpr { + case_token: AttachedToken::empty(), + end_token: AttachedToken::empty(), + operand: None, + conditions: vec![ + CaseWhen { + condition: IsNull(Box::new(Identifier(Ident::new("bar")))), + result: Expr::value(Value::SingleQuotedString("null".to_string())), }, - result: Expr::value(Value::SingleQuotedString("=0".to_string())), - }, - CaseWhen { - condition: BinaryOp { - left: Box::new(Identifier(Ident::new("bar"))), - op: GtEq, - right: Box::new(Expr::value(number("0"))), + CaseWhen { + condition: BinaryOp { + left: Box::new(Identifier(Ident::new("bar"))), + op: Eq, + right: Box::new(Expr::value(number("0"))), + }, + result: Expr::value(Value::SingleQuotedString("=0".to_string())), }, - result: Expr::value(Value::SingleQuotedString(">=0".to_string())), - }, - ], - else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( - "<0".to_string() - )))), - }.into()), + CaseWhen { + condition: BinaryOp { + left: Box::new(Identifier(Ident::new("bar"))), + op: GtEq, + right: Box::new(Expr::value(number("0"))), + }, + result: Expr::value(Value::SingleQuotedString(">=0".to_string())), + }, + ], + else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( + "<0".to_string() + )))), + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -7274,18 +7408,21 @@ fn parse_simple_case_expr() { let select = verified_only_select(sql); use self::Expr::{Case, Identifier}; assert_eq!( - &Case(CaseExpr { - case_token: AttachedToken::empty(), - end_token: AttachedToken::empty(), - operand: Some(Box::new(Identifier(Ident::new("foo")))), - conditions: vec![CaseWhen { - condition: Expr::value(number("1")), - result: Expr::value(Value::SingleQuotedString("Y".to_string())), - }], - else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( - "N".to_string() - )))), - }.into()), + &Case( + CaseExpr { + case_token: AttachedToken::empty(), + end_token: AttachedToken::empty(), + operand: Some(Box::new(Identifier(Ident::new("foo")))), + conditions: vec![CaseWhen { + condition: Expr::value(number("1")), + result: Expr::value(Value::SingleQuotedString("Y".to_string())), + }], + else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( + "N".to_string() + )))), + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -9345,16 +9482,19 @@ fn parse_double_colon_cast_at_timezone() { assert_eq!( &Expr::AtTimeZone { - timestamp: Box::new(Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::Value( - (Value::SingleQuotedString("2001-01-01T00:00:00.000Z".to_string())) - .with_empty_span() - ), - data_type: DataType::Timestamp(None, TimezoneInfo::None), - array: false, - format: None - }.into())), + timestamp: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("2001-01-01T00:00:00.000Z".to_string())) + .with_empty_span() + ), + data_type: DataType::Timestamp(None, TimezoneInfo::None), + array: false, + format: None + } + .into() + )), time_zone: Box::new(Expr::Value( (Value::SingleQuotedString("Europe/Brussels".to_string())).with_empty_span() )), @@ -13640,27 +13780,33 @@ fn test_dictionary_syntax() { Expr::Dictionary(vec![ DictionaryField { key: Ident::with_quote('\'', "start"), - value: Box::new(Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Value( - (Value::SingleQuotedString("2023-04-01".to_owned())).with_empty_span(), - ), - data_type: DataType::Timestamp(None, TimezoneInfo::None), - array: false, - format: None, - }.into())), + value: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Value( + (Value::SingleQuotedString("2023-04-01".to_owned())).with_empty_span(), + ), + data_type: DataType::Timestamp(None, TimezoneInfo::None), + array: false, + format: None, + } + .into(), + )), }, DictionaryField { key: Ident::with_quote('\'', "end"), - value: Box::new(Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Value( - (Value::SingleQuotedString("2023-04-05".to_owned())).with_empty_span(), - ), - data_type: DataType::Timestamp(None, TimezoneInfo::None), - array: false, - format: None, - }.into())), + value: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Value( + (Value::SingleQuotedString("2023-04-05".to_owned())).with_empty_span(), + ), + data_type: DataType::Timestamp(None, TimezoneInfo::None), + array: false, + format: None, + } + .into(), + )), }, ]), ) @@ -13946,18 +14092,21 @@ fn test_extract_seconds_ok() { Expr::Extract { field: Seconds, syntax: ExtractSyntax::From, - expr: Box::new(Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::Value( - (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() - ), - data_type: DataType::Interval { - fields: None, - precision: None - }, - array: false, - format: None, - }.into())), + expr: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() + ), + data_type: DataType::Interval { + fields: None, + precision: None + }, + array: false, + format: None, + } + .into() + )), } ); @@ -13977,18 +14126,21 @@ fn test_extract_seconds_ok() { projection: vec![UnnamedExpr(Expr::Extract { field: Seconds, syntax: ExtractSyntax::From, - expr: Box::new(Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::Value( - (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span(), - ), - data_type: DataType::Interval { - fields: None, - precision: None, - }, - array: false, - format: None, - }.into())), + expr: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span(), + ), + data_type: DataType::Interval { + fields: None, + precision: None, + }, + array: false, + format: None, + } + .into(), + )), })], exclude: None, into: None, @@ -14035,18 +14187,21 @@ fn test_extract_seconds_single_quote_ok() { span: Span::empty(), }), syntax: ExtractSyntax::From, - expr: Box::new(Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::Value( - (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() - ), - data_type: DataType::Interval { - fields: None, - precision: None - }, - array: false, - format: None, - }.into())), + expr: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() + ), + data_type: DataType::Interval { + fields: None, + precision: None + }, + array: false, + format: None, + } + .into() + )), } ) } @@ -16061,52 +16216,58 @@ fn test_lambdas() { ) ] ), - Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::Many(vec![ - LambdaFunctionParameter { - name: Ident::new("p1"), - data_type: None - }, - LambdaFunctionParameter { - name: Ident::new("p2"), - data_type: None - } - ]), - body: Expr::Case(CaseExpr { - case_token: AttachedToken::empty(), - end_token: AttachedToken::empty(), - operand: None, - conditions: vec![ - CaseWhen { - condition: Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident::new("p1"))), - op: BinaryOperator::Eq, - right: Box::new(Expr::Identifier(Ident::new("p2"))) - }, - result: Expr::value(number("0")), - }, - CaseWhen { - condition: Expr::BinaryOp { - left: Box::new(call( - "reverse", - [Expr::Identifier(Ident::new("p1"))] - )), - op: BinaryOperator::Lt, - right: Box::new(call( - "reverse", - [Expr::Identifier(Ident::new("p2"))] - )), - }, - result: Expr::UnaryOp { - op: UnaryOperator::Minus, - expr: Box::new(Expr::value(number("1"))) - } + Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::Many(vec![ + LambdaFunctionParameter { + name: Ident::new("p1"), + data_type: None }, - ], - else_result: Some(Box::new(Expr::value(number("1")))), - }.into()), - syntax: LambdaSyntax::Arrow, - }.into()) + LambdaFunctionParameter { + name: Ident::new("p2"), + data_type: None + } + ]), + body: Expr::Case( + CaseExpr { + case_token: AttachedToken::empty(), + end_token: AttachedToken::empty(), + operand: None, + conditions: vec![ + CaseWhen { + condition: Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("p1"))), + op: BinaryOperator::Eq, + right: Box::new(Expr::Identifier(Ident::new("p2"))) + }, + result: Expr::value(number("0")), + }, + CaseWhen { + condition: Expr::BinaryOp { + left: Box::new(call( + "reverse", + [Expr::Identifier(Ident::new("p1"))] + )), + op: BinaryOperator::Lt, + right: Box::new(call( + "reverse", + [Expr::Identifier(Ident::new("p2"))] + )), + }, + result: Expr::UnaryOp { + op: UnaryOperator::Minus, + expr: Box::new(Expr::value(number("1"))) + } + }, + ], + else_result: Some(Box::new(Expr::value(number("1")))), + } + .into() + ), + syntax: LambdaSyntax::Arrow, + } + .into() + ) ] )), dialects.verified_only_select(sql).projection[0] @@ -16245,90 +16406,111 @@ fn test_geometry_type() { let sql = "point '1,2'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::Point), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::Point), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "line '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::Line), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::Line), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "path '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::GeometricPath), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::GeometricPath), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "box '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::GeometricBox), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::GeometricBox), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "circle '1,2,3'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::Circle), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::Circle), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "polygon '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::Polygon), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::Polygon), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "lseg '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::LineSegment), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::LineSegment), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); } #[test] diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 1e2292ffc0..a6af9fe51b 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -71,14 +71,17 @@ fn test_databricks_exists() { Expr::value(number("3")) ] ), - Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::One(LambdaFunctionParameter { - name: Ident::new("x"), - data_type: None - }), - body: Expr::IsNull(Box::new(Expr::Identifier(Ident::new("x")))), - syntax: LambdaSyntax::Arrow, - }.into()) + Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::One(LambdaFunctionParameter { + name: Ident::new("x"), + data_type: None + }), + body: Expr::IsNull(Box::new(Expr::Identifier(Ident::new("x")))), + syntax: LambdaSyntax::Arrow, + } + .into() + ) ] ), ); @@ -111,52 +114,58 @@ fn test_databricks_lambdas() { Expr::value(Value::SingleQuotedString("World".to_owned())) ] ), - Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::Many(vec![ - LambdaFunctionParameter { - name: Ident::new("p1"), - data_type: None - }, - LambdaFunctionParameter { - name: Ident::new("p2"), - data_type: None - } - ]), - body: Expr::Case(CaseExpr { - case_token: AttachedToken::empty(), - end_token: AttachedToken::empty(), - operand: None, - conditions: vec![ - CaseWhen { - condition: Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident::new("p1"))), - op: BinaryOperator::Eq, - right: Box::new(Expr::Identifier(Ident::new("p2"))) - }, - result: Expr::value(number("0")) - }, - CaseWhen { - condition: Expr::BinaryOp { - left: Box::new(call( - "reverse", - [Expr::Identifier(Ident::new("p1"))] - )), - op: BinaryOperator::Lt, - right: Box::new(call( - "reverse", - [Expr::Identifier(Ident::new("p2"))] - )), - }, - result: Expr::UnaryOp { - op: UnaryOperator::Minus, - expr: Box::new(Expr::value(number("1"))) - } + Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::Many(vec![ + LambdaFunctionParameter { + name: Ident::new("p1"), + data_type: None }, - ], - else_result: Some(Box::new(Expr::value(number("1")))) - }.into()), - syntax: LambdaSyntax::Arrow, - }.into()) + LambdaFunctionParameter { + name: Ident::new("p2"), + data_type: None + } + ]), + body: Expr::Case( + CaseExpr { + case_token: AttachedToken::empty(), + end_token: AttachedToken::empty(), + operand: None, + conditions: vec![ + CaseWhen { + condition: Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("p1"))), + op: BinaryOperator::Eq, + right: Box::new(Expr::Identifier(Ident::new("p2"))) + }, + result: Expr::value(number("0")) + }, + CaseWhen { + condition: Expr::BinaryOp { + left: Box::new(call( + "reverse", + [Expr::Identifier(Ident::new("p1"))] + )), + op: BinaryOperator::Lt, + right: Box::new(call( + "reverse", + [Expr::Identifier(Ident::new("p2"))] + )), + }, + result: Expr::UnaryOp { + op: UnaryOperator::Minus, + expr: Box::new(Expr::value(number("1"))) + } + }, + ], + else_result: Some(Box::new(Expr::value(number("1")))) + } + .into() + ), + syntax: LambdaSyntax::Arrow, + } + .into() + ) ] )), databricks().verified_only_select(sql).projection[0] @@ -342,28 +351,32 @@ fn data_type_timestamp_ntz() { // Literal assert_eq!( databricks().verified_expr("TIMESTAMP_NTZ '2025-03-29T18:52:00'"), - Expr::TypedString(TypedString { - data_type: DataType::TimestampNtz(None), - value: ValueWithSpan { - value: Value::SingleQuotedString("2025-03-29T18:52:00".to_owned()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }.into()) + Expr::TypedString( + TypedString { + data_type: DataType::TimestampNtz(None), + value: ValueWithSpan { + value: Value::SingleQuotedString("2025-03-29T18:52:00".to_owned()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); // Cast assert_eq!( databricks().verified_expr("(created_at)::TIMESTAMP_NTZ"), - Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::Nested(Box::new(Expr::Identifier( - "created_at".into() - ))), - data_type: DataType::TimestampNtz(None), - array: false, - format: None - }.into()) + Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Nested(Box::new(Expr::Identifier("created_at".into()))), + data_type: DataType::TimestampNtz(None), + array: false, + format: None + } + .into() + ) ); // Column definition diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index 9be39628b0..79e73dfd2a 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -382,15 +382,18 @@ fn test_duckdb_specific_int_types() { let sql = format!("SELECT 123::{dtype_string}"); let select = duckdb().verified_only_select(&sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::Value( - Value::Number("123".parse().unwrap(), false).with_empty_span() - ), - data_type: data_type.clone(), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + Value::Number("123".parse().unwrap(), false).with_empty_span() + ), + data_type: data_type.clone(), + array: false, + format: None, + } + .into() + ), expr_from_projection(&select.projection[0]) ); } diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 542cef4f60..b608204082 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -1284,8 +1284,7 @@ fn parse_cast_varchar_max() { #[test] fn parse_convert() { let sql = "CONVERT(INT, 1, 2, 3, NULL)"; - let Expr::Convert(convert) = ms().verified_expr(sql) - else { + let Expr::Convert(convert) = ms().verified_expr(sql) else { unreachable!() }; let ConvertExpr { diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index b333b2d90a..7d0f1ce44d 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -860,19 +860,22 @@ fn test_functional_key_part() { index_column(mysql_and_generic().verified_stmt( r#"CREATE TABLE t (jsoncol JSON, PRIMARY KEY ((CAST(col ->> '$.id' AS UNSIGNED)) ASC))"# )), - Expr::Nested(Box::new(Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident::new("col"))), - op: BinaryOperator::LongArrow, - right: Box::new(Expr::Value( - Value::SingleQuotedString("$.id".to_string()).with_empty_span() - )), - }, - data_type: DataType::Unsigned, - array: false, - format: None, - }.into()))), + Expr::Nested(Box::new(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("col"))), + op: BinaryOperator::LongArrow, + right: Box::new(Expr::Value( + Value::SingleQuotedString("$.id".to_string()).with_empty_span() + )), + }, + data_type: DataType::Unsigned, + array: false, + format: None, + } + .into() + ))), ); assert_eq!( index_column(mysql_and_generic().verified_stmt( diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index dfb2455181..c19bceaacf 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1845,27 +1845,33 @@ fn parse_execute() { has_parentheses: false, using: vec![ ExprWithAlias { - expr: Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Value( - (Value::Number("1337".parse().unwrap(), false)).with_empty_span() - ), - data_type: DataType::SmallInt(None), - array: false, - format: None - }.into()), + expr: Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Value( + (Value::Number("1337".parse().unwrap(), false)).with_empty_span() + ), + data_type: DataType::SmallInt(None), + array: false, + format: None + } + .into() + ), alias: None }, ExprWithAlias { - expr: Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Value( - (Value::Number("7331".parse().unwrap(), false)).with_empty_span() - ), - data_type: DataType::SmallInt(None), - array: false, - format: None - }.into()), + expr: Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Value( + (Value::Number("7331".parse().unwrap(), false)).with_empty_span() + ), + data_type: DataType::SmallInt(None), + array: false, + format: None + } + .into() + ), alias: None }, ], @@ -2474,25 +2480,28 @@ fn parse_array_index_expr() { let select = pg_and_generic().verified_only_select(sql); assert_eq!( &Expr::CompoundFieldAccess { - root: Box::new(Expr::Nested(Box::new(Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Array(Array { - elem: vec![Expr::Array(Array { - elem: vec![num[2].clone(), num[3].clone(),], + root: Box::new(Expr::Nested(Box::new(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Array(Array { + elem: vec![Expr::Array(Array { + elem: vec![num[2].clone(), num[3].clone(),], + named: true, + })], named: true, - })], - named: true, - }), - data_type: DataType::Array(ArrayElemTypeDef::SquareBracket( - Box::new(DataType::Array(ArrayElemTypeDef::SquareBracket( - Box::new(DataType::Int(None)), + }), + data_type: DataType::Array(ArrayElemTypeDef::SquareBracket( + Box::new(DataType::Array(ArrayElemTypeDef::SquareBracket( + Box::new(DataType::Int(None)), + None + ))), None - ))), - None - )), - array: false, - format: None, - }.into())))), + )), + array: false, + format: None, + } + .into() + )))), access_chain: vec![ AccessExpr::Subscript(Subscript::Index { index: num[1].clone() @@ -5852,34 +5861,44 @@ fn parse_at_time_zone() { // check precedence let expr = Expr::BinaryOp { left: Box::new(Expr::AtTimeZone { - timestamp: Box::new(Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("2001-09-28 01:00".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false, - }.into())), - time_zone: Box::new(Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::Value( - Value::SingleQuotedString("America/Los_Angeles".to_owned()).with_empty_span(), - ), - data_type: DataType::Text, - array: false, - format: None, - }.into())), + timestamp: Box::new(Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("2001-09-28 01:00".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false, + } + .into(), + )), + time_zone: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + Value::SingleQuotedString("America/Los_Angeles".to_owned()) + .with_empty_span(), + ), + data_type: DataType::Text, + array: false, + format: None, + } + .into(), + )), }), op: BinaryOperator::Plus, - right: Box::new(Expr::Interval(Interval { - value: Expr::Value( - Value::SingleQuotedString("23 hours".to_owned()).with_empty_span(), - ), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }.into())), + right: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + Value::SingleQuotedString("23 hours".to_owned()).with_empty_span(), + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into(), + )), }; pretty_assertions::assert_eq!( pg_and_generic().verified_expr( @@ -6688,15 +6707,18 @@ fn arrow_cast_precedence() { span: Span::empty(), })), op: BinaryOperator::Arrow, - right: Box::new(Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::Value( - (Value::SingleQuotedString("bar".to_string())).with_empty_span() - ), - data_type: DataType::Text, - array: false, - format: None, - }.into())), + right: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("bar".to_string())).with_empty_span() + ), + data_type: DataType::Text, + array: false, + format: None, + } + .into() + )), } ) } diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index fed9ae9146..c7de7e279a 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -1275,13 +1275,16 @@ fn parse_array() { let sql = "SELECT CAST(a AS ARRAY) FROM customer"; let select = snowflake().verified_only_select(sql); assert_eq!( - &Expr::Cast(CastExpr { - kind: CastKind::Cast, - expr: Expr::Identifier(Ident::new("a")), - data_type: DataType::Array(ArrayElemTypeDef::None), - array: false, - format: None, - }.into()), + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("a")), + data_type: DataType::Array(ArrayElemTypeDef::None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); } @@ -1377,21 +1380,24 @@ fn parse_semi_structured_data_traversal() { assert_eq!( snowflake().verified_expr("a:b::ARRAY[1]"), Expr::JsonAccess { - value: Box::new(Expr::Cast(CastExpr { - kind: CastKind::DoubleColon, - expr: Expr::JsonAccess { - value: Box::new(Expr::Identifier(Ident::new("a"))), - path: JsonPath { - path: vec![JsonPathElem::Dot { - key: "b".to_string(), - quoted: false - }] - } - }, - data_type: DataType::Array(ArrayElemTypeDef::None), - array: false, - format: None, - }.into())), + value: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::JsonAccess { + value: Box::new(Expr::Identifier(Ident::new("a"))), + path: JsonPath { + path: vec![JsonPathElem::Dot { + key: "b".to_string(), + quoted: false + }] + } + }, + data_type: DataType::Array(ArrayElemTypeDef::None), + array: false, + format: None, + } + .into() + )), path: JsonPath { path: vec![JsonPathElem::Bracket { key: Expr::value(number("1"))