Skip to content

Commit

Permalink
Add support for parsing RAISERROR (#1656)
Browse files Browse the repository at this point in the history
  • Loading branch information
AvivDavid-Satori authored Jan 16, 2025
1 parent 9105cae commit 4741500
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 0 deletions.
50 changes: 50 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3441,6 +3441,38 @@ pub enum Statement {
///
/// See <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
SetSessionParam(SetSessionParamKind),
/// RaiseError (MSSQL)
/// RAISERROR ( { msg_id | msg_str | @local_variable }
/// { , severity , state }
/// [ , argument [ , ...n ] ] )
/// [ WITH option [ , ...n ] ]
/// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql?view=sql-server-ver16>
RaisError {
message: Box<Expr>,
severity: Box<Expr>,
state: Box<Expr>,
arguments: Vec<Expr>,
options: Vec<RaisErrorOption>,
},
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum RaisErrorOption {
Log,
NoWait,
SetError,
}

impl fmt::Display for RaisErrorOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RaisErrorOption::Log => write!(f, "LOG"),
RaisErrorOption::NoWait => write!(f, "NOWAIT"),
RaisErrorOption::SetError => write!(f, "SETERROR"),
}
}
}

impl fmt::Display for Statement {
Expand Down Expand Up @@ -5026,6 +5058,24 @@ impl fmt::Display for Statement {
Statement::RenameTable(rename_tables) => {
write!(f, "RENAME TABLE {}", display_comma_separated(rename_tables))
}
Statement::RaisError {
message,
severity,
state,
arguments,
options,
} => {
write!(f, "RAISERROR({message}, {severity}, {state}")?;
if !arguments.is_empty() {
write!(f, ", {}", display_comma_separated(arguments))?;
}
write!(f, ")")?;
if !options.is_empty() {
write!(f, " WITH {}", display_comma_separated(options))?;
}
Ok(())
}

Statement::List(command) => write!(f, "LIST {command}"),
Statement::Remove(command) => write!(f, "REMOVE {command}"),
Statement::SetSessionParam(kind) => write!(f, "SET {kind}"),
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ impl Spanned for Statement {
Statement::LoadData { .. } => Span::empty(),
Statement::UNLISTEN { .. } => Span::empty(),
Statement::RenameTable { .. } => Span::empty(),
Statement::RaisError { .. } => Span::empty(),
Statement::List(..) | Statement::Remove(..) => Span::empty(),
Statement::SetSessionParam { .. } => Span::empty(),
}
Expand Down
3 changes: 3 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ define_keywords!(
LOCATION,
LOCK,
LOCKED,
LOG,
LOGIN,
LOGS,
LONGBLOB,
Expand Down Expand Up @@ -636,6 +637,7 @@ define_keywords!(
QUARTER,
QUERY,
QUOTE,
RAISERROR,
RANGE,
RANK,
RAW,
Expand Down Expand Up @@ -728,6 +730,7 @@ define_keywords!(
SESSION,
SESSION_USER,
SET,
SETERROR,
SETS,
SETTINGS,
SHARE,
Expand Down
41 changes: 41 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ impl<'a> Parser<'a> {
Keyword::SAVEPOINT => self.parse_savepoint(),
Keyword::RELEASE => self.parse_release(),
Keyword::COMMIT => self.parse_commit(),
Keyword::RAISERROR => Ok(self.parse_raiserror()?),
Keyword::ROLLBACK => self.parse_rollback(),
Keyword::ASSERT => self.parse_assert(),
// `PREPARE`, `EXECUTE` and `DEALLOCATE` are Postgres-specific
Expand Down Expand Up @@ -13150,6 +13151,46 @@ impl<'a> Parser<'a> {
}
}

/// Parse a 'RAISERROR' statement
pub fn parse_raiserror(&mut self) -> Result<Statement, ParserError> {
self.expect_token(&Token::LParen)?;
let message = Box::new(self.parse_expr()?);
self.expect_token(&Token::Comma)?;
let severity = Box::new(self.parse_expr()?);
self.expect_token(&Token::Comma)?;
let state = Box::new(self.parse_expr()?);
let arguments = if self.consume_token(&Token::Comma) {
self.parse_comma_separated(Parser::parse_expr)?
} else {
vec![]
};
self.expect_token(&Token::RParen)?;
let options = if self.parse_keyword(Keyword::WITH) {
self.parse_comma_separated(Parser::parse_raiserror_option)?
} else {
vec![]
};
Ok(Statement::RaisError {
message,
severity,
state,
arguments,
options,
})
}

pub fn parse_raiserror_option(&mut self) -> Result<RaisErrorOption, ParserError> {
match self.expect_one_of_keywords(&[Keyword::LOG, Keyword::NOWAIT, Keyword::SETERROR])? {
Keyword::LOG => Ok(RaisErrorOption::Log),
Keyword::NOWAIT => Ok(RaisErrorOption::NoWait),
Keyword::SETERROR => Ok(RaisErrorOption::SetError),
_ => self.expected(
"LOG, NOWAIT OR SETERROR raiserror option",
self.peek_token(),
),
}
}

pub fn parse_deallocate(&mut self) -> Result<Statement, ParserError> {
let prepare = self.parse_keyword(Keyword::PREPARE);
let name = self.parse_identifier()?;
Expand Down
33 changes: 33 additions & 0 deletions tests/sqlparser_mssql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,39 @@ fn parse_mssql_declare() {
);
}

#[test]
fn test_parse_raiserror() {
let sql = r#"RAISERROR('This is a test', 16, 1)"#;
let s = ms().verified_stmt(sql);
assert_eq!(
s,
Statement::RaisError {
message: Box::new(Expr::Value(Value::SingleQuotedString(
"This is a test".to_string()
))),
severity: Box::new(Expr::Value(Value::Number("16".parse().unwrap(), false))),
state: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), false))),
arguments: vec![],
options: vec![],
}
);

let sql = r#"RAISERROR('This is a test', 16, 1) WITH NOWAIT"#;
let _ = ms().verified_stmt(sql);

let sql = r#"RAISERROR('This is a test', 16, 1, 'ARG') WITH SETERROR, LOG"#;
let _ = ms().verified_stmt(sql);

let sql = r#"RAISERROR(N'This is message %s %d.', 10, 1, N'number', 5)"#;
let _ = ms().verified_stmt(sql);

let sql = r#"RAISERROR(N'<<%*.*s>>', 10, 1, 7, 3, N'abcde')"#;
let _ = ms().verified_stmt(sql);

let sql = r#"RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState)"#;
let _ = ms().verified_stmt(sql);
}

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

0 comments on commit 4741500

Please sign in to comment.