Skip to content

Commit 3ac5670

Browse files
guan404mingiffyio
andauthored
MSSQL: Support standalone BEGIN...END blocks (apache#2186)
Signed-off-by: Guan-Ming (Wesley) Chiu <105915352+guan404ming@users.noreply.github.com> Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
1 parent 2ac82e9 commit 3ac5670

File tree

3 files changed

+103
-5
lines changed

3 files changed

+103
-5
lines changed

src/dialect/mssql.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,29 @@ impl Dialect for MsSqlDialect {
145145
}
146146

147147
fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
148-
if parser.peek_keyword(Keyword::IF) {
148+
if parser.parse_keyword(Keyword::BEGIN) {
149+
// Check if this is a BEGIN...END block rather than BEGIN TRANSACTION
150+
let is_block = parser
151+
.maybe_parse(|p| {
152+
if p.parse_transaction_modifier().is_some()
153+
|| p.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK])
154+
.is_some()
155+
|| matches!(p.peek_token_ref().token, Token::SemiColon | Token::EOF)
156+
{
157+
p.expected("statement", p.peek_token())
158+
} else {
159+
Ok(())
160+
}
161+
})
162+
.unwrap_or(None)
163+
.is_some();
164+
if is_block {
165+
Some(parser.parse_begin_exception_end())
166+
} else {
167+
parser.prev_token();
168+
None
169+
}
170+
} else if parser.peek_keyword(Keyword::IF) {
149171
Some(self.parse_if_stmt(parser))
150172
} else if parser.parse_keywords(&[Keyword::CREATE, Keyword::TRIGGER]) {
151173
Some(self.parse_create_trigger(parser, false))

src/parser/mod.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17902,9 +17902,9 @@ impl<'a> Parser<'a> {
1790217902
})
1790317903
}
1790417904

17905-
/// Parse a 'BEGIN' statement
17906-
pub fn parse_begin(&mut self) -> Result<Statement, ParserError> {
17907-
let modifier = if !self.dialect.supports_start_transaction_modifier() {
17905+
/// Parse a transaction modifier keyword that can follow a `BEGIN` statement.
17906+
pub(crate) fn parse_transaction_modifier(&mut self) -> Option<TransactionModifier> {
17907+
if !self.dialect.supports_start_transaction_modifier() {
1790817908
None
1790917909
} else if self.parse_keyword(Keyword::DEFERRED) {
1791017910
Some(TransactionModifier::Deferred)
@@ -17918,7 +17918,12 @@ impl<'a> Parser<'a> {
1791817918
Some(TransactionModifier::Catch)
1791917919
} else {
1792017920
None
17921-
};
17921+
}
17922+
}
17923+
17924+
/// Parse a 'BEGIN' statement
17925+
pub fn parse_begin(&mut self) -> Result<Statement, ParserError> {
17926+
let modifier = self.parse_transaction_modifier();
1792217927
let transaction = match self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]) {
1792317928
Some(Keyword::TRANSACTION) => Some(BeginTransactionKind::Transaction),
1792417929
Some(Keyword::WORK) => Some(BeginTransactionKind::Work),

tests/sqlparser_mssql.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2554,3 +2554,74 @@ fn test_sql_keywords_as_column_aliases() {
25542554
}
25552555
}
25562556
}
2557+
2558+
#[test]
2559+
fn parse_mssql_begin_end_block() {
2560+
// Single statement
2561+
let sql = "BEGIN SELECT 1; END";
2562+
let stmt = ms().verified_stmt(sql);
2563+
match &stmt {
2564+
Statement::StartTransaction {
2565+
begin,
2566+
has_end_keyword,
2567+
statements,
2568+
transaction,
2569+
modifier,
2570+
..
2571+
} => {
2572+
assert!(begin);
2573+
assert!(has_end_keyword);
2574+
assert!(transaction.is_none());
2575+
assert!(modifier.is_none());
2576+
assert_eq!(statements.len(), 1);
2577+
}
2578+
_ => panic!("Expected StartTransaction, got: {stmt:?}"),
2579+
}
2580+
2581+
// Multiple statements
2582+
let sql = "BEGIN SELECT 1; SELECT 2; END";
2583+
let stmt = ms().verified_stmt(sql);
2584+
match &stmt {
2585+
Statement::StartTransaction {
2586+
statements,
2587+
has_end_keyword,
2588+
..
2589+
} => {
2590+
assert!(has_end_keyword);
2591+
assert_eq!(statements.len(), 2);
2592+
}
2593+
_ => panic!("Expected StartTransaction, got: {stmt:?}"),
2594+
}
2595+
2596+
// DML inside BEGIN/END
2597+
let sql = "BEGIN INSERT INTO t VALUES (1); UPDATE t SET x = 2; END";
2598+
let stmt = ms().verified_stmt(sql);
2599+
match &stmt {
2600+
Statement::StartTransaction {
2601+
statements,
2602+
has_end_keyword,
2603+
..
2604+
} => {
2605+
assert!(has_end_keyword);
2606+
assert_eq!(statements.len(), 2);
2607+
}
2608+
_ => panic!("Expected StartTransaction, got: {stmt:?}"),
2609+
}
2610+
2611+
// BEGIN TRANSACTION still works
2612+
let sql = "BEGIN TRANSACTION";
2613+
let stmt = ms().verified_stmt(sql);
2614+
match &stmt {
2615+
Statement::StartTransaction {
2616+
begin,
2617+
has_end_keyword,
2618+
transaction,
2619+
..
2620+
} => {
2621+
assert!(begin);
2622+
assert!(!has_end_keyword);
2623+
assert!(transaction.is_some());
2624+
}
2625+
_ => panic!("Expected StartTransaction, got: {stmt:?}"),
2626+
}
2627+
}

0 commit comments

Comments
 (0)