diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c4d1b50cd..def3ad881 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -460,7 +460,7 @@ impl fmt::Display for Array { #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct Interval { /// The interval value expression (commonly a string literal). - pub value: Box, + pub value: Expr, /// Optional leading time unit (e.g., `HOUR`, `MINUTE`). pub leading_field: Option, /// Optional leading precision for the leading field. @@ -475,7 +475,7 @@ pub struct Interval { impl fmt::Display for Interval { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let value = self.value.as_ref(); + let value = &self.value; match ( &self.leading_field, self.leading_precision, @@ -1025,42 +1025,9 @@ pub enum Expr { expr: Box, }, /// CONVERT a value to a different data type or character encoding. e.g. `CONVERT(foo USING utf8mb4)` - Convert { - /// CONVERT (false) or TRY_CONVERT (true) - /// - is_try: bool, - /// The expression to convert. - expr: Box, - /// The target data type, if provided. - data_type: Option, - /// Optional target character encoding (e.g., `utf8mb4`). - charset: Option, - /// `true` when target precedes the value (MSSQL syntax). - target_before_value: bool, - /// How to translate the expression. - /// - /// [MSSQL]: https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16#style - styles: Vec, - }, + Convert(Box), /// `CAST` an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))` - Cast { - /// The cast kind (e.g., `CAST`, `TRY_CAST`). - kind: CastKind, - /// Expression being cast. - expr: Box, - /// Target data type. - data_type: DataType, - /// [MySQL] allows CAST(... AS type ARRAY) in functional index definitions for InnoDB - /// multi-valued indices. It's not really a datatype, and is only allowed in `CAST` in key - /// specifications, so it's a flag here. - /// - /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#function_cast - array: bool, - /// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery] - /// - /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax - format: Option, - }, + Cast(Box), /// AT a timestamp to a different timezone e.g. `FROM_UNIXTIME(0) AT TIME ZONE 'UTC-06:00'` AtTimeZone { /// Timestamp expression to shift. @@ -1192,26 +1159,15 @@ pub enum Expr { /// A constant of form ` 'value'`. /// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`), /// as well as constants of other types (a non-standard PostgreSQL extension). - TypedString(TypedString), + TypedString(Box), /// Scalar function call e.g. `LEFT(foo, 5)` - Function(Function), + 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 /// - Case { - /// The attached `CASE` token (keeps original spacing/comments). - case_token: AttachedToken, - /// The attached `END` token (keeps original spacing/comments). - end_token: AttachedToken, - /// Optional operand expression after `CASE` (for simple CASE). - operand: Option>, - /// The `WHEN ... THEN` conditions and results. - conditions: Vec, - /// Optional `ELSE` result expression. - else_result: Option>, - }, + Case(Box), /// An exists expression `[ NOT ] EXISTS(SELECT ...)`, used in expressions like /// `WHERE [ NOT ] EXISTS (SELECT ...)`. Exists { @@ -1277,7 +1233,7 @@ pub enum Expr { /// An array expression e.g. `ARRAY[1, 2]` Array(Array), /// An interval expression e.g. `INTERVAL '1' YEAR` - Interval(Interval), + Interval(Box), /// `MySQL` specific text search function [(1)]. /// /// Syntax: @@ -1328,7 +1284,7 @@ pub enum Expr { /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/functions#higher-order-functions---operator-and-lambdaparams-expr-function) /// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html) /// [DuckDB](https://duckdb.org/docs/stable/sql/functions/lambda) - Lambda(LambdaFunction), + Lambda(Box), /// Checks membership of a value in a JSON array MemberOf(MemberOf), } @@ -1338,6 +1294,78 @@ 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 + } + } +} + +/// A [`CONVERT` expression](Expr::Convert) +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct ConvertExpr { + /// CONVERT (false) or TRY_CONVERT (true) + /// + pub is_try: bool, + /// The expression to convert. + pub expr: Expr, + /// The target data type, if provided. + pub data_type: Option, + /// Optional target character encoding (e.g., `utf8mb4`). + pub charset: Option, + /// `true` when target precedes the value (MSSQL syntax). + pub target_before_value: bool, + /// How to translate the expression. + /// + /// [MSSQL]: https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16#style + pub styles: Vec, +} + +/// A [`CAST` expression](Expr::Cast) +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct CastExpr { + /// The cast kind (e.g., `CAST`, `TRY_CAST`). + pub kind: CastKind, + /// Expression being cast. + pub expr: Expr, + /// Target data type. + pub data_type: DataType, + /// [MySQL] allows CAST(... AS type ARRAY) in functional index definitions for InnoDB + /// multi-valued indices. It's not really a datatype, and is only allowed in `CAST` in key + /// specifications, so it's a flag here. + /// + /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#function_cast + pub array: bool, + /// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery] + /// + /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax + pub format: Option, +} + +/// A [`CASE` expression](Expr::Case) +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct CaseExpr { + /// The attached `CASE` token (keeps original spacing/comments). + pub case_token: AttachedToken, + /// The attached `END` token (keeps original spacing/comments). + pub end_token: AttachedToken, + /// Optional operand expression after `CASE` (for simple CASE). + pub operand: Option>, + /// The `WHEN ... THEN` conditions and results. + pub conditions: Vec, + /// Optional `ELSE` result expression. + pub else_result: Option>, } /// The contents inside the `[` and `]` in a subscript expression. @@ -1437,7 +1465,7 @@ pub struct LambdaFunction { /// The parameters to the lambda function. pub params: OneOrManyWithParens, /// The body of the lambda function. - pub body: Box, + pub body: Expr, /// The syntax style used to write the lambda function. pub syntax: LambdaSyntax, } @@ -1892,14 +1920,15 @@ impl fmt::Display for Expr { write!(f, "{op}{expr}") } } - Expr::Convert { - is_try, - expr, - target_before_value, - data_type, - charset, - styles, - } => { + Expr::Convert(convert) => { + let ConvertExpr { + is_try, + expr, + target_before_value, + data_type, + charset, + styles, + } = &**convert; write!(f, "{}CONVERT(", if *is_try { "TRY_" } else { "" })?; if let Some(data_type) = data_type { if let Some(charset) = charset { @@ -1919,41 +1948,44 @@ impl fmt::Display for Expr { } write!(f, ")") } - Expr::Cast { - kind, - expr, - data_type, - array, - format, - } => match kind { - CastKind::Cast => { - write!(f, "CAST({expr} AS {data_type}")?; - if *array { - write!(f, " ARRAY")?; + Expr::Cast(cast) => { + let CastExpr { + kind, + expr, + data_type, + array, + format, + } = &**cast; + match kind { + CastKind::Cast => { + write!(f, "CAST({expr} AS {data_type}")?; + if *array { + write!(f, " ARRAY")?; + } + if let Some(format) = format { + write!(f, " FORMAT {format}")?; + } + write!(f, ")") } - if let Some(format) = format { - write!(f, " FORMAT {format}")?; + CastKind::TryCast => { + if let Some(format) = format { + write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})") + } else { + write!(f, "TRY_CAST({expr} AS {data_type})") + } } - write!(f, ")") - } - CastKind::TryCast => { - if let Some(format) = format { - write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})") - } else { - write!(f, "TRY_CAST({expr} AS {data_type})") + CastKind::SafeCast => { + if let Some(format) = format { + write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})") + } else { + write!(f, "SAFE_CAST({expr} AS {data_type})") + } } - } - CastKind::SafeCast => { - if let Some(format) = format { - write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})") - } else { - write!(f, "SAFE_CAST({expr} AS {data_type})") + CastKind::DoubleColon => { + write!(f, "{expr}::{data_type}") } } - CastKind::DoubleColon => { - write!(f, "{expr}::{data_type}") - } - }, + } Expr::Extract { field, syntax, @@ -1983,13 +2015,14 @@ impl fmt::Display for Expr { Expr::Prefixed { prefix, value } => write!(f, "{prefix} {value}"), Expr::TypedString(ts) => ts.fmt(f), Expr::Function(fun) => fun.fmt(f), - Expr::Case { - case_token: _, - end_token: _, - operand, - conditions, - else_result, - } => { + Expr::Case(case) => { + let CaseExpr { + case_token: _, + end_token: _, + operand, + conditions, + else_result, + } = &**case; f.write_str("CASE")?; if let Some(operand) = operand { f.write_str(" ")?; @@ -10887,7 +10920,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 { @@ -12325,29 +12358,33 @@ mod tests { #[test] fn test_interval_display() { - let interval = Expr::Interval(Interval { - value: Box::new(Expr::Value( - Value::SingleQuotedString(String::from("123:45.67")).with_empty_span(), - )), - leading_field: Some(DateTimeField::Minute), - leading_precision: Some(10), - last_field: Some(DateTimeField::Second), - fractional_seconds_precision: Some(9), - }); + let interval = Expr::Interval( + Interval { + value: Expr::Value( + Value::SingleQuotedString(String::from("123:45.67")).with_empty_span(), + ), + leading_field: Some(DateTimeField::Minute), + leading_precision: Some(10), + last_field: Some(DateTimeField::Second), + fractional_seconds_precision: Some(9), + } + .into(), + ); assert_eq!( "INTERVAL '123:45.67' MINUTE (10) TO SECOND (9)", format!("{interval}"), ); - let interval = Expr::Interval(Interval { - value: Box::new(Expr::Value( - Value::SingleQuotedString(String::from("5")).with_empty_span(), - )), - leading_field: Some(DateTimeField::Second), - leading_precision: Some(1), - last_field: None, - fractional_seconds_precision: Some(3), - }); + let interval = Expr::Interval( + Interval { + value: Expr::Value(Value::SingleQuotedString(String::from("5")).with_empty_span()), + leading_field: Some(DateTimeField::Second), + leading_precision: Some(1), + last_field: None, + fractional_seconds_precision: Some(3), + } + .into(), + ); assert_eq!("INTERVAL '5' SECOND (1, 3)", format!("{interval}")); } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 74f731a78..789fe6706 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -30,24 +30,24 @@ use crate::tokenizer::Span; use super::{ comments, dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation, AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, - AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, - ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, - ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, - CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, - ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr, - FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, - IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join, - JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, - MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr, - MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName, - ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy, - OrderByExpr, OrderByKind, OutputClause, Partition, PartitionBoundValue, PivotValueSource, - ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction, - RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, - SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, - TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins, Update, - UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement, - WildcardAdditionalOptions, With, WithFill, + AttachedToken, BeginEndStatements, CaseExpr, CaseStatement, CastExpr, CloseCursor, + ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConditionalStatementBlock, + ConditionalStatements, ConflictTarget, ConnectByKind, ConstraintCharacteristics, ConvertExpr, + CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, + ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, + Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, + FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert, + Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, + LateralView, LimitClause, MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, + MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, + NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction, + OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Partition, + PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement, + RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement, + ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, + SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject, + TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Value, Values, + ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill, }; /// Given an iterator of spans, return the [Span::union] of all spans. @@ -1530,7 +1530,10 @@ impl Spanned for Expr { .union(&union_spans(collation.0.iter().map(|i| i.span()))), Expr::Nested(expr) => expr.span(), Expr::Value(value) => value.span(), - Expr::TypedString(TypedString { value, .. }) => value.span(), + Expr::TypedString(s) => { + let TypedString { value, .. } = &**s; + value.span() + } Expr::Function(function) => function.span(), Expr::GroupingSets(vec) => { union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span()))) @@ -1553,25 +1556,31 @@ impl Spanned for Expr { right, } => left.span().union(&right.span()), Expr::UnaryOp { op: _, expr } => expr.span(), - Expr::Convert { - expr, - data_type: _, - charset, - target_before_value: _, - styles, - is_try: _, - } => union_spans( - core::iter::once(expr.span()) - .chain(charset.as_ref().map(|i| i.span())) - .chain(styles.iter().map(|i| i.span())), - ), - Expr::Cast { - kind: _, - expr, - data_type: _, - array: _, - format: _, - } => expr.span(), + Expr::Convert(convert) => { + let ConvertExpr { + expr, + data_type: _, + charset, + target_before_value: _, + styles, + is_try: _, + } = &**convert; + union_spans( + core::iter::once(expr.span()) + .chain(charset.as_ref().map(|i| i.span())) + .chain(styles.iter().map(|i| i.span())), + ) + } + Expr::Cast(cast) => { + let CastExpr { + kind: _, + expr, + data_type: _, + array: _, + format: _, + } = &**cast; + expr.span() + } Expr::AtTimeZone { timestamp, time_zone, @@ -1607,26 +1616,29 @@ impl Spanned for Expr { ), ), Expr::Prefixed { value, .. } => value.span(), - Expr::Case { - case_token, - end_token, - operand, - conditions, - else_result, - } => union_spans( - iter::once(case_token.0.span) - .chain( - operand - .as_ref() - .map(|i| i.span()) - .into_iter() - .chain(conditions.iter().flat_map(|case_when| { - [case_when.condition.span(), case_when.result.span()] - })) - .chain(else_result.as_ref().map(|i| i.span())), - ) - .chain(iter::once(end_token.0.span)), - ), + Expr::Case(case) => { + let CaseExpr { + case_token, + end_token, + operand, + conditions, + else_result, + } = &**case; + union_spans( + iter::once(case_token.0.span) + .chain( + operand + .as_ref() + .map(|i| i.span()) + .into_iter() + .chain(conditions.iter().flat_map(|case_when| { + [case_when.condition.span(), case_when.result.span()] + })) + .chain(else_result.as_ref().map(|i| i.span())), + ) + .chain(iter::once(end_token.0.span)), + ) + } Expr::Exists { subquery, .. } => subquery.span(), Expr::Subquery(query) => query.span(), Expr::Struct { .. } => Span::empty(), diff --git a/src/ast/visitor.rs b/src/ast/visitor.rs index 5f9b37489..28d5d043d 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(()) /// }); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 6adecb0c6..311b50b56 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1520,7 +1520,7 @@ impl<'a> Parser<'a> { filter: None, over: None, within_group: vec![], - }))) + }.into()))) } Keyword::CURRENT_TIMESTAMP | Keyword::CURRENT_TIME @@ -1582,7 +1582,7 @@ impl<'a> Parser<'a> { null_treatment: None, over: None, within_group: vec![], - }))) + }.into()))) } Keyword::NOT => Ok(Some(self.parse_not()?)), Keyword::MATCH if self.dialect.supports_match_against() => { @@ -1655,14 +1655,17 @@ impl<'a> Parser<'a> { // For example: `a -> a * 2`. Token::Arrow if self.dialect.supports_lambda_functions() => { self.expect_token(&Token::Arrow)?; - Ok(Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::One(LambdaFunctionParameter { - name: w.to_ident(w_span), - data_type: None, - }), - body: Box::new(self.parse_expr()?), - syntax: LambdaSyntax::Arrow, - })) + Ok(Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::One(LambdaFunctionParameter { + name: w.to_ident(w_span), + data_type: None, + }), + body: self.parse_expr()?, + syntax: LambdaSyntax::Arrow, + } + .into(), + )) } // An unreserved word (likely an identifier) that is followed by another word (likley a data type) // which is then followed by an arrow, which indicates a lambda function with a single, typed parameter. @@ -1673,14 +1676,17 @@ impl<'a> Parser<'a> { { let data_type = self.parse_data_type()?; self.expect_token(&Token::Arrow)?; - Ok(Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::One(LambdaFunctionParameter { - name: w.to_ident(w_span), - data_type: Some(data_type), - }), - body: Box::new(self.parse_expr()?), - syntax: LambdaSyntax::Arrow, - })) + Ok(Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::One(LambdaFunctionParameter { + name: w.to_ident(w_span), + data_type: Some(data_type), + }), + body: self.parse_expr()?, + syntax: LambdaSyntax::Arrow, + } + .into(), + )) } _ => Ok(Expr::Identifier(w.to_ident(w_span))), } @@ -1723,19 +1729,25 @@ impl<'a> Parser<'a> { DataType::Custom(..) => parser_err!("dummy", loc), // MySQL supports using the `BINARY` keyword as a cast to binary type. DataType::Binary(..) if self.dialect.supports_binary_kw_as_cast() => { - Ok(Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(parser.parse_expr()?), - data_type: DataType::Binary(None), - array: false, - format: None, - }) + Ok(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: parser.parse_expr()?, + data_type: DataType::Binary(None), + array: false, + format: None, + } + .into(), + )) } - data_type => Ok(Expr::TypedString(TypedString { - data_type, - value: parser.parse_value()?, - uses_odbc_syntax: false, - })), + data_type => Ok(Expr::TypedString( + TypedString { + data_type, + value: parser.parse_value()?, + uses_odbc_syntax: false, + } + .into(), + )), } })?; @@ -1945,11 +1957,14 @@ impl<'a> Parser<'a> { } fn parse_geometric_type(&mut self, kind: GeometricTypeKind) -> Result { - Ok(Expr::TypedString(TypedString { - data_type: DataType::GeometricType(kind), - value: self.parse_value()?, - uses_odbc_syntax: false, - })) + Ok(Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(kind), + value: self.parse_value()?, + uses_odbc_syntax: false, + } + .into(), + )) } /// Try to parse an [Expr::CompoundFieldAccess] like `a.b.c` or `a.b[1].c`. @@ -2278,11 +2293,14 @@ impl<'a> Parser<'a> { p.expect_token(&Token::RParen)?; p.expect_token(&Token::Arrow)?; let expr = p.parse_expr()?; - Ok(Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::Many(params), - body: Box::new(expr), - syntax: LambdaSyntax::Arrow, - })) + Ok(Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::Many(params), + body: expr, + syntax: LambdaSyntax::Arrow, + } + .into(), + )) }) } @@ -2302,11 +2320,14 @@ impl<'a> Parser<'a> { self.expect_token(&Token::Colon)?; // Parse the body expression let body = self.parse_expr()?; - Ok(Expr::Lambda(LambdaFunction { - params, - body: Box::new(body), - syntax: LambdaSyntax::LambdaKeyword, - })) + Ok(Expr::Lambda( + LambdaFunction { + params, + body, + syntax: LambdaSyntax::LambdaKeyword, + } + .into(), + )) } /// Parses the parameters of a lambda function with optional typing. @@ -2377,11 +2398,14 @@ impl<'a> Parser<'a> { _ => return p.expected("ODBC datetime keyword (t, d, or ts)", token), }; let value = p.parse_value()?; - Ok(Expr::TypedString(TypedString { - data_type, - value, - uses_odbc_syntax: true, - })) + Ok(Expr::TypedString( + TypedString { + data_type, + value, + uses_odbc_syntax: true, + } + .into(), + )) }) } @@ -2408,7 +2432,7 @@ impl<'a> Parser<'a> { self.parse_function_call(name).map(Expr::Function) } - fn parse_function_call(&mut self, name: ObjectName) -> Result { + fn parse_function_call(&mut self, name: ObjectName) -> Result, ParserError> { self.expect_token(&Token::LParen)?; // Snowflake permits a subquery to be passed as an argument without @@ -2425,7 +2449,8 @@ impl<'a> Parser<'a> { null_treatment: None, over: None, within_group: vec![], - }); + } + .into()); } let mut args = self.parse_function_argument_list()?; @@ -2493,7 +2518,8 @@ impl<'a> Parser<'a> { filter, over, within_group, - }) + } + .into()) } /// Optionally parses a null treatment clause. @@ -2519,16 +2545,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![], - })) + 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`. @@ -2676,13 +2705,16 @@ impl<'a> Parser<'a> { None }; let end_token = AttachedToken(self.expect_keyword(Keyword::END)?); - Ok(Expr::Case { - case_token, - end_token, - operand, - conditions, - else_result, - }) + Ok(Expr::Case( + CaseExpr { + case_token, + end_token, + operand, + conditions, + else_result, + } + .into(), + )) } /// Parse an optional `FORMAT` clause for `CAST` expressions. @@ -2719,14 +2751,17 @@ impl<'a> Parser<'a> { Default::default() }; self.expect_token(&Token::RParen)?; - Ok(Expr::Convert { - is_try, - expr: Box::new(expr), - data_type: Some(data_type), - charset: None, - target_before_value: true, - styles, - }) + Ok(Expr::Convert( + ConvertExpr { + is_try, + expr, + data_type: Some(data_type), + charset: None, + target_before_value: true, + styles, + } + .into(), + )) } /// Parse a SQL CONVERT function: @@ -2742,14 +2777,17 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::USING) { let charset = self.parse_object_name(false)?; self.expect_token(&Token::RParen)?; - return Ok(Expr::Convert { - is_try, - expr: Box::new(expr), - data_type: None, - charset: Some(charset), - target_before_value: false, - styles: vec![], - }); + return Ok(Expr::Convert( + ConvertExpr { + is_try, + expr, + data_type: None, + charset: Some(charset), + target_before_value: false, + styles: vec![], + } + .into(), + )); } self.expect_token(&Token::Comma)?; let data_type = self.parse_data_type()?; @@ -2759,14 +2797,17 @@ impl<'a> Parser<'a> { None }; self.expect_token(&Token::RParen)?; - Ok(Expr::Convert { - is_try, - expr: Box::new(expr), - data_type: Some(data_type), - charset, - target_before_value: false, - styles: vec![], - }) + Ok(Expr::Convert( + ConvertExpr { + is_try, + expr, + data_type: Some(data_type), + charset, + target_before_value: false, + styles: vec![], + } + .into(), + )) } /// Parse a SQL CAST function e.g. `CAST(expr AS FLOAT)` @@ -2778,13 +2819,16 @@ impl<'a> Parser<'a> { let array = self.parse_keyword(Keyword::ARRAY); let format = self.parse_optional_cast_format()?; self.expect_token(&Token::RParen)?; - Ok(Expr::Cast { - kind, - expr: Box::new(expr), - data_type, - array, - format, - }) + Ok(Expr::Cast( + CastExpr { + kind, + expr, + data_type, + array, + format, + } + .into(), + )) } /// Parse a SQL EXISTS expression e.g. `WHERE EXISTS(SELECT ...)`. @@ -3301,13 +3345,16 @@ impl<'a> Parser<'a> { } }; - Ok(Expr::Interval(Interval { - value: Box::new(value), - leading_field, - leading_precision, - last_field, - fractional_seconds_precision: fsec_precision, - })) + Ok(Expr::Interval( + Interval { + value, + leading_field, + leading_precision, + last_field, + fractional_seconds_precision: fsec_precision, + } + .into(), + )) } /// Peek at the next token and determine if it is a temporal unit @@ -4060,13 +4107,16 @@ impl<'a> Parser<'a> { ), } } else if Token::DoubleColon == *tok { - Ok(Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(expr), - data_type: self.parse_data_type()?, - array: false, - format: None, - }) + Ok(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr, + data_type: self.parse_data_type()?, + array: false, + format: None, + } + .into(), + )) } else if Token::ExclamationMark == *tok && self.dialect.supports_factorial_operator() { Ok(Expr::UnaryOp { op: UnaryOperator::PGPostfixFactorial, @@ -4308,13 +4358,16 @@ impl<'a> Parser<'a> { /// Parse a PostgreSQL casting style which is in the form of `expr::datatype`. pub fn parse_pg_cast(&mut self, expr: Expr) -> Result { - Ok(Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(expr), - data_type: self.parse_data_type()?, - array: false, - format: None, - }) + Ok(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr, + data_type: self.parse_data_type()?, + array: false, + format: None, + } + .into(), + )) } /// Get the precedence of the next token @@ -11095,7 +11148,7 @@ impl<'a> Parser<'a> { let object_name = self.parse_object_name(false)?; if self.peek_token_ref().token == Token::LParen { match self.parse_function(object_name)? { - Expr::Function(f) => Ok(Statement::Call(f)), + Expr::Function(f) => Ok(Statement::Call(*f)), other => parser_err!( format!("Expected a simple procedure call but found: {other}"), self.peek_token_ref().span.start @@ -13839,7 +13892,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, 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 9ba5960e8..b0be41722 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![], - }) + 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 79db34b06..6648e1311 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -916,14 +916,17 @@ fn parse_typed_struct_syntax_bigquery() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Datetime(None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Datetime(None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Datetime(None), @@ -960,15 +963,16 @@ fn parse_typed_struct_syntax_bigquery() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::Interval(Interval { - value: Box::new(Expr::Value( - Value::SingleQuotedString("2".into()).with_empty_span() - )), - leading_field: Some(DateTimeField::Hour), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None - })], + values: vec![Expr::Interval( + Interval { + value: Expr::Value(Value::SingleQuotedString("2".into()).with_empty_span()), + leading_field: Some(DateTimeField::Hour), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Interval { @@ -982,16 +986,19 @@ fn parse_typed_struct_syntax_bigquery() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::SingleQuotedString( - r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::SingleQuotedString( + r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::JSON, @@ -1019,16 +1026,19 @@ fn parse_typed_struct_syntax_bigquery() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString( - "2008-12-25 15:30:00 America/Los_Angeles".into() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString( + "2008-12-25 15:30:00 America/Los_Angeles".into() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Timestamp(None, TimezoneInfo::None), @@ -1040,14 +1050,17 @@ fn parse_typed_struct_syntax_bigquery() { assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Time(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("15:30:00".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Time(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("15:30:00".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Time(None, TimezoneInfo::None), @@ -1062,14 +1075,17 @@ fn parse_typed_struct_syntax_bigquery() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Numeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Numeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Numeric(ExactNumberInfo::None), @@ -1080,14 +1096,17 @@ fn parse_typed_struct_syntax_bigquery() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::BigNumeric(ExactNumberInfo::None), @@ -1258,14 +1277,17 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Datetime(None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Datetime(None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Datetime(None), @@ -1302,15 +1324,16 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::Interval(Interval { - value: Box::new(Expr::Value( - Value::SingleQuotedString("1".into()).with_empty_span() - )), - leading_field: Some(DateTimeField::Month), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None - })], + values: vec![Expr::Interval( + Interval { + value: Expr::Value(Value::SingleQuotedString("1".into()).with_empty_span()), + leading_field: Some(DateTimeField::Month), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Interval { @@ -1324,16 +1347,19 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::SingleQuotedString( - r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::SingleQuotedString( + r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::JSON, @@ -1361,16 +1387,19 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString( - "2008-12-25 15:30:00 America/Los_Angeles".into() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString( + "2008-12-25 15:30:00 America/Los_Angeles".into() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Timestamp(None, TimezoneInfo::None), @@ -1382,14 +1411,17 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Time(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("15:30:00".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Time(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("15:30:00".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Time(None, TimezoneInfo::None), @@ -1404,14 +1436,17 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::Numeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::Numeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::Numeric(ExactNumberInfo::None), @@ -1422,14 +1457,17 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { ); assert_eq!( &Expr::Struct { - values: vec![Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - })], + values: vec![Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + )], fields: vec![StructField { field_name: None, field_type: DataType::BigNumeric(ExactNumberInfo::None), @@ -2229,25 +2267,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, - }), + 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)), @@ -2510,14 +2551,17 @@ fn test_triple_quote_typed_strings() { let expr = bigquery().verified_expr(r#"JSON """{"foo":"bar's"}""""#); assert_eq!( - Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::TripleDoubleQuotedString(r#"{"foo":"bar's"}"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::TripleDoubleQuotedString(r#"{"foo":"bar's"}"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr ); } diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 82f79577b..0688741f2 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,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(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(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 { @@ -876,7 +880,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 +895,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 08fb6107f..408ab269f 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![] - }), + &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 - }), + &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,11 +1725,14 @@ 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 +1753,14 @@ 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 +1792,14 @@ 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 +1811,14 @@ 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 +1832,14 @@ 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 +1873,39 @@ 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] @@ -2966,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![] - })), + 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"))), }), @@ -2997,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![] - })), + 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"))), }), @@ -3062,26 +3087,32 @@ fn parse_cast() { let sql = "SELECT CAST(id AS BIGINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::BigInt(None), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::BigInt(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS TINYINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::TinyInt(None), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::TinyInt(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); @@ -3104,107 +3135,131 @@ fn parse_cast() { let sql = "SELECT CAST(id AS NVARCHAR(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::Nvarchar(Some(CharacterLength::IntegerLength { - length: 50, - unit: None, - })), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Nvarchar(Some(CharacterLength::IntegerLength { + length: 50, + unit: None, + })), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS CLOB) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::Clob(None), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Clob(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS CLOB(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::Clob(Some(50)), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Clob(Some(50)), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS BINARY(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::Binary(Some(50)), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Binary(Some(50)), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS VARBINARY(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::Varbinary(Some(BinaryLength::IntegerLength { length: 50 })), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Varbinary(Some(BinaryLength::IntegerLength { length: 50 })), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS BLOB) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::Blob(None), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Blob(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS BLOB(50)) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::Blob(Some(50)), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::Blob(Some(50)), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(details AS JSONB) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("details"))), - data_type: DataType::JSONB, - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("details")), + data_type: DataType::JSONB, + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); } @@ -3214,13 +3269,16 @@ fn parse_try_cast() { let sql = "SELECT TRY_CAST(id AS BIGINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::TryCast, - expr: Box::new(Expr::Identifier(Ident::new("id"))), - data_type: DataType::BigInt(None), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::TryCast, + expr: Expr::Identifier(Ident::new("id")), + data_type: DataType::BigInt(None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); verified_stmt("SELECT TRY_CAST(id AS BIGINT) FROM customer"); @@ -3425,59 +3483,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() + ), expr_from_projection(only(&select.projection)) ); @@ -5598,35 +5659,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![] - }), + &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)) ); } @@ -5638,35 +5702,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![], - }), + &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)) ); @@ -5713,39 +5780,42 @@ 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![], - }), + &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]) ); 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 +5851,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, .. }), .. @@ -5851,34 +5921,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![], - }), + 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, @@ -5886,34 +5959,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![], - }), + 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, @@ -6111,14 +6187,17 @@ fn parse_literal_date() { let sql = "SELECT DATE '1999-01-01'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Date, - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::Date, + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -6128,14 +6207,17 @@ fn parse_literal_time() { let sql = "SELECT TIME '01:23:34'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Time(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("01:23:34".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::Time(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("01:23:34".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -6145,14 +6227,17 @@ fn parse_literal_datetime() { let sql = "SELECT DATETIME '1999-01-01 01:23:34.45'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Datetime(None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::Datetime(None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -6162,14 +6247,17 @@ fn parse_literal_timestamp_without_time_zone() { let sql = "SELECT TIMESTAMP '1999-01-01 01:23:34'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); @@ -6181,14 +6269,17 @@ fn parse_literal_timestamp_with_time_zone() { let sql = "SELECT TIMESTAMPTZ '1999-01-01 01:23:34Z'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::Tz), - value: ValueWithSpan { - value: Value::SingleQuotedString("1999-01-01 01:23:34Z".into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::Tz), + value: ValueWithSpan { + value: Value::SingleQuotedString("1999-01-01 01:23:34Z".into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); @@ -6202,101 +6293,122 @@ fn parse_interval_all() { let sql = "SELECT INTERVAL '1-1' YEAR TO MONTH"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString(String::from("1-1"))).with_empty_span() - )), - leading_field: Some(DateTimeField::Year), - leading_precision: None, - last_field: Some(DateTimeField::Month), - fractional_seconds_precision: None, - }), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1-1"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Year), + leading_precision: None, + last_field: Some(DateTimeField::Month), + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL '01:01.01' MINUTE (5) TO SECOND (5)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString(String::from("01:01.01"))).with_empty_span() - )), - leading_field: Some(DateTimeField::Minute), - leading_precision: Some(5), - last_field: Some(DateTimeField::Second), - fractional_seconds_precision: Some(5), - }), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("01:01.01"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Minute), + leading_precision: Some(5), + last_field: Some(DateTimeField::Second), + fractional_seconds_precision: Some(5), + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL '1' SECOND (5, 4)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString(String::from("1"))).with_empty_span() - )), - leading_field: Some(DateTimeField::Second), - leading_precision: Some(5), - last_field: None, - fractional_seconds_precision: Some(4), - }), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Second), + leading_precision: Some(5), + last_field: None, + fractional_seconds_precision: Some(4), + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL '10' HOUR"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString(String::from("10"))).with_empty_span() - )), - leading_field: Some(DateTimeField::Hour), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("10"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Hour), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL 5 DAY"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Box::new(Expr::value(number("5"))), - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }), + &Expr::Interval( + Interval { + value: Expr::value(number("5")), + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL 5 DAYS"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Box::new(Expr::value(number("5"))), - leading_field: Some(DateTimeField::Days), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }), + &Expr::Interval( + Interval { + value: Expr::value(number("5")), + leading_field: Some(DateTimeField::Days), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); let sql = "SELECT INTERVAL '10' HOUR (1)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString(String::from("10"))).with_empty_span() - )), - leading_field: Some(DateTimeField::Hour), - leading_precision: Some(1), - last_field: None, - fractional_seconds_precision: None, - }), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("10"))).with_empty_span() + ), + leading_field: Some(DateTimeField::Hour), + leading_precision: Some(1), + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); @@ -6359,15 +6471,18 @@ fn parse_interval_dont_require_unit() { let sql = "SELECT INTERVAL '1 DAY'"; let select = dialects.verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() - )), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), expr_from_projection(only(&select.projection)), ); dialects.verified_only_select("SELECT INTERVAL '1 YEAR'"); @@ -6397,65 +6512,74 @@ fn parse_interval_require_qualifier() { let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { - value: Box::new(Expr::BinaryOp { - left: Box::new(Expr::value(number("1"))), - op: BinaryOperator::Plus, - right: Box::new(Expr::value(number("1"))), - }), - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }), + &Expr::Interval( + Interval { + value: Expr::BinaryOp { + left: Box::new(Expr::value(number("1"))), + op: BinaryOperator::Plus, + right: Box::new(Expr::value(number("1"))), + }, + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), ); let sql = "SELECT INTERVAL '1' + '1' DAY"; let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { - value: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Value( - (Value::SingleQuotedString("1".to_string())).with_empty_span() - )), - op: BinaryOperator::Plus, - right: Box::new(Expr::Value( - (Value::SingleQuotedString("1".to_string())).with_empty_span() - )), - }), - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }), + &Expr::Interval( + Interval { + value: Expr::BinaryOp { + left: Box::new(Expr::Value( + (Value::SingleQuotedString("1".to_string())).with_empty_span() + )), + op: BinaryOperator::Plus, + right: Box::new(Expr::Value( + (Value::SingleQuotedString("1".to_string())).with_empty_span() + )), + }, + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), ); let sql = "SELECT INTERVAL '1' + '2' - '3' DAY"; let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { - value: Box::new(Expr::BinaryOp { - left: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Value( - (Value::SingleQuotedString("1".to_string())).with_empty_span() - )), - op: BinaryOperator::Plus, + &Expr::Interval( + Interval { + value: Expr::BinaryOp { + left: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Value( + (Value::SingleQuotedString("1".to_string())).with_empty_span() + )), + op: BinaryOperator::Plus, + right: Box::new(Expr::Value( + (Value::SingleQuotedString("2".to_string())).with_empty_span() + )), + }), + op: BinaryOperator::Minus, right: Box::new(Expr::Value( - (Value::SingleQuotedString("2".to_string())).with_empty_span() + (Value::SingleQuotedString("3".to_string())).with_empty_span() )), - }), - op: BinaryOperator::Minus, - right: Box::new(Expr::Value( - (Value::SingleQuotedString("3".to_string())).with_empty_span() - )), - }), - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }), + }, + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), ); } @@ -6467,15 +6591,18 @@ fn parse_interval_disallow_interval_expr() { let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() - )), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - }), + &Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), ); dialects.verified_only_select("SELECT INTERVAL '1 YEAR'"); @@ -6490,25 +6617,31 @@ fn parse_interval_disallow_interval_expr() { assert_eq!( expr_from_projection(only(&select.projection)), &Expr::BinaryOp { - left: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() - )), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - })), + left: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1 DAY"))).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + )), op: BinaryOperator::Gt, - right: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString(String::from("1 SECOND"))).with_empty_span() - )), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - })) + right: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString(String::from("1 SECOND"))).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + )) } ); } @@ -6520,15 +6653,18 @@ fn interval_disallow_interval_expr_gt() { assert_eq!( expr, Expr::BinaryOp { - left: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString("1 second".to_string())).with_empty_span() - )), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - },)), + left: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString("1 second".to_string())).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into(), + )), op: BinaryOperator::Gt, right: Box::new(Expr::Identifier(Ident { value: "x".to_string(), @@ -6545,21 +6681,27 @@ fn interval_disallow_interval_expr_double_colon() { let expr = dialects.verified_expr("INTERVAL '1 second'::TEXT"); assert_eq!( expr, - Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString("1 second".to_string())).with_empty_span() - )), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - })), - data_type: DataType::Text, - array: false, - format: None, - } + Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString("1 second".to_string())).with_empty_span() + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into() + ), + data_type: DataType::Text, + array: false, + format: None, + } + .into() + ) ) } @@ -6613,15 +6755,19 @@ fn parse_interval_and_or_xor() { span: Span::empty(), })), op: BinaryOperator::Plus, - right: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString("5 days".to_string())).with_empty_span(), - )), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - })), + right: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString("5 days".to_string())) + .with_empty_span(), + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into(), + )), }), }), op: BinaryOperator::And, @@ -6639,15 +6785,19 @@ fn parse_interval_and_or_xor() { span: Span::empty(), })), op: BinaryOperator::Plus, - right: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( - (Value::SingleQuotedString("3 days".to_string())).with_empty_span(), - )), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - })), + right: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + (Value::SingleQuotedString("3 days".to_string())) + .with_empty_span(), + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into(), + )), }), }), }), @@ -6761,11 +6911,12 @@ fn parse_json_keyword() { }'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::SingleQuotedString( - r#"{ + &Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::SingleQuotedString( + r#"{ "id": 10, "type": "fruit", "name": "apple", @@ -6785,12 +6936,14 @@ fn parse_json_keyword() { ] } }"# - .to_string() - ), - span: Span::empty(), - }, - uses_odbc_syntax: false, - }), + .to_string() + ), + span: Span::empty(), + }, + uses_odbc_syntax: false, + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -6799,23 +6952,27 @@ fn parse_json_keyword() { fn parse_typed_strings() { let expr = verified_expr(r#"JSON '{"foo":"bar"}'"#); assert_eq!( - Expr::TypedString(TypedString { - data_type: DataType::JSON, - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"{"foo":"bar"}"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + Expr::TypedString( + TypedString { + data_type: DataType::JSON, + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"{"foo":"bar"}"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr ); - if let Expr::TypedString(TypedString { - data_type, - value, - uses_odbc_syntax: false, - }) = expr - { + if let Expr::TypedString(s) = expr { + let TypedString { + data_type, + value, + uses_odbc_syntax, + } = *s; + assert_eq!(false, uses_odbc_syntax); assert_eq!(DataType::JSON, data_type); assert_eq!(r#"{"foo":"bar"}"#, value.into_string().unwrap()); } @@ -6826,14 +6983,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '0'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"0"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"0"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '0'"); @@ -6841,14 +7001,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '123456'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"123456"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"123456"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '123456'"); @@ -6856,14 +7019,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '-3.14'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"-3.14"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"-3.14"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '-3.14'"); @@ -6871,14 +7037,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '-0.54321'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"-0.54321"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"-0.54321"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '-0.54321'"); @@ -6886,14 +7055,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '1.23456e05'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"1.23456e05"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"1.23456e05"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '1.23456e05'"); @@ -6901,14 +7073,17 @@ fn parse_bignumeric_keyword() { let sql = r#"SELECT BIGNUMERIC '-9.876e-3'"#; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString(TypedString { - data_type: DataType::BigNumeric(ExactNumberInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString(r#"-9.876e-3"#.into()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }), + &Expr::TypedString( + TypedString { + data_type: DataType::BigNumeric(ExactNumberInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString(r#"-9.876e-3"#.into()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ), expr_from_projection(only(&select.projection)), ); verified_stmt("SELECT BIGNUMERIC '-9.876e-3'"); @@ -7189,36 +7364,39 @@ fn parse_searched_case_expr() { use self::Expr::{BinaryOp, Case, Identifier, IsNull}; let select = verified_only_select(sql); assert_eq!( - &Case { - case_token: AttachedToken::empty(), - end_token: AttachedToken::empty(), - operand: None, - conditions: vec![ - CaseWhen { - condition: IsNull(Box::new(Identifier(Ident::new("bar")))), - result: Expr::value(Value::SingleQuotedString("null".to_string())), - }, - CaseWhen { - condition: BinaryOp { - left: Box::new(Identifier(Ident::new("bar"))), - op: Eq, - right: Box::new(Expr::value(number("0"))), + &Case( + CaseExpr { + case_token: AttachedToken::empty(), + end_token: AttachedToken::empty(), + operand: None, + conditions: vec![ + CaseWhen { + condition: IsNull(Box::new(Identifier(Ident::new("bar")))), + result: Expr::value(Value::SingleQuotedString("null".to_string())), }, - result: Expr::value(Value::SingleQuotedString("=0".to_string())), - }, - CaseWhen { - condition: BinaryOp { - left: Box::new(Identifier(Ident::new("bar"))), - op: GtEq, - right: Box::new(Expr::value(number("0"))), + CaseWhen { + condition: BinaryOp { + left: Box::new(Identifier(Ident::new("bar"))), + op: Eq, + right: Box::new(Expr::value(number("0"))), + }, + result: Expr::value(Value::SingleQuotedString("=0".to_string())), }, - result: Expr::value(Value::SingleQuotedString(">=0".to_string())), - }, - ], - else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( - "<0".to_string() - )))), - }, + CaseWhen { + condition: BinaryOp { + left: Box::new(Identifier(Ident::new("bar"))), + op: GtEq, + right: Box::new(Expr::value(number("0"))), + }, + result: Expr::value(Value::SingleQuotedString(">=0".to_string())), + }, + ], + else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( + "<0".to_string() + )))), + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -7230,18 +7408,21 @@ fn parse_simple_case_expr() { let select = verified_only_select(sql); use self::Expr::{Case, Identifier}; assert_eq!( - &Case { - case_token: AttachedToken::empty(), - end_token: AttachedToken::empty(), - operand: Some(Box::new(Identifier(Ident::new("foo")))), - conditions: vec![CaseWhen { - condition: Expr::value(number("1")), - result: Expr::value(Value::SingleQuotedString("Y".to_string())), - }], - else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( - "N".to_string() - )))), - }, + &Case( + CaseExpr { + case_token: AttachedToken::empty(), + end_token: AttachedToken::empty(), + operand: Some(Box::new(Identifier(Ident::new("foo")))), + conditions: vec![CaseWhen { + condition: Expr::value(number("1")), + result: Expr::value(Value::SingleQuotedString("Y".to_string())), + }], + else_result: Some(Box::new(Expr::value(Value::SingleQuotedString( + "N".to_string() + )))), + } + .into() + ), expr_from_projection(only(&select.projection)), ); } @@ -9301,16 +9482,19 @@ fn parse_double_colon_cast_at_timezone() { assert_eq!( &Expr::AtTimeZone { - timestamp: Box::new(Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( - (Value::SingleQuotedString("2001-01-01T00:00:00.000Z".to_string())) - .with_empty_span() - )), - data_type: DataType::Timestamp(None, TimezoneInfo::None), - array: false, - format: None - }), + timestamp: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("2001-01-01T00:00:00.000Z".to_string())) + .with_empty_span() + ), + data_type: DataType::Timestamp(None, TimezoneInfo::None), + array: false, + format: None + } + .into() + )), time_zone: Box::new(Expr::Value( (Value::SingleQuotedString("Europe/Brussels".to_string())).with_empty_span() )), @@ -10604,7 +10788,7 @@ fn parse_time_functions() { within_group: vec![], }; assert_eq!( - &Expr::Function(select_localtime_func_call_ast.clone()), + &Expr::Function(select_localtime_func_call_ast.clone().into()), expr_from_projection(&select.projection[0]) ); @@ -10613,7 +10797,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]) ); } @@ -12687,25 +12871,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, - }), + 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(), + ), }), ], }; @@ -13029,26 +13216,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 - })), - 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, @@ -13059,19 +13228,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![] - }), + 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") }, ] @@ -13528,15 +13721,14 @@ 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); @@ -13588,27 +13780,33 @@ fn test_dictionary_syntax() { Expr::Dictionary(vec![ DictionaryField { key: Ident::with_quote('\'', "start"), - value: Box::new(Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Value( - (Value::SingleQuotedString("2023-04-01".to_owned())).with_empty_span(), - )), - data_type: DataType::Timestamp(None, TimezoneInfo::None), - array: false, - format: None, - }), + value: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Value( + (Value::SingleQuotedString("2023-04-01".to_owned())).with_empty_span(), + ), + data_type: DataType::Timestamp(None, TimezoneInfo::None), + array: false, + format: None, + } + .into(), + )), }, DictionaryField { key: Ident::with_quote('\'', "end"), - value: Box::new(Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Value( - (Value::SingleQuotedString("2023-04-05".to_owned())).with_empty_span(), - )), - data_type: DataType::Timestamp(None, TimezoneInfo::None), - array: false, - format: None, - }), + value: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Value( + (Value::SingleQuotedString("2023-04-05".to_owned())).with_empty_span(), + ), + data_type: DataType::Timestamp(None, TimezoneInfo::None), + array: false, + format: None, + } + .into(), + )), }, ]), ) @@ -13894,18 +14092,21 @@ fn test_extract_seconds_ok() { Expr::Extract { field: Seconds, syntax: ExtractSyntax::From, - expr: Box::new(Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( - (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() - )), - data_type: DataType::Interval { - fields: None, - precision: None - }, - array: false, - format: None, - }), + expr: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() + ), + data_type: DataType::Interval { + fields: None, + precision: None + }, + array: false, + format: None, + } + .into() + )), } ); @@ -13925,18 +14126,21 @@ fn test_extract_seconds_ok() { projection: vec![UnnamedExpr(Expr::Extract { field: Seconds, syntax: ExtractSyntax::From, - expr: Box::new(Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( - (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span(), - )), - data_type: DataType::Interval { - fields: None, - precision: None, - }, - array: false, - format: None, - }), + expr: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span(), + ), + data_type: DataType::Interval { + fields: None, + precision: None, + }, + array: false, + format: None, + } + .into(), + )), })], exclude: None, into: None, @@ -13983,18 +14187,21 @@ fn test_extract_seconds_single_quote_ok() { span: Span::empty(), }), syntax: ExtractSyntax::From, - expr: Box::new(Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( - (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() - )), - data_type: DataType::Interval { - fields: None, - precision: None - }, - array: false, - format: None, - }), + expr: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("2 seconds".to_string())).with_empty_span() + ), + data_type: DataType::Interval { + fields: None, + precision: None + }, + array: false, + format: None, + } + .into() + )), } ) } @@ -15425,22 +15632,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![] - })), + 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")))] } ); @@ -15449,48 +15659,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![] - })), - 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![], - })), + within_group: vec![], + } + .into(), + )), access_chain: vec![AccessExpr::Dot(Expr::Identifier(Ident::new("b")))], }; @@ -16000,52 +16216,58 @@ fn test_lambdas() { ) ] ), - Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::Many(vec![ - LambdaFunctionParameter { - name: Ident::new("p1"), - data_type: None - }, - LambdaFunctionParameter { - name: Ident::new("p2"), - data_type: None - } - ]), - body: Box::new(Expr::Case { - case_token: AttachedToken::empty(), - end_token: AttachedToken::empty(), - operand: None, - conditions: vec![ - CaseWhen { - condition: Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident::new("p1"))), - op: BinaryOperator::Eq, - right: Box::new(Expr::Identifier(Ident::new("p2"))) - }, - result: Expr::value(number("0")), - }, - CaseWhen { - condition: Expr::BinaryOp { - left: Box::new(call( - "reverse", - [Expr::Identifier(Ident::new("p1"))] - )), - op: BinaryOperator::Lt, - right: Box::new(call( - "reverse", - [Expr::Identifier(Ident::new("p2"))] - )), - }, - result: Expr::UnaryOp { - op: UnaryOperator::Minus, - expr: Box::new(Expr::value(number("1"))) - } + Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::Many(vec![ + LambdaFunctionParameter { + name: Ident::new("p1"), + data_type: None }, - ], - else_result: Some(Box::new(Expr::value(number("1")))), - }), - syntax: LambdaSyntax::Arrow, - }) + LambdaFunctionParameter { + name: Ident::new("p2"), + data_type: None + } + ]), + body: Expr::Case( + CaseExpr { + case_token: AttachedToken::empty(), + end_token: AttachedToken::empty(), + operand: None, + conditions: vec![ + CaseWhen { + condition: Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("p1"))), + op: BinaryOperator::Eq, + right: Box::new(Expr::Identifier(Ident::new("p2"))) + }, + result: Expr::value(number("0")), + }, + CaseWhen { + condition: Expr::BinaryOp { + left: Box::new(call( + "reverse", + [Expr::Identifier(Ident::new("p1"))] + )), + op: BinaryOperator::Lt, + right: Box::new(call( + "reverse", + [Expr::Identifier(Ident::new("p2"))] + )), + }, + result: Expr::UnaryOp { + op: UnaryOperator::Minus, + expr: Box::new(Expr::value(number("1"))) + } + }, + ], + else_result: Some(Box::new(Expr::value(number("1")))), + } + .into() + ), + syntax: LambdaSyntax::Arrow, + } + .into() + ) ] )), dialects.verified_only_select(sql).projection[0] @@ -16184,90 +16406,111 @@ fn test_geometry_type() { let sql = "point '1,2'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::Point), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::Point), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "line '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::Line), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::Line), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "path '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::GeometricPath), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::GeometricPath), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "box '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::GeometricBox), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::GeometricBox), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "circle '1,2,3'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::Circle), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::Circle), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "polygon '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::Polygon), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::Polygon), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); let sql = "lseg '1,2,3,4'"; assert_eq!( all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql), - Expr::TypedString(TypedString { - data_type: DataType::GeometricType(GeometricTypeKind::LineSegment), - value: ValueWithSpan { - value: Value::SingleQuotedString("1,2,3,4".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }) + Expr::TypedString( + TypedString { + data_type: DataType::GeometricType(GeometricTypeKind::LineSegment), + value: ValueWithSpan { + value: Value::SingleQuotedString("1,2,3,4".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); } #[test] diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 79b3d0654..a6af9fe51 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -71,14 +71,17 @@ fn test_databricks_exists() { Expr::value(number("3")) ] ), - Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::One(LambdaFunctionParameter { - name: Ident::new("x"), - data_type: None - }), - body: Box::new(Expr::IsNull(Box::new(Expr::Identifier(Ident::new("x"))))), - syntax: LambdaSyntax::Arrow, - }) + Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::One(LambdaFunctionParameter { + name: Ident::new("x"), + data_type: None + }), + body: Expr::IsNull(Box::new(Expr::Identifier(Ident::new("x")))), + syntax: LambdaSyntax::Arrow, + } + .into() + ) ] ), ); @@ -111,52 +114,58 @@ fn test_databricks_lambdas() { Expr::value(Value::SingleQuotedString("World".to_owned())) ] ), - Expr::Lambda(LambdaFunction { - params: OneOrManyWithParens::Many(vec![ - LambdaFunctionParameter { - name: Ident::new("p1"), - data_type: None - }, - LambdaFunctionParameter { - name: Ident::new("p2"), - data_type: None - } - ]), - body: Box::new(Expr::Case { - case_token: AttachedToken::empty(), - end_token: AttachedToken::empty(), - operand: None, - conditions: vec![ - CaseWhen { - condition: Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident::new("p1"))), - op: BinaryOperator::Eq, - right: Box::new(Expr::Identifier(Ident::new("p2"))) - }, - result: Expr::value(number("0")) + Expr::Lambda( + LambdaFunction { + params: OneOrManyWithParens::Many(vec![ + LambdaFunctionParameter { + name: Ident::new("p1"), + data_type: None }, - CaseWhen { - condition: Expr::BinaryOp { - left: Box::new(call( - "reverse", - [Expr::Identifier(Ident::new("p1"))] - )), - op: BinaryOperator::Lt, - right: Box::new(call( - "reverse", - [Expr::Identifier(Ident::new("p2"))] - )), - }, - result: Expr::UnaryOp { - op: UnaryOperator::Minus, - expr: Box::new(Expr::value(number("1"))) - } - }, - ], - else_result: Some(Box::new(Expr::value(number("1")))) - }), - syntax: LambdaSyntax::Arrow, - }) + LambdaFunctionParameter { + name: Ident::new("p2"), + data_type: None + } + ]), + body: Expr::Case( + CaseExpr { + case_token: AttachedToken::empty(), + end_token: AttachedToken::empty(), + operand: None, + conditions: vec![ + CaseWhen { + condition: Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("p1"))), + op: BinaryOperator::Eq, + right: Box::new(Expr::Identifier(Ident::new("p2"))) + }, + result: Expr::value(number("0")) + }, + CaseWhen { + condition: Expr::BinaryOp { + left: Box::new(call( + "reverse", + [Expr::Identifier(Ident::new("p1"))] + )), + op: BinaryOperator::Lt, + right: Box::new(call( + "reverse", + [Expr::Identifier(Ident::new("p2"))] + )), + }, + result: Expr::UnaryOp { + op: UnaryOperator::Minus, + expr: Box::new(Expr::value(number("1"))) + } + }, + ], + else_result: Some(Box::new(Expr::value(number("1")))) + } + .into() + ), + syntax: LambdaSyntax::Arrow, + } + .into() + ) ] )), databricks().verified_only_select(sql).projection[0] @@ -342,28 +351,32 @@ fn data_type_timestamp_ntz() { // Literal assert_eq!( databricks().verified_expr("TIMESTAMP_NTZ '2025-03-29T18:52:00'"), - Expr::TypedString(TypedString { - data_type: DataType::TimestampNtz(None), - value: ValueWithSpan { - value: Value::SingleQuotedString("2025-03-29T18:52:00".to_owned()), - span: Span::empty(), - }, - uses_odbc_syntax: false - }) + Expr::TypedString( + TypedString { + data_type: DataType::TimestampNtz(None), + value: ValueWithSpan { + value: Value::SingleQuotedString("2025-03-29T18:52:00".to_owned()), + span: Span::empty(), + }, + uses_odbc_syntax: false + } + .into() + ) ); // Cast assert_eq!( databricks().verified_expr("(created_at)::TIMESTAMP_NTZ"), - Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::Nested(Box::new(Expr::Identifier( - "created_at".into() - )))), - data_type: DataType::TimestampNtz(None), - array: false, - format: None - } + Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Nested(Box::new(Expr::Identifier("created_at".into()))), + data_type: DataType::TimestampNtz(None), + array: false, + format: None + } + .into() + ) ); // Column definition diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index 671e92b97..79e73dfd2 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -382,15 +382,18 @@ fn test_duckdb_specific_int_types() { let sql = format!("SELECT 123::{dtype_string}"); let select = duckdb().verified_only_select(&sql); assert_eq!( - &Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( - Value::Number("123".parse().unwrap(), false).with_empty_span() - )), - data_type: data_type.clone(), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + Value::Number("123".parse().unwrap(), false).with_empty_span() + ), + data_type: data_type.clone(), + array: false, + format: None, + } + .into() + ), expr_from_projection(&select.projection[0]) ); } @@ -634,7 +637,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 +666,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 1b0948518..1623be1a3 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 07dd0fcb6..b60820408 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,310 +979,294 @@ 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!( - &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(_))) - )); - } - _ => 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( + (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))"); - 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] @@ -1300,19 +1284,19 @@ fn parse_cast_varchar_max() { #[test] fn parse_convert() { let sql = "CONVERT(INT, 1, 2, 3, NULL)"; - let Expr::Convert { + let Expr::Convert(convert) = ms().verified_expr(sql) else { + unreachable!() + }; + let ConvertExpr { is_try, expr, data_type, charset, target_before_value, styles, - } = ms().verified_expr(sql) - else { - unreachable!() - }; + } = *convert; assert!(!is_try); - assert_eq!(Expr::value(number("1")), *expr); + assert_eq!(Expr::value(number("1")), expr); assert_eq!(Some(DataType::Int(None)), data_type); assert!(charset.is_none()); assert!(target_before_value); @@ -1909,7 +1893,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 6c59997c3..7d0f1ce44 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -832,17 +832,13 @@ 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); } } @@ -864,37 +860,40 @@ fn test_functional_key_part() { index_column(mysql_and_generic().verified_stmt( r#"CREATE TABLE t (jsoncol JSON, PRIMARY KEY ((CAST(col ->> '$.id' AS UNSIGNED)) ASC))"# )), - Expr::Nested(Box::new(Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident::new("col"))), - op: BinaryOperator::LongArrow, - right: Box::new(Expr::Value( - Value::SingleQuotedString("$.id".to_string()).with_empty_span() - )), - }), - data_type: DataType::Unsigned, - array: false, - format: None, - })), + Expr::Nested(Box::new(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("col"))), + op: BinaryOperator::LongArrow, + right: Box::new(Expr::Value( + Value::SingleQuotedString("$.id".to_string()).with_empty_span() + )), + }, + data_type: DataType::Unsigned, + array: false, + format: None, + } + .into() + ))), ); assert_eq!( index_column(mysql_and_generic().verified_stmt( r#"CREATE TABLE t (jsoncol JSON, PRIMARY KEY ((CAST(col ->> '$.fields' AS UNSIGNED ARRAY)) ASC))"# )), - Expr::Nested(Box::new(Expr::Cast { + Expr::Nested(Box::new(Expr::Cast(CastExpr { kind: CastKind::Cast, - expr: Box::new(Expr::BinaryOp { + expr: Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("col"))), op: BinaryOperator::LongArrow, right: Box::new(Expr::Value( Value::SingleQuotedString("$.fields".to_string()).with_empty_span() )), - }), + }, data_type: DataType::Unsigned, array: true, format: None, - })), + }.into()))), ); } diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 6b4f35d78..c19bceaac 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1845,27 +1845,33 @@ fn parse_execute() { has_parentheses: false, using: vec![ ExprWithAlias { - expr: Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Value( - (Value::Number("1337".parse().unwrap(), false)).with_empty_span() - )), - data_type: DataType::SmallInt(None), - array: false, - format: None - }, + expr: Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Value( + (Value::Number("1337".parse().unwrap(), false)).with_empty_span() + ), + data_type: DataType::SmallInt(None), + array: false, + format: None + } + .into() + ), alias: None }, ExprWithAlias { - expr: Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Value( - (Value::Number("7331".parse().unwrap(), false)).with_empty_span() - )), - data_type: DataType::SmallInt(None), - array: false, - format: None - }, + expr: Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Value( + (Value::Number("7331".parse().unwrap(), false)).with_empty_span() + ), + data_type: DataType::SmallInt(None), + array: false, + format: None + } + .into() + ), alias: None }, ], @@ -2474,25 +2480,28 @@ fn parse_array_index_expr() { let select = pg_and_generic().verified_only_select(sql); assert_eq!( &Expr::CompoundFieldAccess { - root: Box::new(Expr::Nested(Box::new(Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Array(Array { - elem: vec![Expr::Array(Array { - elem: vec![num[2].clone(), num[3].clone(),], + root: Box::new(Expr::Nested(Box::new(Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Array(Array { + elem: vec![Expr::Array(Array { + elem: vec![num[2].clone(), num[3].clone(),], + named: true, + })], named: true, - })], - named: true, - })), - data_type: DataType::Array(ArrayElemTypeDef::SquareBracket( - Box::new(DataType::Array(ArrayElemTypeDef::SquareBracket( - Box::new(DataType::Int(None)), + }), + data_type: DataType::Array(ArrayElemTypeDef::SquareBracket( + Box::new(DataType::Array(ArrayElemTypeDef::SquareBracket( + Box::new(DataType::Int(None)), + None + ))), None - ))), - None - )), - array: false, - format: None, - }))), + )), + array: false, + format: None, + } + .into() + )))), access_chain: vec![ AccessExpr::Subscript(Subscript::Index { index: num[1].clone() @@ -2744,39 +2753,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![], - }), + 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 +3208,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![] - }), + &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)), ); } @@ -3538,8 +3553,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 +3570,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 +3596,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 +3615,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 +3634,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:?}" ); @@ -3690,35 +3707,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![], - })))), + 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]) @@ -3876,55 +3898,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![], - }), + &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![], - }), + &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![], - }), + &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![], - }), + &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]) ); } @@ -4363,20 +4397,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![], - }), + &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] { @@ -5824,34 +5861,44 @@ fn parse_at_time_zone() { // check precedence let expr = Expr::BinaryOp { left: Box::new(Expr::AtTimeZone { - timestamp: Box::new(Expr::TypedString(TypedString { - data_type: DataType::Timestamp(None, TimezoneInfo::None), - value: ValueWithSpan { - value: Value::SingleQuotedString("2001-09-28 01:00".to_string()), - span: Span::empty(), - }, - uses_odbc_syntax: false, - })), - time_zone: Box::new(Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( - Value::SingleQuotedString("America/Los_Angeles".to_owned()).with_empty_span(), - )), - data_type: DataType::Text, - array: false, - format: None, - }), + timestamp: Box::new(Expr::TypedString( + TypedString { + data_type: DataType::Timestamp(None, TimezoneInfo::None), + value: ValueWithSpan { + value: Value::SingleQuotedString("2001-09-28 01:00".to_string()), + span: Span::empty(), + }, + uses_odbc_syntax: false, + } + .into(), + )), + time_zone: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + Value::SingleQuotedString("America/Los_Angeles".to_owned()) + .with_empty_span(), + ), + data_type: DataType::Text, + array: false, + format: None, + } + .into(), + )), }), op: BinaryOperator::Plus, - right: Box::new(Expr::Interval(Interval { - value: Box::new(Expr::Value( - Value::SingleQuotedString("23 hours".to_owned()).with_empty_span(), - )), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - })), + right: Box::new(Expr::Interval( + Interval { + value: Expr::Value( + Value::SingleQuotedString("23 hours".to_owned()).with_empty_span(), + ), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + .into(), + )), }; pretty_assertions::assert_eq!( pg_and_generic().verified_expr( @@ -6660,15 +6707,18 @@ fn arrow_cast_precedence() { span: Span::empty(), })), op: BinaryOperator::Arrow, - right: Box::new(Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::Value( - (Value::SingleQuotedString("bar".to_string())).with_empty_span() - )), - data_type: DataType::Text, - array: false, - format: None, - }), + right: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::Value( + (Value::SingleQuotedString("bar".to_string())).with_empty_span() + ), + data_type: DataType::Text, + array: false, + format: None, + } + .into() + )), } ) } diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 184aa5b69..b50bfd144 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 666876fa7..c7de7e279 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -533,22 +533,25 @@ fn test_snowflake_create_table_cluster_by() { Some(WrappedCollection::Parentheses(vec![ Expr::Identifier(Ident::new("a")), Expr::Identifier(Ident::new("b")), - Expr::Function(Function { - name: ObjectName::from(vec![Ident::new("my_func")]), - uses_odbc_syntax: false, - parameters: FunctionArguments::None, - args: FunctionArguments::List(FunctionArgumentList { - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("c")) - ))], - duplicate_treatment: None, - clauses: vec![], - }), - filter: None, - null_treatment: None, - over: None, - within_group: vec![], - }), + 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 ) @@ -1272,13 +1275,16 @@ fn parse_array() { let sql = "SELECT CAST(a AS ARRAY) FROM customer"; let select = snowflake().verified_only_select(sql); assert_eq!( - &Expr::Cast { - kind: CastKind::Cast, - expr: Box::new(Expr::Identifier(Ident::new("a"))), - data_type: DataType::Array(ArrayElemTypeDef::None), - array: false, - format: None, - }, + &Expr::Cast( + CastExpr { + kind: CastKind::Cast, + expr: Expr::Identifier(Ident::new("a")), + data_type: DataType::Array(ArrayElemTypeDef::None), + array: false, + format: None, + } + .into() + ), expr_from_projection(only(&select.projection)) ); } @@ -1374,21 +1380,24 @@ fn parse_semi_structured_data_traversal() { assert_eq!( snowflake().verified_expr("a:b::ARRAY[1]"), Expr::JsonAccess { - value: Box::new(Expr::Cast { - kind: CastKind::DoubleColon, - expr: Box::new(Expr::JsonAccess { - value: Box::new(Expr::Identifier(Ident::new("a"))), - path: JsonPath { - path: vec![JsonPathElem::Dot { - key: "b".to_string(), - quoted: false - }] - } - }), - data_type: DataType::Array(ArrayElemTypeDef::None), - array: false, - format: None, - }), + value: Box::new(Expr::Cast( + CastExpr { + kind: CastKind::DoubleColon, + expr: Expr::JsonAccess { + value: Box::new(Expr::Identifier(Ident::new("a"))), + path: JsonPath { + path: vec![JsonPathElem::Dot { + key: "b".to_string(), + quoted: false + }] + } + }, + data_type: DataType::Array(ArrayElemTypeDef::None), + array: false, + format: None, + } + .into() + )), path: JsonPath { path: vec![JsonPathElem::Bracket { key: Expr::value(number("1")) @@ -1435,7 +1444,7 @@ fn parse_delimited_identifiers() { expr_from_projection(&select.projection[0]), ); assert_eq!( - &Expr::Function(Function { + Some(&Function { name: ObjectName::from(vec![Ident::with_quote('"', "myfun")]), uses_odbc_syntax: false, parameters: FunctionArguments::None, @@ -1449,7 +1458,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 } => { @@ -1653,22 +1662,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![] - }) + 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() + ) ], ); } @@ -4755,70 +4767,65 @@ fn test_snowflake_create_view_copy_grants() { #[test] fn test_snowflake_identifier_function() { // Using IDENTIFIER to reference a column - match &snowflake() + let SelectItem::UnnamedExpr(expr) = &snowflake() .verified_only_select("SELECT identifier('email') FROM customers") .projection[0] - { - SelectItem::UnnamedExpr(Expr::Function(Function { name, args, .. })) => { - assert_eq!(*name, ObjectName::from(vec![Ident::new("identifier")])); - assert_eq!( - *args, - FunctionArguments::List(FunctionArgumentList { - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - Value::SingleQuotedString("email".to_string()).into() - )))], - clauses: vec![], - duplicate_treatment: None - }) - ); - } - _ => unreachable!(), - } + 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!(), - } + 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() - .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!(), - } + 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, + 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')") { @@ -4946,8 +4953,8 @@ fn test_timestamp_ntz_with_precision() { let select = snowflake().verified_only_select("SELECT CAST('2024-01-01 01:00:00' AS TIMESTAMP_NTZ(9))"); match expr_from_projection(only(&select.projection)) { - Expr::Cast { data_type, .. } => { - assert_eq!(*data_type, DataType::TimestampNtz(Some(9))); + Expr::Cast(cast) => { + assert_eq!(cast.data_type, DataType::TimestampNtz(Some(9))); } _ => unreachable!(), } diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 33c38fb0a..b5049a0a9 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![], - }))] + 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() + ))] ); } }