From 46489511ba8ce51a6b08c831818431fb42efec64 Mon Sep 17 00:00:00 2001 From: Seve Martinez Date: Tue, 13 Aug 2024 07:17:57 -0700 Subject: [PATCH 1/3] rebasing to main --- src/ast/mod.rs | 41 +++++++++++++++++++++++++++----------- src/parser/mod.rs | 20 ++++++++++++++----- tests/sqlparser_common.rs | 42 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 86e2592a3..d08b2c546 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -493,6 +493,17 @@ pub enum ExtractSyntax { Comma, } +/// The syntax used in a CEIL or FLOOR expression. +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum CeilFloorKind { + /// `CEIL( TO )` + DateTimeField(DateTimeField), + /// `CEIL( [, ])` + Scale(Value), +} + /// An SQL expression of any type. /// /// The parser does not distinguish between expressions of different types @@ -667,16 +678,22 @@ pub enum Expr { /// ```sql /// CEIL( [TO DateTimeField]) /// ``` + /// ```sql + /// CEIL( [, ] ) + /// ``` Ceil { expr: Box, - field: DateTimeField, + field: CeilFloorKind, }, /// ```sql /// FLOOR( [TO DateTimeField]) /// ``` + /// ```sql + /// FLOOR( [, ] ) + /// Floor { expr: Box, - field: DateTimeField, + field: CeilFloorKind, }, /// ```sql /// POSITION( in ) @@ -1223,20 +1240,20 @@ impl fmt::Display for Expr { ExtractSyntax::From => write!(f, "EXTRACT({field} FROM {expr})"), ExtractSyntax::Comma => write!(f, "EXTRACT({field}, {expr})"), }, - Expr::Ceil { expr, field } => { - if field == &DateTimeField::NoDateTime { + Expr::Ceil { expr, field } => match field { + CeilFloorKind::DateTimeField(DateTimeField::NoDateTime) => { write!(f, "CEIL({expr})") - } else { - write!(f, "CEIL({expr} TO {field})") } - } - Expr::Floor { expr, field } => { - if field == &DateTimeField::NoDateTime { + CeilFloorKind::DateTimeField(dt_field) => write!(f, "CEIL({expr} TO {dt_field})"), + CeilFloorKind::Scale(s) => write!(f, "CEIL({expr}, {s})"), + }, + Expr::Floor { expr, field } => match field { + CeilFloorKind::DateTimeField(DateTimeField::NoDateTime) => { write!(f, "FLOOR({expr})") - } else { - write!(f, "FLOOR({expr} TO {field})") } - } + CeilFloorKind::DateTimeField(dt_field) => write!(f, "FLOOR({expr} TO {dt_field})"), + CeilFloorKind::Scale(s) => write!(f, "FLOOR({expr}, {s})"), + }, Expr::Position { expr, r#in } => write!(f, "POSITION({expr} IN {in})"), Expr::Collate { expr, collation } => write!(f, "{expr} COLLATE {collation}"), Expr::Nested(ast) => write!(f, "({ast})"), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 60a7b4d0b..cb202a93e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1708,12 +1708,22 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; // Parse `CEIL/FLOOR(expr)` - let mut field = DateTimeField::NoDateTime; - let keyword_to = self.parse_keyword(Keyword::TO); - if keyword_to { + let field = if self.parse_keyword(Keyword::TO) { // Parse `CEIL/FLOOR(expr TO DateTimeField)` - field = self.parse_date_time_field()?; - } + CeilFloorKind::DateTimeField(self.parse_date_time_field()?) + } else if self.consume_token(&Token::Comma) { + // Parse `CEIL/FLOOR(expr, scale)` + match self.parse_value()? { + Value::Number(n, s) => CeilFloorKind::Scale(Value::Number(n, s)), + _ => { + return Err(ParserError::ParserError( + "Scale field can only be of number type".to_string(), + )) + } + } + } else { + CeilFloorKind::DateTimeField(DateTimeField::NoDateTime) + }; self.expect_token(&Token::RParen)?; if is_ceil { Ok(Expr::Ceil { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 293269cdd..ee190d904 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2494,6 +2494,44 @@ fn parse_floor_number() { verified_stmt("SELECT FLOOR(float_column) FROM my_table"); } +#[test] +fn parse_ceil_number_scale() { + verified_stmt("SELECT CEIL(1.5, 1)"); + verified_stmt("SELECT CEIL(float_column, 3) FROM my_table"); +} + +#[test] +fn parse_floor_number_scale() { + verified_stmt("SELECT FLOOR(1.5, 1)"); + verified_stmt("SELECT FLOOR(float_column, 3) FROM my_table"); +} + +#[test] +fn parse_ceil_scale() { + let sql = "SELECT CEIL(d, 2)"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Ceil { + expr: Box::new(Expr::Identifier(Ident::new("d"))), + field: CeilFloorKind::Scale(Value::Number(2.to_string(), false)), + }, + expr_from_projection(only(&select.projection)), + ); +} + +#[test] +fn parse_floor_scale() { + let sql = "SELECT FLOOR(d, 2)"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Floor { + expr: Box::new(Expr::Identifier(Ident::new("d"))), + field: CeilFloorKind::Scale(Value::Number(2.to_string(), false)), + }, + expr_from_projection(only(&select.projection)), + ); +} + #[test] fn parse_ceil_datetime() { let sql = "SELECT CEIL(d TO DAY)"; @@ -2501,7 +2539,7 @@ fn parse_ceil_datetime() { assert_eq!( &Expr::Ceil { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: DateTimeField::Day, + field: CeilFloorKind::DateTimeField(DateTimeField::Day), }, expr_from_projection(only(&select.projection)), ); @@ -2528,7 +2566,7 @@ fn parse_floor_datetime() { assert_eq!( &Expr::Floor { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: DateTimeField::Day, + field: CeilFloorKind::DateTimeField(DateTimeField::Day), }, expr_from_projection(only(&select.projection)), ); From 471b5f4325913e9e2feba7ad708594e5802deb18 Mon Sep 17 00:00:00 2001 From: Seve Martinez Date: Tue, 13 Aug 2024 07:22:42 -0700 Subject: [PATCH 2/3] adding kinesis documentation for ceil/floor syntax --- src/ast/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index d08b2c546..46faa82e5 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -494,6 +494,13 @@ pub enum ExtractSyntax { } /// The syntax used in a CEIL or FLOOR expression. +/// +/// The `CEIL/FLOOR( TO