diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 010a8189b..a80289e32 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4686,6 +4686,10 @@ pub enum Statement { /// /// See: Print(PrintStatement), + /// MSSQL `WAITFOR` statement. + /// + /// See: + WaitFor(WaitForStatement), /// ```sql /// RETURN [ expression ] /// ``` @@ -6125,6 +6129,7 @@ impl fmt::Display for Statement { Ok(()) } 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}"), @@ -10868,6 +10873,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 bdd430e7a..3c44471ed 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -482,6 +482,7 @@ impl Spanned for Statement { Statement::RenameTable { .. } => Span::empty(), Statement::RaisError { .. } => 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/dialect/mssql.rs b/src/dialect/mssql.rs index e763165d5..f09fb1cdd 100644 --- a/src/dialect/mssql.rs +++ b/src/dialect/mssql.rs @@ -18,7 +18,7 @@ use crate::ast::helpers::attached_token::AttachedToken; use crate::ast::{ BeginEndStatements, ConditionalStatementBlock, ConditionalStatements, CreateTrigger, - GranteesType, IfStatement, Statement, + GranteesType, IfStatement, Statement, WaitForStatement, WaitForType, }; use crate::dialect::Dialect; use crate::keywords::Keyword; @@ -179,6 +179,8 @@ impl Dialect for MsSqlDialect { Keyword::TRIGGER, ]) { Some(self.parse_create_trigger(parser, true)) + } else if parser.parse_keyword(Keyword::WAITFOR) { + Some(self.parse_waitfor(parser)) } else { None } @@ -343,4 +345,19 @@ impl MsSqlDialect { } Ok(stmts) } + + /// Parse `WAITFOR { DELAY 'time' | TIME 'time' }` + /// + /// See: + fn parse_waitfor(&self, parser: &mut Parser) -> Result { + let wait_type = if parser.parse_keyword(Keyword::DELAY) { + WaitForType::Delay + } else if parser.parse_keyword(Keyword::TIME) { + WaitForType::Time + } else { + return parser.expected("DELAY or TIME", parser.peek_token()); + }; + let expr = parser.parse_expr()?; + Ok(Statement::WaitFor(WaitForStatement { wait_type, expr })) + } } diff --git a/src/keywords.rs b/src/keywords.rs index f84f4d213..d6e763dc0 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -312,6 +312,7 @@ define_keywords!( DEFINE, DEFINED, DEFINER, + DELAY, DELAYED, DELAY_KEY_WRITE, DELEGATED, @@ -1136,6 +1137,7 @@ define_keywords!( VIRTUAL, VOLATILE, VOLUME, + WAITFOR, WAREHOUSE, WAREHOUSES, WEEK, diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 84b8658b0..263ce78ba 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -1665,6 +1665,43 @@ fn test_parse_raiserror() { let _ = ms().verified_stmt(sql); } +#[test] +fn test_parse_waitfor() { + // WAITFOR DELAY + let sql = "WAITFOR DELAY '00:00:05'"; + let stmt = ms().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().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().verified_stmt(sql); + + // Error: WAITFOR without DELAY or TIME + let res = ms().parse_sql_statements("WAITFOR '00:00:05'"); + assert!(res.is_err()); +} + #[test] fn parse_use() { let valid_object_names = [