Skip to content

Commit

Permalink
add support for BEGIN TRANSACTION modifiers in sqlite
Browse files Browse the repository at this point in the history
Support the following syntaxes
- BEGIN DEFERRED
- BEGIN IMMEDIATE
- BEGIN EXCLUSIVE
  • Loading branch information
takaebato committed Dec 15, 2023
1 parent 8d97330 commit 1318e49
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 1 deletion.
32 changes: 31 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1819,6 +1819,8 @@ pub enum Statement {
StartTransaction {
modes: Vec<TransactionMode>,
begin: bool,
// Only for sqlite
modifier: Option<TransactionModifier>,
},
/// `SET TRANSACTION ...`
SetTransaction {
Expand Down Expand Up @@ -3107,9 +3109,14 @@ impl fmt::Display for Statement {
Statement::StartTransaction {
modes,
begin: syntax_begin,
modifier,
} => {
if *syntax_begin {
write!(f, "BEGIN TRANSACTION")?;
if let Some(modifier) = *modifier {
write!(f, "BEGIN {} TRANSACTION", modifier)?;
} else {
write!(f, "BEGIN TRANSACTION")?;
}
} else {
write!(f, "START TRANSACTION")?;
}
Expand Down Expand Up @@ -4292,6 +4299,29 @@ impl fmt::Display for TransactionIsolationLevel {
}
}

/// Sqlite specific syntax
///
/// https://sqlite.org/lang_transaction.html
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum TransactionModifier {
Deferred,
Immediate,
Exclusive,
}

impl fmt::Display for TransactionModifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionModifier::*;
f.write_str(match self {
Deferred => "DEFERRED",
Immediate => "IMMEDIATE",
Exclusive => "EXCLUSIVE",
})
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
Expand Down
3 changes: 3 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ define_keywords!(
DECIMAL,
DECLARE,
DEFAULT,
DEFERRED,
DELETE,
DELIMITED,
DELIMITER,
Expand Down Expand Up @@ -254,6 +255,7 @@ define_keywords!(
EVERY,
EXCEPT,
EXCLUDE,
EXCLUSIVE,
EXEC,
EXECUTE,
EXISTS,
Expand Down Expand Up @@ -321,6 +323,7 @@ define_keywords!(
IF,
IGNORE,
ILIKE,
IMMEDIATE,
IMMUTABLE,
IN,
INCLUDE,
Expand Down
13 changes: 13 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7834,14 +7834,27 @@ impl<'a> Parser<'a> {
Ok(Statement::StartTransaction {
modes: self.parse_transaction_modes()?,
begin: false,
modifier: None,
})
}

pub fn parse_begin(&mut self) -> Result<Statement, ParserError> {
let modifier = if !dialect_of!(self is SQLiteDialect) {
None
} else if self.parse_keyword(Keyword::DEFERRED) {
Some(TransactionModifier::Deferred)
} else if self.parse_keyword(Keyword::IMMEDIATE) {
Some(TransactionModifier::Immediate)
} else if self.parse_keyword(Keyword::EXCLUSIVE) {
Some(TransactionModifier::Exclusive)
} else {
None
};
let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]);
Ok(Statement::StartTransaction {
modes: self.parse_transaction_modes()?,
begin: true,
modifier,
})
}

Expand Down
34 changes: 34 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6232,6 +6232,40 @@ fn parse_start_transaction() {
);
}

#[test]
fn parse_start_transaction_sqlite() {
let dialect = SQLiteDialect {};

let check = |sql: &str, expected_modifier: Option<TransactionModifier>| match Parser::parse_sql(
&dialect, &sql,
)
.unwrap()
.pop()
.unwrap()
{
Statement::StartTransaction { modifier, .. } => assert_eq!(modifier, expected_modifier),
_ => panic!("{}", sql),
};

let sql = "BEGIN DEFERRED";
check(sql, Some(TransactionModifier::Deferred));

let sql = "BEGIN DEFERRED TRANSACTION";
check(sql, Some(TransactionModifier::Deferred));

let sql = "BEGIN IMMEDIATE";
check(sql, Some(TransactionModifier::Immediate));

let sql = "BEGIN IMMEDIATE TRANSACTION";
check(sql, Some(TransactionModifier::Immediate));

let sql = "BEGIN EXCLUSIVE";
check(sql, Some(TransactionModifier::Exclusive));

let sql = "BEGIN EXCLUSIVE TRANSACTION";
check(sql, Some(TransactionModifier::Exclusive));
}

#[test]
fn parse_set_transaction() {
// SET TRANSACTION shares transaction mode parsing code with START
Expand Down

0 comments on commit 1318e49

Please sign in to comment.