diff --git a/src/ast/mod.rs b/src/ast/mod.rs index eda282260..dbf5003cc 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4764,6 +4764,10 @@ pub enum Statement { /// /// See: Print(PrintStatement), + /// MSSQL `WAITFOR` statement. + /// + /// See: + WaitFor(WaitForStatement), /// ```sql /// RETURN [ expression ] /// ``` @@ -6217,6 +6221,7 @@ impl fmt::Display for Statement { } Statement::Throw(s) => write!(f, "{s}"), Statement::Print(s) => write!(f, "{s}"), + Statement::WaitFor(s) => write!(f, "{s}"), Statement::Return(r) => write!(f, "{r}"), Statement::List(command) => write!(f, "LIST {command}"), Statement::Remove(command) => write!(f, "REMOVE {command}"), @@ -10964,6 +10969,47 @@ impl fmt::Display for PrintStatement { } } +/// The type of `WAITFOR` statement (MSSQL). +/// +/// See: +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum WaitForType { + /// `WAITFOR DELAY 'time_to_pass'` + Delay, + /// `WAITFOR TIME 'time_to_execute'` + Time, +} + +impl fmt::Display for WaitForType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + WaitForType::Delay => write!(f, "DELAY"), + WaitForType::Time => write!(f, "TIME"), + } + } +} + +/// MSSQL `WAITFOR` statement. +/// +/// See: +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct WaitForStatement { + /// `DELAY` or `TIME`. + pub wait_type: WaitForType, + /// The time expression. + pub expr: Expr, +} + +impl fmt::Display for WaitForStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "WAITFOR {} {}", self.wait_type, self.expr) + } +} + /// Represents a `Return` statement. /// /// [MsSql triggers](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index f4bdf85a3..d792c13ca 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -483,6 +483,7 @@ impl Spanned for Statement { Statement::RaisError { .. } => Span::empty(), Statement::Throw(_) => Span::empty(), Statement::Print { .. } => Span::empty(), + Statement::WaitFor(_) => Span::empty(), Statement::Return { .. } => Span::empty(), Statement::List(..) | Statement::Remove(..) => Span::empty(), Statement::ExportData(ExportData { diff --git a/src/keywords.rs b/src/keywords.rs index f1dbcd937..cc2b9e9dd 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -312,6 +312,7 @@ define_keywords!( DEFINE, DEFINED, DEFINER, + DELAY, DELAYED, DELAY_KEY_WRITE, DELEGATED, @@ -1138,6 +1139,7 @@ define_keywords!( VIRTUAL, VOLATILE, VOLUME, + WAITFOR, WAREHOUSE, WAREHOUSES, WEEK, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index eba9b32dc..4f32422f8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -703,6 +703,8 @@ impl<'a> Parser<'a> { // `COMMENT` is snowflake specific https://docs.snowflake.com/en/sql-reference/sql/comment Keyword::COMMENT if self.dialect.supports_comment_on() => self.parse_comment(), Keyword::PRINT => self.parse_print(), + // `WAITFOR` is MSSQL specific https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql + Keyword::WAITFOR => self.parse_waitfor(), Keyword::RETURN => self.parse_return(), Keyword::EXPORT => { self.prev_token(); @@ -19289,6 +19291,21 @@ impl<'a> Parser<'a> { })) } + /// Parse [Statement::WaitFor] + /// + /// See: + fn parse_waitfor(&mut self) -> Result { + let wait_type = if self.parse_keyword(Keyword::DELAY) { + WaitForType::Delay + } else if self.parse_keyword(Keyword::TIME) { + WaitForType::Time + } else { + return self.expected("DELAY or TIME", self.peek_token()); + }; + let expr = self.parse_expr()?; + Ok(Statement::WaitFor(WaitForStatement { wait_type, expr })) + } + /// Parse [Statement::Return] fn parse_return(&mut self) -> Result { match self.maybe_parse(|p| p.parse_expr())? { diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index cf9ae8980..d7d11ba66 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -1702,6 +1702,43 @@ fn test_parse_throw() { ); } +#[test] +fn test_parse_waitfor() { + // WAITFOR DELAY + let sql = "WAITFOR DELAY '00:00:05'"; + let stmt = ms_and_generic().verified_stmt(sql); + assert_eq!( + stmt, + Statement::WaitFor(WaitForStatement { + wait_type: WaitForType::Delay, + expr: Expr::Value( + (Value::SingleQuotedString("00:00:05".to_string())).with_empty_span() + ), + }) + ); + + // WAITFOR TIME + let sql = "WAITFOR TIME '14:30:00'"; + let stmt = ms_and_generic().verified_stmt(sql); + assert_eq!( + stmt, + Statement::WaitFor(WaitForStatement { + wait_type: WaitForType::Time, + expr: Expr::Value( + (Value::SingleQuotedString("14:30:00".to_string())).with_empty_span() + ), + }) + ); + + // WAITFOR DELAY with variable + let sql = "WAITFOR DELAY @WaitTime"; + let _ = ms_and_generic().verified_stmt(sql); + + // Error: WAITFOR without DELAY or TIME + let res = ms_and_generic().parse_sql_statements("WAITFOR '00:00:05'"); + assert!(res.is_err()); +} + #[test] fn parse_use() { let valid_object_names = [