Skip to content

Commit 4741500

Browse files
Add support for parsing RAISERROR (#1656)
1 parent 9105cae commit 4741500

File tree

5 files changed

+128
-0
lines changed

5 files changed

+128
-0
lines changed

src/ast/mod.rs

+50
Original file line numberDiff line numberDiff line change
@@ -3441,6 +3441,38 @@ pub enum Statement {
34413441
///
34423442
/// See <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
34433443
SetSessionParam(SetSessionParamKind),
3444+
/// RaiseError (MSSQL)
3445+
/// RAISERROR ( { msg_id | msg_str | @local_variable }
3446+
/// { , severity , state }
3447+
/// [ , argument [ , ...n ] ] )
3448+
/// [ WITH option [ , ...n ] ]
3449+
/// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql?view=sql-server-ver16>
3450+
RaisError {
3451+
message: Box<Expr>,
3452+
severity: Box<Expr>,
3453+
state: Box<Expr>,
3454+
arguments: Vec<Expr>,
3455+
options: Vec<RaisErrorOption>,
3456+
},
3457+
}
3458+
3459+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
3460+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3461+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
3462+
pub enum RaisErrorOption {
3463+
Log,
3464+
NoWait,
3465+
SetError,
3466+
}
3467+
3468+
impl fmt::Display for RaisErrorOption {
3469+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3470+
match self {
3471+
RaisErrorOption::Log => write!(f, "LOG"),
3472+
RaisErrorOption::NoWait => write!(f, "NOWAIT"),
3473+
RaisErrorOption::SetError => write!(f, "SETERROR"),
3474+
}
3475+
}
34443476
}
34453477

