@@ -460,7 +460,7 @@ impl fmt::Display for Array {
460460#[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
461461pub struct Interval {
462462 /// The interval value expression (commonly a string literal).
463- pub value : Box < Expr > ,
463+ pub value : Expr ,
464464 /// Optional leading time unit (e.g., `HOUR`, `MINUTE`).
465465 pub leading_field : Option < DateTimeField > ,
466466 /// Optional leading precision for the leading field.
@@ -475,7 +475,7 @@ pub struct Interval {
475475
476476impl fmt:: Display for Interval {
477477 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
478- let value = self . value . as_ref ( ) ;
478+ let value = & self . value ;
479479 match (
480480 & self . leading_field ,
481481 self . leading_precision ,
@@ -1025,44 +1025,9 @@ pub enum Expr {
10251025 expr : Box < Expr > ,
10261026 } ,
10271027 /// CONVERT a value to a different data type or character encoding. e.g. `CONVERT(foo USING utf8mb4)`
1028- // XXX too big
1029- Convert {
1030- /// CONVERT (false) or TRY_CONVERT (true)
1031- /// <https://learn.microsoft.com/en-us/sql/t-sql/functions/try-convert-transact-sql?view=sql-server-ver16>
1032- is_try : bool ,
1033- /// The expression to convert.
1034- expr : Box < Expr > ,
1035- /// The target data type, if provided.
1036- data_type : Option < DataType > ,
1037- /// Optional target character encoding (e.g., `utf8mb4`).
1038- charset : Option < ObjectName > ,
1039- /// `true` when target precedes the value (MSSQL syntax).
1040- target_before_value : bool ,
1041- /// How to translate the expression.
1042- ///
1043- /// [MSSQL]: https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16#style
1044- styles : Vec < Expr > ,
1045- } ,
1028+ Convert ( Box < ConvertExpr > ) ,
10461029 /// `CAST` an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))`
1047- // XXX too big
1048- Cast {
1049- /// The cast kind (e.g., `CAST`, `TRY_CAST`).
1050- kind : CastKind ,
1051- /// Expression being cast.
1052- expr : Box < Expr > ,
1053- /// Target data type.
1054- data_type : DataType ,
1055- /// [MySQL] allows CAST(... AS type ARRAY) in functional index definitions for InnoDB
1056- /// multi-valued indices. It's not really a datatype, and is only allowed in `CAST` in key
1057- /// specifications, so it's a flag here.
1058- ///
1059- /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#function_cast
1060- array : bool ,
1061- /// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery]
1062- ///
1063- /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
1064- format : Option < CastFormat > ,
1065- } ,
1030+ Cast ( Box < CastExpr > ) ,
10661031 /// AT a timestamp to a different timezone e.g. `FROM_UNIXTIME(0) AT TIME ZONE 'UTC-06:00'`
10671032 AtTimeZone {
10681033 /// Timestamp expression to shift.
@@ -1194,29 +1159,15 @@ pub enum Expr {
11941159 /// A constant of form `<data_type> 'value'`.
11951160 /// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
11961161 /// as well as constants of other types (a non-standard PostgreSQL extension).
1197- // XXX too big
1198- TypedString ( TypedString ) ,
1162+ TypedString ( Box < TypedString > ) ,
11991163 /// Scalar function call e.g. `LEFT(foo, 5)`
1200- // XXX too big
12011164 Function ( Box < Function > ) ,
12021165 /// `CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END`
12031166 ///
12041167 /// Note we only recognize a complete single expression as `<condition>`,
12051168 /// not `< 0` nor `1, 2, 3` as allowed in a `<simple when clause>` per
12061169 /// <https://jakewheat.github.io/sql-overview/sql-2011-foundation-grammar.html#simple-when-clause>
1207- // XXX too big
1208- Case {
1209- /// The attached `CASE` token (keeps original spacing/comments).
1210- case_token : AttachedToken ,
1211- /// The attached `END` token (keeps original spacing/comments).
1212- end_token : AttachedToken ,
1213- /// Optional operand expression after `CASE` (for simple CASE).
1214- operand : Option < Box < Expr > > ,
1215- /// The `WHEN ... THEN` conditions and results.
1216- conditions : Vec < CaseWhen > ,
1217- /// Optional `ELSE` result expression.
1218- else_result : Option < Box < Expr > > ,
1219- } ,
1170+ Case ( Box < CaseExpr > ) ,
12201171 /// An exists expression `[ NOT ] EXISTS(SELECT ...)`, used in expressions like
12211172 /// `WHERE [ NOT ] EXISTS (SELECT ...)`.
12221173 Exists {
@@ -1282,8 +1233,7 @@ pub enum Expr {
12821233 /// An array expression e.g. `ARRAY[1, 2]`
12831234 Array ( Array ) ,
12841235 /// An interval expression e.g. `INTERVAL '1' YEAR`
1285- // XXX too big
1286- Interval ( Interval ) ,
1236+ Interval ( Box < Interval > ) ,
12871237 /// `MySQL` specific text search function [(1)].
12881238 ///
12891239 /// Syntax:
@@ -1334,8 +1284,7 @@ pub enum Expr {
13341284 /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/functions#higher-order-functions---operator-and-lambdaparams-expr-function)
13351285 /// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html)
13361286 /// [DuckDB](https://duckdb.org/docs/stable/sql/functions/lambda)
1337- // XXX too big
1338- Lambda ( LambdaFunction ) ,
1287+ Lambda ( Box < LambdaFunction > ) ,
13391288 /// Checks membership of a value in a JSON array
13401289 MemberOf ( MemberOf ) ,
13411290}
@@ -1357,6 +1306,68 @@ impl Expr {
13571306 }
13581307}
13591308
1309+ /// A [`CONVERT` expression](Expr::Convert)
1310+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
1311+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
1312+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
1313+ pub struct ConvertExpr {
1314+ /// CONVERT (false) or TRY_CONVERT (true)
1315+ /// <https://learn.microsoft.com/en-us/sql/t-sql/functions/try-convert-transact-sql?view=sql-server-ver16>
1316+ pub is_try : bool ,
1317+ /// The expression to convert.
1318+ pub expr : Expr ,
1319+ /// The target data type, if provided.
1320+ pub data_type : Option < DataType > ,
1321+ /// Optional target character encoding (e.g., `utf8mb4`).
1322+ pub charset : Option < ObjectName > ,
1323+ /// `true` when target precedes the value (MSSQL syntax).
1324+ pub target_before_value : bool ,
1325+ /// How to translate the expression.
1326+ ///
1327+ /// [MSSQL]: https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16#style
1328+ pub styles : Vec < Expr > ,
1329+ }
1330+
1331+ /// A [`CAST` expression](Expr::Cast)
1332+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
1333+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
1334+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
1335+ pub struct CastExpr {
1336+ /// The cast kind (e.g., `CAST`, `TRY_CAST`).
1337+ pub kind : CastKind ,
1338+ /// Expression being cast.
1339+ pub expr : Expr ,
1340+ /// Target data type.
1341+ pub data_type : DataType ,
1342+ /// [MySQL] allows CAST(... AS type ARRAY) in functional index definitions for InnoDB
1343+ /// multi-valued indices. It's not really a datatype, and is only allowed in `CAST` in key
1344+ /// specifications, so it's a flag here.
1345+ ///
1346+ /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#function_cast
1347+ pub array : bool ,
1348+ /// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery]
1349+ ///
1350+ /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
1351+ pub format : Option < CastFormat > ,
1352+ }
1353+
1354+ /// A [`CASE` expression](Expr::Case)
1355+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
1356+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
1357+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
1358+ pub struct CaseExpr {
1359+ /// The attached `CASE` token (keeps original spacing/comments).
1360+ pub case_token : AttachedToken ,
1361+ /// The attached `END` token (keeps original spacing/comments).
1362+ pub end_token : AttachedToken ,
1363+ /// Optional operand expression after `CASE` (for simple CASE).
1364+ pub operand : Option < Box < Expr > > ,
1365+ /// The `WHEN ... THEN` conditions and results.
1366+ pub conditions : Vec < CaseWhen > ,
1367+ /// Optional `ELSE` result expression.
1368+ pub else_result : Option < Box < Expr > > ,
1369+ }
1370+
13601371/// The contents inside the `[` and `]` in a subscript expression.
13611372#[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
13621373#[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
@@ -1454,7 +1465,7 @@ pub struct LambdaFunction {
14541465 /// The parameters to the lambda function.
14551466 pub params : OneOrManyWithParens < LambdaFunctionParameter > ,
14561467 /// The body of the lambda function.
1457- pub body : Box < Expr > ,
1468+ pub body : Expr ,
14581469 /// The syntax style used to write the lambda function.
14591470 pub syntax : LambdaSyntax ,
14601471}
@@ -1909,14 +1920,15 @@ impl fmt::Display for Expr {
19091920 write ! ( f, "{op}{expr}" )
19101921 }
19111922 }
1912- Expr :: Convert {
1913- is_try,
1914- expr,
1915- target_before_value,
1916- data_type,
1917- charset,
1918- styles,
1919- } => {
1923+ Expr :: Convert ( convert) => {
1924+ let ConvertExpr {
1925+ is_try,
1926+ expr,
1927+ target_before_value,
1928+ data_type,
1929+ charset,
1930+ styles,
1931+ } = & * * convert;
19201932 write ! ( f, "{}CONVERT(" , if * is_try { "TRY_" } else { "" } ) ?;
19211933 if let Some ( data_type) = data_type {
19221934 if let Some ( charset) = charset {
@@ -1936,40 +1948,43 @@ impl fmt::Display for Expr {
19361948 }
19371949 write ! ( f, ")" )
19381950 }
1939- Expr :: Cast {
1940- kind,
1941- expr,
1942- data_type,
1943- array,
1944- format,
1945- } => match kind {
1946- CastKind :: Cast => {
1947- write ! ( f, "CAST({expr} AS {data_type}" ) ?;
1948- if * array {
1949- write ! ( f, " ARRAY" ) ?;
1951+ Expr :: Cast ( cast) => {
1952+ let CastExpr {
1953+ kind,
1954+ expr,
1955+ data_type,
1956+ array,
1957+ format,
1958+ } = & * * cast;
1959+ match kind {
1960+ CastKind :: Cast => {
1961+ write ! ( f, "CAST({expr} AS {data_type}" ) ?;
1962+ if * array {
1963+ write ! ( f, " ARRAY" ) ?;
1964+ }
1965+ if let Some ( format) = format {
1966+ write ! ( f, " FORMAT {format}" ) ?;
1967+ }
1968+ write ! ( f, ")" )
19501969 }
1951- if let Some ( format) = format {
1952- write ! ( f, " FORMAT {format}" ) ?;
1970+ CastKind :: TryCast => {
1971+ if let Some ( format) = format {
1972+ write ! ( f, "TRY_CAST({expr} AS {data_type} FORMAT {format})" )
1973+ } else {
1974+ write ! ( f, "TRY_CAST({expr} AS {data_type})" )
1975+ }
19531976 }
1954- write ! ( f, ")" )
1955- }
1956- CastKind :: TryCast => {
1957- if let Some ( format) = format {
1958- write ! ( f, "TRY_CAST({expr} AS {data_type} FORMAT {format})" )
1959- } else {
1960- write ! ( f, "TRY_CAST({expr} AS {data_type})" )
1977+ CastKind :: SafeCast => {
1978+ if let Some ( format) = format {
1979+ write ! ( f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})" )
1980+ } else {
1981+ write ! ( f, "SAFE_CAST({expr} AS {data_type})" )
1982+ }
19611983 }
1962- }
1963- CastKind :: SafeCast => {
1964- if let Some ( format) = format {
1965- write ! ( f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})" )
1966- } else {
1967- write ! ( f, "SAFE_CAST({expr} AS {data_type})" )
1984+ CastKind :: DoubleColon => {
1985+ write ! ( f, "{expr}::{data_type}" )
19681986 }
19691987 }
1970- CastKind :: DoubleColon => {
1971- write ! ( f, "{expr}::{data_type}" )
1972- }
19731988 } ,
19741989 Expr :: Extract {
19751990 field,
@@ -2000,13 +2015,14 @@ impl fmt::Display for Expr {
20002015 Expr :: Prefixed { prefix, value } => write ! ( f, "{prefix} {value}" ) ,
20012016 Expr :: TypedString ( ts) => ts. fmt ( f) ,
20022017 Expr :: Function ( fun) => fun. fmt ( f) ,
2003- Expr :: Case {
2004- case_token : _,
2005- end_token : _,
2006- operand,
2007- conditions,
2008- else_result,
2009- } => {
2018+ Expr :: Case ( case) => {
2019+ let CaseExpr {
2020+ case_token : _,
2021+ end_token : _,
2022+ operand,
2023+ conditions,
2024+ else_result,
2025+ } = & * * case;
20102026 f. write_str ( "CASE" ) ?;
20112027 if let Some ( operand) = operand {
20122028 f. write_str ( " " ) ?;
@@ -12343,28 +12359,28 @@ mod tests {
1234312359 #[ test]
1234412360 fn test_interval_display ( ) {
1234512361 let interval = Expr :: Interval ( Interval {
12346- value : Box :: new ( Expr :: Value (
12362+ value : Expr :: Value (
1234712363 Value :: SingleQuotedString ( String :: from ( "123:45.67" ) ) . with_empty_span ( ) ,
12348- ) ) ,
12364+ ) ,
1234912365 leading_field : Some ( DateTimeField :: Minute ) ,
1235012366 leading_precision : Some ( 10 ) ,
1235112367 last_field : Some ( DateTimeField :: Second ) ,
1235212368 fractional_seconds_precision : Some ( 9 ) ,
12353- } ) ;
12369+ } . into ( ) ) ;
1235412370 assert_eq ! (
1235512371 "INTERVAL '123:45.67' MINUTE (10) TO SECOND (9)" ,
1235612372 format!( "{interval}" ) ,
1235712373 ) ;
1235812374
1235912375 let interval = Expr :: Interval ( Interval {
12360- value : Box :: new ( Expr :: Value (
12376+ value : Expr :: Value (
1236112377 Value :: SingleQuotedString ( String :: from ( "5" ) ) . with_empty_span ( ) ,
12362- ) ) ,
12378+ ) ,
1236312379 leading_field : Some ( DateTimeField :: Second ) ,
1236412380 leading_precision : Some ( 1 ) ,
1236512381 last_field : None ,
1236612382 fractional_seconds_precision : Some ( 3 ) ,
12367- } ) ;
12383+ } . into ( ) ) ;
1236812384 assert_eq ! ( "INTERVAL '5' SECOND (1, 3)" , format!( "{interval}" ) ) ;
1236912385 }
1237012386
0 commit comments