qail_pg/driver/
transaction.rs1use super::{PgConnection, PgError, PgResult};
4
5fn quote_savepoint_name(name: &str) -> PgResult<String> {
8 if name.is_empty() {
9 return Err(PgError::Query("savepoint name is empty".to_string()));
10 }
11 if name.contains('\0') {
12 return Err(PgError::Query(
13 "savepoint name contains NUL byte".to_string(),
14 ));
15 }
16 Ok(format!("\"{}\"", name.replace('"', "\"\"")))
17}
18
19impl PgConnection {
20 pub async fn begin_transaction(&mut self) -> PgResult<()> {
24 self.execute_simple("BEGIN").await
25 }
26
27 pub async fn commit(&mut self) -> PgResult<()> {
30 self.execute_simple("COMMIT").await
31 }
32
33 pub async fn rollback(&mut self) -> PgResult<()> {
36 self.execute_simple("ROLLBACK").await
37 }
38
39 pub async fn savepoint(&mut self, name: &str) -> PgResult<()> {
43 self.execute_simple(&format!("SAVEPOINT {}", quote_savepoint_name(name)?))
44 .await
45 }
46
47 pub async fn rollback_to(&mut self, name: &str) -> PgResult<()> {
51 self.execute_simple(&format!(
52 "ROLLBACK TO SAVEPOINT {}",
53 quote_savepoint_name(name)?
54 ))
55 .await
56 }
57
58 pub async fn release_savepoint(&mut self, name: &str) -> PgResult<()> {
60 self.execute_simple(&format!(
61 "RELEASE SAVEPOINT {}",
62 quote_savepoint_name(name)?
63 ))
64 .await
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::quote_savepoint_name;
71
72 #[test]
73 fn quote_savepoint_name_escapes_quotes() {
74 assert_eq!(quote_savepoint_name("sp\"1").unwrap(), "\"sp\"\"1\"");
75 }
76
77 #[test]
78 fn quote_savepoint_name_rejects_empty_or_nul() {
79 assert!(quote_savepoint_name("").is_err());
80 assert!(quote_savepoint_name("sp\0shadow").is_err());
81 }
82}