Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4676,6 +4676,19 @@ pub enum Statement {
/// Additional `WITH` options for RAISERROR.
options: Vec<RaisErrorOption>,
},
/// Throw (MSSQL)
/// ```sql
/// THROW [ error_number, message, state ]
/// ```
/// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/throw-transact-sql>
Throw {
/// Error number expression.
error_number: Option<Box<Expr>>,
/// Error message expression.
message: Option<Box<Expr>>,
/// State expression.
state: Option<Box<Expr>>,
},
/// ```sql
/// PRINT msg_str | @local_variable | string_expr
/// ```
Expand Down Expand Up @@ -6120,6 +6133,19 @@ impl fmt::Display for Statement {
}
Ok(())
}
Statement::Throw {
error_number,
message,
state,
} => {
write!(f, "THROW")?;
if let (Some(error_number), Some(message), Some(state)) =
(error_number, message, state)
{
write!(f, " {error_number}, {message}, {state}")?;
}
Ok(())
}
Statement::Print(s) => write!(f, "{s}"),
Statement::Return(r) => write!(f, "{r}"),
Statement::List(command) => write!(f, "LIST {command}"),
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ impl Spanned for Statement {
Statement::UNLISTEN { .. } => Span::empty(),
Statement::RenameTable { .. } => Span::empty(),
Statement::RaisError { .. } => Span::empty(),
Statement::Throw { .. } => Span::empty(),
Statement::Print { .. } => Span::empty(),
Statement::Return { .. } => Span::empty(),
Statement::List(..) | Statement::Remove(..) => Span::empty(),
Expand Down
1 change: 1 addition & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,7 @@ define_keywords!(
TEXT,
TEXTFILE,
THEN,
THROW,
TIES,
TIME,
TIMEFORMAT,
Expand Down
26 changes: 26 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ impl<'a> Parser<'a> {
Keyword::RELEASE => self.parse_release(),
Keyword::COMMIT => self.parse_commit(),
Keyword::RAISERROR => Ok(self.parse_raiserror()?),
Keyword::THROW => Ok(self.parse_throw()?),
Keyword::ROLLBACK => self.parse_rollback(),
Keyword::ASSERT => self.parse_assert(),
// `PREPARE`, `EXECUTE` and `DEALLOCATE` are Postgres-specific
Expand Down Expand Up @@ -18260,6 +18261,31 @@ impl<'a> Parser<'a> {
}
}

/// Parse a MSSQL `THROW` statement
/// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/throw-transact-sql>
pub fn parse_throw(&mut self) -> Result<Statement, ParserError> {
// THROW with no arguments is a re-throw inside a CATCH block
if self.peek_token_ref().token == Token::SemiColon
|| self.peek_token_ref().token == Token::EOF
{
return Ok(Statement::Throw {
error_number: None,
message: None,
state: None,
});
}
let error_number = Box::new(self.parse_expr()?);
self.expect_token(&Token::Comma)?;
let message = Box::new(self.parse_expr()?);
self.expect_token(&Token::Comma)?;
let state = Box::new(self.parse_expr()?);
Ok(Statement::Throw {
error_number: Some(error_number),
message: Some(message),
state: Some(state),
})
}

/// Parse a SQL `DEALLOCATE` statement
pub fn parse_deallocate(&mut self) -> Result<Statement, ParserError> {
let prepare = self.parse_keyword(Keyword::PREPARE);
Expand Down
37 changes: 37 additions & 0 deletions tests/sqlparser_mssql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,43 @@ fn test_parse_raiserror() {
let _ = ms().verified_stmt(sql);
}

#[test]
fn test_parse_throw() {
// THROW with arguments
let sql = r#"THROW 51000, 'Record does not exist.', 1"#;
let s = ms().verified_stmt(sql);
assert_eq!(
s,
Statement::Throw {
error_number: Some(Box::new(Expr::Value(
(Value::Number("51000".parse().unwrap(), false)).with_empty_span()
))),
message: Some(Box::new(Expr::Value(
(Value::SingleQuotedString("Record does not exist.".to_string())).with_empty_span()
))),
state: Some(Box::new(Expr::Value(
(Value::Number("1".parse().unwrap(), false)).with_empty_span()
))),
}
);

// THROW with variable references
let sql = r#"THROW @ErrorNumber, @ErrorMessage, @ErrorState"#;
let _ = ms().verified_stmt(sql);

// Re-throw (no arguments)
let sql = r#"THROW"#;
let s = ms().verified_stmt(sql);
assert_eq!(
s,
Statement::Throw {
error_number: None,
message: None,
state: None,
}
);
}

#[test]
fn parse_use() {
let valid_object_names = [
Expand Down