support for session transaction and transaction snapshot. (#379)

* add support for snapshot id in set transaction

Signed-off-by: poonai <rbalajis25@gmail.com>

* add support for default session transaction characteristics

Signed-off-by: poonai <rbalajis25@gmail.com>

* add additional assertion for parse_set_transaction test

Signed-off-by: poonai <rbalajis25@gmail.com>

* Fix clippy

Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
This commit is contained in:
Poonai 2021-12-15 01:43:12 +05:30 committed by GitHub
parent a81805ea30
commit 4c121a92a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 11 deletions

View file

@ -786,7 +786,11 @@ pub enum Statement {
/// `{ BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...` /// `{ BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...`
StartTransaction { modes: Vec<TransactionMode> }, StartTransaction { modes: Vec<TransactionMode> },
/// `SET TRANSACTION ...` /// `SET TRANSACTION ...`
SetTransaction { modes: Vec<TransactionMode> }, SetTransaction {
modes: Vec<TransactionMode>,
snapshot: Option<Value>,
session: bool,
},
/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]` /// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
Commit { chain: bool }, Commit { chain: bool },
/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]` /// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
@ -1369,11 +1373,22 @@ impl fmt::Display for Statement {
} }
Ok(()) Ok(())
} }
Statement::SetTransaction { modes } => { Statement::SetTransaction {
write!(f, "SET TRANSACTION")?; modes,
snapshot,
session,
} => {
if *session {
write!(f, "SET SESSION CHARACTERISTICS AS TRANSACTION")?;
} else {
write!(f, "SET TRANSACTION")?;
}
if !modes.is_empty() { if !modes.is_empty() {
write!(f, " {}", display_comma_separated(modes))?; write!(f, " {}", display_comma_separated(modes))?;
} }
if let Some(snapshot_id) = snapshot {
write!(f, " SNAPSHOT {}", snapshot_id)?;
}
Ok(()) Ok(())
} }
Statement::Commit { chain } => { Statement::Commit { chain } => {

View file

@ -412,6 +412,7 @@ define_keywords!(
SHOW, SHOW,
SIMILAR, SIMILAR,
SMALLINT, SMALLINT,
SNAPSHOT,
SOME, SOME,
SORT, SORT,
SPECIFIC, SPECIFIC,

View file

@ -2670,9 +2670,26 @@ impl<'a> Parser<'a> {
value: values, value: values,
}); });
} }
} else if variable.value == "TRANSACTION" && modifier.is_none() { } else if variable.value == "CHARACTERISTICS" {
self.expect_keywords(&[Keyword::AS, Keyword::TRANSACTION])?;
Ok(Statement::SetTransaction { Ok(Statement::SetTransaction {
modes: self.parse_transaction_modes()?, modes: self.parse_transaction_modes()?,
snapshot: None,
session: true,
})
} else if variable.value == "TRANSACTION" && modifier.is_none() {
if self.parse_keyword(Keyword::SNAPSHOT) {
let snaphot_id = self.parse_value()?;
return Ok(Statement::SetTransaction {
modes: vec![],
snapshot: Some(snaphot_id),
session: false,
});
}
Ok(Statement::SetTransaction {
modes: self.parse_transaction_modes()?,
snapshot: None,
session: false,
}) })
} else { } else {
self.expected("equals sign or TO", self.peek_token()) self.expected("equals sign or TO", self.peek_token())

View file

@ -3586,14 +3586,22 @@ fn parse_set_transaction() {
// TRANSACTION, so no need to duplicate the tests here. We just do a quick // TRANSACTION, so no need to duplicate the tests here. We just do a quick
// sanity check. // sanity check.
match verified_stmt("SET TRANSACTION READ ONLY, READ WRITE, ISOLATION LEVEL SERIALIZABLE") { match verified_stmt("SET TRANSACTION READ ONLY, READ WRITE, ISOLATION LEVEL SERIALIZABLE") {
Statement::SetTransaction { modes } => assert_eq!( Statement::SetTransaction {
modes, modes,
vec![ session,
TransactionMode::AccessMode(TransactionAccessMode::ReadOnly), snapshot,
TransactionMode::AccessMode(TransactionAccessMode::ReadWrite), } => {
TransactionMode::IsolationLevel(TransactionIsolationLevel::Serializable), assert_eq!(
] modes,
), vec![
TransactionMode::AccessMode(TransactionAccessMode::ReadOnly),
TransactionMode::AccessMode(TransactionAccessMode::ReadWrite),
TransactionMode::IsolationLevel(TransactionIsolationLevel::Serializable),
]
);
assert!(!session);
assert_eq!(snapshot, None);
}
_ => unreachable!(), _ => unreachable!(),
} }
} }

View file

@ -723,6 +723,32 @@ fn parse_map_access_expr() {
); );
} }
#[test]
fn test_transaction_statement() {
let statement = pg().verified_stmt("SET TRANSACTION SNAPSHOT '000003A1-1'");
assert_eq!(
statement,
Statement::SetTransaction {
modes: vec![],
snapshot: Some(Value::SingleQuotedString(String::from("000003A1-1"))),
session: false
}
);
let statement = pg().verified_stmt("SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY, READ WRITE, ISOLATION LEVEL SERIALIZABLE");
assert_eq!(
statement,
Statement::SetTransaction {
modes: vec![
TransactionMode::AccessMode(TransactionAccessMode::ReadOnly),
TransactionMode::AccessMode(TransactionAccessMode::ReadWrite),
TransactionMode::IsolationLevel(TransactionIsolationLevel::Serializable),
],
snapshot: None,
session: true
}
);
}
fn pg() -> TestedDialects { fn pg() -> TestedDialects {
TestedDialects { TestedDialects {
dialects: vec![Box::new(PostgreSqlDialect {})], dialects: vec![Box::new(PostgreSqlDialect {})],