diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 1798223f3..35b704215 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -924,6 +924,9 @@ pub enum Expr { syntax: ExtractSyntax, expr: Box, }, + /// In BigQuery, the `DATE_TRUNC` and `DATETIME_TRUNC` functions can be used to truncate a timestamp to a different granularity. + /// e.g. `DATE_TRUNC(CURRENT_DATE(), DAY)` + DateTimeField(DateTimeField), /// ```sql /// CEIL( [TO DateTimeField]) /// ``` @@ -1937,6 +1940,7 @@ impl fmt::Display for Expr { Expr::Prior(expr) => write!(f, "PRIOR {expr}"), Expr::Lambda(lambda) => write!(f, "{lambda}"), Expr::MemberOf(member_of) => write!(f, "{member_of}"), + Expr::DateTimeField(field) => write!(f, "{field}"), } } } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 3e82905e1..04abcf5fb 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1625,6 +1625,7 @@ impl Spanned for Expr { Expr::Prior(expr) => expr.span(), Expr::Lambda(_) => Span::empty(), Expr::MemberOf(member_of) => member_of.value.span().union(&member_of.array.span()), + Expr::DateTimeField(_) => Span::empty(), } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 8d5a55da0..8d1e84504 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -14896,6 +14896,12 @@ impl<'a> Parser<'a> { } pub fn parse_function_args(&mut self) -> Result { + if dialect_of!(self is BigQueryDialect) && self.next_token_is_temporal_unit() { + let unit = self.parse_date_time_field()?; + return Ok(FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::DateTimeField(unit), + ))); + } let arg = if self.dialect.supports_named_fn_args_with_expr_name() { self.maybe_parse(|p| { let name = p.parse_expr()?; diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 2ba54d3e1..e448b9f08 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -2566,3 +2566,101 @@ fn test_struct_trailing_and_nested_bracket() { ) ); } + +#[test] +fn test_datetime_granularity() { + let stmt = bigquery().verified_stmt(concat!( + "SELECT ", + "DATE_TRUNC(CURRENT_DATE, DAY), ", + "DATE_TRUNC(CURRENT_DATE, WEEK(MONDAY)) ", + "FROM my_table", + )); + match stmt { + Statement::Query(query) => { + let body = query.body.as_ref(); + match body { + SetExpr::Select(select) => { + let projection = &select.projection; + assert_eq!( + projection[0], + SelectItem::UnnamedExpr(Expr::Function(Function { + name: ObjectName(vec![ObjectNamePart::Identifier(Ident::new( + "DATE_TRUNC" + ))]), + args: FunctionArguments::List(FunctionArgumentList { + args: vec![ + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function( + Function { + name: ObjectName(vec![ObjectNamePart::Identifier( + Ident::new("CURRENT_DATE") + )]), + args: FunctionArguments::None, + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + filter: None, + null_treatment: None, + over: None, + within_group: vec![], + } + ))), + FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::DateTimeField(DateTimeField::Day) + )), + ], + clauses: vec![], + duplicate_treatment: None, + }), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + filter: None, + null_treatment: None, + over: None, + within_group: vec![], + })) + ); + assert_eq!( + projection[1], + SelectItem::UnnamedExpr(Expr::Function(Function { + name: ObjectName(vec![ObjectNamePart::Identifier(Ident::new( + "DATE_TRUNC" + ))]), + args: FunctionArguments::List(FunctionArgumentList { + args: vec![ + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Function( + Function { + name: ObjectName(vec![ObjectNamePart::Identifier( + Ident::new("CURRENT_DATE") + )]), + args: FunctionArguments::None, + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + filter: None, + null_treatment: None, + over: None, + within_group: vec![], + } + ))), + FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::DateTimeField(DateTimeField::Week(Some(Ident::new( + "MONDAY" + )))) + )), + ], + clauses: vec![], + duplicate_treatment: None, + }), + uses_odbc_syntax: false, + parameters: FunctionArguments::None, + filter: None, + null_treatment: None, + over: None, + within_group: vec![], + })) + ); + } + _ => panic!("Expected a select statement"), + } + } + _ => panic!("Expected a select statement"), + } +}