Skip to content

Commit 5ec1463

Browse files
committed
Add support for parsing WHILE statements
- it's a conditional block alongside IF & CASE
1 parent f1e8ac7 commit 5ec1463

File tree

5 files changed

+116
-6
lines changed

5 files changed

+116
-6
lines changed

src/ast/mod.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2226,7 +2226,33 @@ impl fmt::Display for IfStatement {
22262226
}
22272227
}
22282228

2229-
/// A block within a [Statement::Case] or [Statement::If]-like statement
2229+
/// A `WHILE` statement.
2230+
///
2231+
/// Example:
2232+
/// ```sql
2233+
/// WHILE @@FETCH_STATUS = 0
2234+
/// BEGIN
2235+
/// FETCH NEXT FROM c1 INTO @var1, @var2;
2236+
/// END
2237+
/// ```
2238+
///
2239+
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/while-transact-sql)
2240+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2241+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2242+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2243+
pub struct WhileStatement {
2244+
pub while_block: ConditionalStatementBlock,
2245+
}
2246+
2247+
impl fmt::Display for WhileStatement {
2248+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2249+
let WhileStatement { while_block } = self;
2250+
write!(f, "{while_block}")?;
2251+
Ok(())
2252+
}
2253+
}
2254+
2255+
/// A block within a [Statement::Case] or [Statement::If] or [Statement::While]-like statement
22302256
///
22312257
/// Example 1:
22322258
/// ```sql
@@ -2242,6 +2268,14 @@ impl fmt::Display for IfStatement {
22422268
/// ```sql
22432269
/// ELSE SELECT 1; SELECT 2;
22442270
/// ```
2271+
///
2272+
/// Example 4:
2273+
/// ```sql
2274+
/// WHILE @@FETCH_STATUS = 0
2275+
/// BEGIN
2276+
/// FETCH NEXT FROM c1 INTO @var1, @var2;
2277+
/// END
2278+
/// ```
22452279
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
22462280
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22472281
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
@@ -2981,6 +3015,8 @@ pub enum Statement {
29813015
Case(CaseStatement),
29823016
/// An `IF` statement.
29833017
If(IfStatement),
3018+
/// A `WHILE` statement.
3019+
While(WhileStatement),
29843020
/// A `RAISE` statement.
29853021
Raise(RaiseStatement),
29863022
/// ```sql
@@ -4345,6 +4381,9 @@ impl fmt::Display for Statement {
43454381
Statement::If(stmt) => {
43464382
write!(f, "{stmt}")
43474383
}
4384+
Statement::While(stmt) => {
4385+
write!(f, "{stmt}")
4386+
}
43484387
Statement::Raise(stmt) => {
43494388
write!(f, "{stmt}")
43504389
}

src/ast/spans.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use super::{
3636
ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select,
3737
SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias,
3838
TableAliasColumnDef, TableConstraint, TableFactor, TableObject, TableOptionsClustered,
39-
TableWithJoins, UpdateTableFromKind, Use, Value, Values, ViewColumnDef,
39+
TableWithJoins, UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement,
4040
WildcardAdditionalOptions, With, WithFill,
4141
};
4242

@@ -338,6 +338,7 @@ impl Spanned for Statement {
338338
} => source.span(),
339339
Statement::Case(stmt) => stmt.span(),
340340
Statement::If(stmt) => stmt.span(),
341+
Statement::While(stmt) => stmt.span(),
341342
Statement::Raise(stmt) => stmt.span(),
342343
Statement::Call(function) => function.span(),
343344
Statement::Copy {
@@ -775,6 +776,14 @@ impl Spanned for IfStatement {
775776
}
776777
}
777778

779+
impl Spanned for WhileStatement {
780+
fn span(&self) -> Span {
781+
let WhileStatement { while_block } = self;
782+
783+
while_block.span()
784+
}
785+
}
786+
778787
impl Spanned for ConditionalStatements {
779788
fn span(&self) -> Span {
780789
match self {

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,7 @@ define_keywords!(
981981
WHEN,
982982
WHENEVER,
983983
WHERE,
984+
WHILE,
984985
WIDTH_BUCKET,
985986
WINDOW,
986987
WITH,

src/parser/mod.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,10 @@ impl<'a> Parser<'a> {
536536
self.prev_token();
537537
self.parse_if_stmt()
538538
}
539+
Keyword::WHILE => {
540+
self.prev_token();
541+
self.parse_while()
542+
}
539543
Keyword::RAISE => {
540544
self.prev_token();
541545
self.parse_raise_stmt()
@@ -704,8 +708,18 @@ impl<'a> Parser<'a> {
704708
}))
705709
}
706710

711+
/// Parse a `WHILE` statement.
712+
///
713+
/// See [Statement::While]
714+
fn parse_while(&mut self) -> Result<Statement, ParserError> {
715+
self.expect_keyword_is(Keyword::WHILE)?;
716+
let while_block = self.parse_conditional_statement_block(&[Keyword::END])?;
717+
718+
Ok(Statement::While(WhileStatement { while_block }))
719+
}
720+
707721
/// Parses an expression and associated list of statements
708-
/// belonging to a conditional statement like `IF` or `WHEN`.
722+
/// belonging to a conditional statement like `IF` or `WHEN` or `WHILE`.
709723
///
710724
/// Example:
711725
/// ```sql
@@ -720,20 +734,36 @@ impl<'a> Parser<'a> {
720734

721735
let condition = match &start_token.token {
722736
Token::Word(w) if w.keyword == Keyword::ELSE => None,
737+
Token::Word(w) if w.keyword == Keyword::WHILE => {
738+
let expr = self.parse_expr()?;
739+
Some(expr)
740+
}
723741
_ => {
724742
let expr = self.parse_expr()?;
725743
then_token = Some(AttachedToken(self.expect_keyword(Keyword::THEN)?));
726744
Some(expr)
727745
}
728746
};
729747

730-
let statements = self.parse_statement_list(terminal_keywords)?;
748+
let conditional_statements = if self.peek_keyword(Keyword::BEGIN) {
749+
let begin_token = self.expect_keyword(Keyword::BEGIN)?;
750+
let statements = self.parse_statement_list(terminal_keywords)?;
751+
let end_token = self.expect_keyword(Keyword::END)?;
752+
ConditionalStatements::BeginEnd(BeginEndStatements {
753+
begin_token: AttachedToken(begin_token),
754+
statements,
755+
end_token: AttachedToken(end_token),
756+
})
757+
} else {
758+
let statements = self.parse_statement_list(terminal_keywords)?;
759+
ConditionalStatements::Sequence { statements }
760+
};
731761

732762
Ok(ConditionalStatementBlock {
733763
start_token: AttachedToken(start_token),
734764
condition,
735765
then_token,
736-
conditional_statements: ConditionalStatements::Sequence { statements },
766+
conditional_statements,
737767
})
738768
}
739769

@@ -4457,6 +4487,9 @@ impl<'a> Parser<'a> {
44574487
break;
44584488
}
44594489
}
4490+
if let Token::EOF = self.peek_nth_token_ref(0).token {
4491+
break;
4492+
}
44604493
values.push(self.parse_statement()?);
44614494
self.expect_token(&Token::SemiColon)?;
44624495
}

tests/sqlparser_mssql.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1405,10 +1405,38 @@ fn test_mssql_cursor() {
14051405
\
14061406
FETCH NEXT FROM Employee_Cursor; \
14071407
\
1408+
WHILE @@FETCH_STATUS = 0 \
1409+
BEGIN \
1410+
FETCH NEXT FROM Employee_Cursor; \
1411+
END; \
1412+
\
14081413
CLOSE Employee_Cursor; \
14091414
DEALLOCATE Employee_Cursor\
14101415
";
1411-
let _ = ms().statements_parse_to(full_cursor_usage, 5, "");
1416+
let _ = ms().statements_parse_to(full_cursor_usage, 6, "");
1417+
}
1418+
1419+
#[test]
1420+
fn test_mssql_while_statement() {
1421+
let while_single_statement = "WHILE 1 = 0 PRINT 'Hello World';";
1422+
let _ = ms().verified_stmt(while_single_statement);
1423+
1424+
let while_begin_end = "\
1425+
WHILE @@FETCH_STATUS = 0 \
1426+
BEGIN \
1427+
FETCH NEXT FROM Employee_Cursor; \
1428+
END\
1429+
";
1430+
let _ = ms().verified_stmt(while_begin_end);
1431+
1432+
let while_begin_end_multiple_statements = "\
1433+
WHILE @@FETCH_STATUS = 0 \
1434+
BEGIN \
1435+
FETCH NEXT FROM Employee_Cursor; \
1436+
PRINT 'Hello World'; \
1437+
END\
1438+
";
1439+
let _ = ms().verified_stmt(while_begin_end_multiple_statements);
14121440
}
14131441

14141442
#[test]

0 commit comments

Comments
 (0)