From f8e9e185be5add02dceae7f40d611fa558326969 Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Fri, 6 Mar 2026 18:36:00 +0100 Subject: [PATCH 1/3] 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 e201f78420..e008625557 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1014,6 +1014,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) /// @@ -1032,6 +1033,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, @@ -1181,14 +1183,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, @@ -1266,6 +1271,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)]. /// @@ -1317,6 +1323,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), @@ -1327,6 +1334,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. @@ -10741,7 +10758,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 eaaa95ec8b..ba32917cb1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1516,7 +1516,7 @@ impl<'a> Parser<'a> { filter: None, over: None, within_group: vec![], - }))) + }.into()))) } Keyword::CURRENT_TIMESTAMP | Keyword::CURRENT_TIME @@ -1578,7 +1578,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() => { @@ -2404,7 +2404,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 @@ -2421,7 +2421,7 @@ impl<'a> Parser<'a> { null_treatment: None, over: None, within_group: vec![], - }); + }.into()); } let mut args = self.parse_function_argument_list()?; @@ -2489,7 +2489,7 @@ impl<'a> Parser<'a> { filter, over, within_group, - }) + }.into()) } /// Optionally parses a null treatment clause. @@ -2524,7 +2524,7 @@ impl<'a> Parser<'a> { over: None, null_treatment: None, within_group: vec![], - })) + }.into())) } /// Parse window frame `UNITS` clause: `ROWS`, `RANGE`, or `GROUPS`. @@ -11036,7 +11036,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 @@ -13780,7 +13780,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 ce962cb807..d632086ae4 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -2237,7 +2237,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 a59e3b96e4..9760be7f6d 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, @@ -10598,7 +10592,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]) ); @@ -10607,7 +10601,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]) ); } @@ -12699,7 +12693,7 @@ fn parse_map_access_expr() { over: None, within_group: vec![], uses_odbc_syntax: false, - }), + }.into()), }), ], }; @@ -13040,7 +13034,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")]), @@ -13065,7 +13059,7 @@ fn test_selective_aggregation() { null_treatment: None, over: None, within_group: vec![] - }), + }.into()), alias: Ident::new("agg2") }, ] @@ -13522,15 +13516,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); @@ -15434,7 +15425,7 @@ fn parse_composite_access_expr() { filter: None, over: None, within_group: vec![] - })), + }.into())), access_chain: vec![AccessExpr::Dot(Expr::Identifier(Ident::new("b")))] } ); @@ -15458,7 +15449,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"))), @@ -15484,7 +15475,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 33a6cb4565..66f372f926 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 da6ecace66..f90929fbda 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 60aca14b3c..a8104f87fd 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 c51cf3bdf1..b89fc321ed 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -522,7 +522,7 @@ fn test_snowflake_create_table_cluster_by() { null_treatment: None, over: None, within_group: vec![], - }), + }.into()), ])), cluster_by ) @@ -1409,7 +1409,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, @@ -1423,7 +1423,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 } => { @@ -1642,7 +1642,7 @@ fn test_alter_table_clustering() { null_treatment: None, over: None, within_group: vec![] - }) + }.into()) ], ); } @@ -4653,70 +4653,55 @@ fn test_snowflake_create_view_with_composite_policy_name() { #[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 6aaec4c60c908e559d22da401f31608bcd9ba28a Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Fri, 6 Mar 2026 19:40:04 +0100 Subject: [PATCH 2/3] 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 ba32917cb1..271db8e7b4 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2421,7 +2421,8 @@ impl<'a> Parser<'a> { null_treatment: None, over: None, within_group: vec![], - }.into()); + } + .into()); } let mut args = self.parse_function_argument_list()?; @@ -2489,7 +2490,8 @@ impl<'a> Parser<'a> { filter, over, within_group, - }.into()) + } + .into()) } /// Optionally parses a null treatment clause. @@ -2515,16 +2517,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`. @@ -13780,7 +13785,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 d632086ae4..4fee21e3ab 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -2219,25 +2219,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 9760be7f6d..c50261c1b3 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, @@ -12675,25 +12724,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(), + ), }), ], }; @@ -13017,26 +13069,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, @@ -13047,19 +13081,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") }, ] @@ -13521,7 +13579,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); @@ -15410,22 +15470,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")))] } ); @@ -15434,48 +15497,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 f90929fbda..3b71445ccf 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 a8104f87fd..a3fb09095c 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 b89fc321ed..713a551f9a 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -507,22 +507,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 ) @@ -1627,22 +1630,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() + ) ], ); } @@ -4655,7 +4661,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!( @@ -4672,7 +4681,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!( @@ -4687,10 +4699,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 7f68507287d78264d2621c5f4dafd41a615854be Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Fri, 6 Mar 2026 21:07:40 +0100 Subject: [PATCH 3/3] 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(()) /// });