diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 010a8189b..4ce99f311 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -6298,7 +6298,7 @@ impl Display for CascadeOption { } } -/// Transaction started with [ TRANSACTION | WORK ] +/// Transaction started with [ TRANSACTION | WORK | TRAN ] #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] @@ -6307,6 +6307,9 @@ pub enum BeginTransactionKind { Transaction, /// Alternate `WORK` keyword. Work, + /// MSSQL shorthand `TRAN` keyword. + /// See + Tran, } impl Display for BeginTransactionKind { @@ -6314,6 +6317,7 @@ impl Display for BeginTransactionKind { match self { BeginTransactionKind::Transaction => write!(f, "TRANSACTION"), BeginTransactionKind::Work => write!(f, "WORK"), + BeginTransactionKind::Tran => write!(f, "TRAN"), } } } diff --git a/src/dialect/mssql.rs b/src/dialect/mssql.rs index e763165d5..a2127f0da 100644 --- a/src/dialect/mssql.rs +++ b/src/dialect/mssql.rs @@ -151,8 +151,12 @@ impl Dialect for MsSqlDialect { let is_block = parser .maybe_parse(|p| { if p.parse_transaction_modifier().is_some() - || p.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]) - .is_some() + || p.parse_one_of_keywords(&[ + Keyword::TRANSACTION, + Keyword::WORK, + Keyword::TRAN, + ]) + .is_some() || matches!(p.peek_token_ref().token, Token::SemiColon | Token::EOF) { p.expected("statement", p.peek_token()) diff --git a/src/keywords.rs b/src/keywords.rs index f84f4d213..d1a4212f3 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -1050,6 +1050,7 @@ define_keywords!( TOTP, TRACE, TRAILING, + TRAN, TRANSACTION, TRANSIENT, TRANSLATE, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0b152f2be..a438b77f3 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -18067,11 +18067,14 @@ impl<'a> Parser<'a> { /// Parse a 'BEGIN' statement pub fn parse_begin(&mut self) -> Result { let modifier = self.parse_transaction_modifier(); - let transaction = match self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]) { - Some(Keyword::TRANSACTION) => Some(BeginTransactionKind::Transaction), - Some(Keyword::WORK) => Some(BeginTransactionKind::Work), - _ => None, - }; + let transaction = + match self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK, Keyword::TRAN]) + { + Some(Keyword::TRANSACTION) => Some(BeginTransactionKind::Transaction), + Some(Keyword::WORK) => Some(BeginTransactionKind::Work), + Some(Keyword::TRAN) => Some(BeginTransactionKind::Tran), + _ => None, + }; Ok(Statement::StartTransaction { modes: self.parse_transaction_modes()?, begin: true, @@ -18205,7 +18208,7 @@ impl<'a> Parser<'a> { /// Parse an optional `AND [NO] CHAIN` clause for `COMMIT` and `ROLLBACK` statements pub fn parse_commit_rollback_chain(&mut self) -> Result { - let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]); + let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK, Keyword::TRAN]); if self.parse_keyword(Keyword::AND) { let chain = !self.parse_keyword(Keyword::NO); self.expect_keyword_is(Keyword::CHAIN)?; diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 84b8658b0..fcce59f91 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -2628,3 +2628,31 @@ fn parse_mssql_begin_end_block() { _ => panic!("Expected StartTransaction, got: {stmt:?}"), } } + +/// MSSQL supports `TRAN` as shorthand for `TRANSACTION`. +/// See +#[test] +fn parse_mssql_tran_shorthand() { + // BEGIN TRAN + let sql = "BEGIN TRAN"; + let stmt = ms().verified_stmt(sql); + match &stmt { + Statement::StartTransaction { + begin, + transaction, + has_end_keyword, + .. + } => { + assert!(begin); + assert_eq!(*transaction, Some(BeginTransactionKind::Tran)); + assert!(!has_end_keyword); + } + _ => panic!("Expected StartTransaction, got: {stmt:?}"), + } + + // COMMIT TRAN normalizes to COMMIT (same as COMMIT TRANSACTION) + ms().one_statement_parses_to("COMMIT TRAN", "COMMIT"); + + // ROLLBACK TRAN normalizes to ROLLBACK (same as ROLLBACK TRANSACTION) + ms().one_statement_parses_to("ROLLBACK TRAN", "ROLLBACK"); +}