34463478
impl fmt::Display for Statement {
@@ -5026,6 +5058,24 @@ impl fmt::Display for Statement {
50265058
Statement::RenameTable(rename_tables) => {
50275059
write!(f, "RENAME TABLE {}", display_comma_separated(rename_tables))
50285060
}
5061+
Statement::RaisError {
5062+
message,
5063+
severity,
5064+
state,
5065+
arguments,
5066+
options,
5067+
} => {
5068+
write!(f, "RAISERROR({message}, {severity}, {state}")?;
5069+
if !arguments.is_empty() {
5070+
write!(f, ", {}", display_comma_separated(arguments))?;
5071+
}
5072+
write!(f, ")")?;
5073+
if !options.is_empty() {
5074+
write!(f, " WITH {}", display_comma_separated(options))?;
5075+
}
5076+
Ok(())
5077+
}
5078+
50295079
Statement::List(command) => write!(f, "LIST {command}"),
50305080
Statement::Remove(command) => write!(f, "REMOVE {command}"),
50315081
Statement::SetSessionParam(kind) => write!(f, "SET {kind}"),

src/ast/spans.rs

+1
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ impl Spanned for Statement {
495495
Statement::LoadData { .. } => Span::empty(),
496496
Statement::UNLISTEN { .. } => Span::empty(),
497497
Statement::RenameTable { .. } => Span::empty(),
498+
Statement::RaisError { .. } => Span::empty(),
498499
Statement::List(..) | Statement::Remove(..) => Span::empty(),
499500
Statement::SetSessionParam { .. } => Span::empty(),
500501
}

src/keywords.rs

+3
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ define_keywords!(
468468
LOCATION,
469469
LOCK,
470470
LOCKED,
471+
LOG,
471472
LOGIN,
472473
LOGS,
473474
LONGBLOB,
@@ -636,6 +637,7 @@ define_keywords!(
636637
QUARTER,
637638
QUERY,
638639
QUOTE,
640+
RAISERROR,
639641
RANGE,
640642
RANK,
641643
RAW,
@@ -728,6 +730,7 @@ define_keywords!(
728730
SESSION,
729731
SESSION_USER,
730732
SET,
733+
SETERROR,
731734
SETS,
732735
SETTINGS,
733736
SHARE,

src/parser/mod.rs

+41
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ impl<'a> Parser<'a> {
579579
Keyword::SAVEPOINT => self.parse_savepoint(),
580580
Keyword::RELEASE => self.parse_release(),
581581
Keyword::COMMIT => self.parse_commit(),
582+
Keyword::RAISERROR => Ok(self.parse_raiserror()?),
582583
Keyword::ROLLBACK => self.parse_rollback(),
583584
Keyword::ASSERT => self.parse_assert(),
584585
// `PREPARE`, `EXECUTE` and `DEALLOCATE` are Postgres-specific
@@ -13150,6 +13151,46 @@ impl<'a> Parser<'a> {
1315013151
}
1315113152
}
1315213153

13154+
/// Parse a 'RAISERROR' statement
13155+
pub fn parse_raiserror(&mut self) -> Result<Statement, ParserError> {
13156+
self.expect_token(&Token::LParen)?;
13157+
let message = Box::new(self.parse_expr()?);
13158+
self.expect_token(&Token::Comma)?;
13159+
let severity = Box::new(self.parse_expr()?);
13160+
self.expect_token(&Token::Comma)?;
13161+
let state = Box::new(self.parse_expr()?);
13162+
let arguments = if self.consume_token(&Token::Comma) {
13163+
self.parse_comma_separated(Parser::parse_expr)?
13164+
} else {
13165+
vec![]
13166+
};
13167+
self.expect_token(&Token::RParen)?;
13168+
let options = if self.parse_keyword(Keyword::WITH) {
13169+
self.parse_comma_separated(Parser::parse_raiserror_option)?
13170+
} else {
13171+
vec![]
13172+
};
13173+
Ok(Statement::RaisError {
13174+
message,
13175+
severity,
13176+
state,
13177+
arguments,
13178+
options,
13179+
})
13180+
}
13181+
13182+
pub fn parse_raiserror_option(&mut self) -> Result<RaisErrorOption, ParserError> {
13183+
match self.expect_one_of_keywords(&[Keyword::LOG, Keyword::NOWAIT, Keyword::SETERROR])? {
13184+
Keyword::LOG => Ok(RaisErrorOption::Log),
13185+
Keyword::NOWAIT => Ok(RaisErrorOption::NoWait),
13186+
Keyword::SETERROR => Ok(RaisErrorOption::SetError),
13187+
_ => self.expected(
13188+
"LOG, NOWAIT OR SETERROR raiserror option",
13189+
self.peek_token(),
13190+
),
13191+
}
13192+
}
13193+
1315313194
pub fn parse_deallocate(&mut self) -> Result<Statement, ParserError> {
1315413195
let prepare = self.parse_keyword(Keyword::PREPARE);
1315513196
let name = self.parse_identifier()?;

tests/sqlparser_mssql.rs

+33
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,39 @@ fn parse_mssql_declare() {
12501250
);
12511251
}
12521252

1253+
#[test]
1254+
fn test_parse_raiserror() {
1255+
let sql = r#"RAISERROR('This is a test', 16, 1)"#;
1256+
let s = ms().verified_stmt(sql);
1257+
assert_eq!(
1258+
s,
1259+
Statement::RaisError {
1260+
message: Box::new(Expr::Value(Value::SingleQuotedString(
1261+
"This is a test".to_string()
1262+
))),
1263+
severity: Box::new(Expr::Value(Value::Number("16".parse().unwrap(), false))),
1264+
state: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), false))),
1265+
arguments: vec![],
1266+
options: vec![],
1267+
}
1268+
);
1269+
1270+
let sql = r#"RAISERROR('This is a test', 16, 1) WITH NOWAIT"#;
1271+
let _ = ms().verified_stmt(sql);
1272+
1273+
let sql = r#"RAISERROR('This is a test', 16, 1, 'ARG') WITH SETERROR, LOG"#;
1274+
let _ = ms().verified_stmt(sql);
1275+
1276+
let sql = r#"RAISERROR(N'This is message %s %d.', 10, 1, N'number', 5)"#;
1277+
let _ = ms().verified_stmt(sql);
1278+
1279+
let sql = r#"RAISERROR(N'<<%*.*s>>', 10, 1, 7, 3, N'abcde')"#;
1280+
let _ = ms().verified_stmt(sql);
1281+
1282+
let sql = r#"RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState)"#;
1283+
let _ = ms().verified_stmt(sql);
1284+
}
1285+
12531286
#[test]
12541287
fn parse_use() {
12551288
let valid_object_names = [

0 commit comments

Comments
 (0)