mysql/conn/
transaction.rs

1// Copyright (c) 2020 rust-mysql-simple contributors
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9use mysql_common::packets::OkPacket;
10
11use std::{borrow::Cow, fmt};
12
13use crate::{
14    conn::{
15        query_result::{Binary, Text},
16        ConnMut,
17    },
18    prelude::*,
19    LocalInfileHandler, Params, QueryResult, Result, Statement,
20};
21
22/// MySql transaction options.
23#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
24pub struct TxOpts {
25    with_consistent_snapshot: bool,
26    isolation_level: Option<IsolationLevel>,
27    access_mode: Option<AccessMode>,
28}
29
30impl TxOpts {
31    /// Returns the value of the characteristic.
32    pub fn with_consistent_snapshot(&self) -> bool {
33        self.with_consistent_snapshot
34    }
35
36    /// Returns the access mode value.
37    pub fn access_mode(&self) -> Option<AccessMode> {
38        self.access_mode
39    }
40
41    /// Returns the isolation level value.
42    pub fn isolation_level(&self) -> Option<IsolationLevel> {
43        self.isolation_level
44    }
45
46    /// Turns on/off the `WITH CONSISTENT SNAPSHOT` tx characteristic (defaults to `false`).
47    pub fn set_with_consistent_snapshot(mut self, val: bool) -> Self {
48        self.with_consistent_snapshot = val;
49        self
50    }
51
52    /// Defines the transaction access mode (defaults to `None`, i.e unspecified).
53    pub fn set_access_mode(mut self, access_mode: Option<AccessMode>) -> Self {
54        self.access_mode = access_mode;
55        self
56    }
57
58    /// Defines the transaction isolation level (defaults to `None`, i.e. unspecified).
59    pub fn set_isolation_level(mut self, level: Option<IsolationLevel>) -> Self {
60        self.isolation_level = level;
61        self
62    }
63}
64
65/// MySql transaction access mode.
66#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
67#[repr(u8)]
68pub enum AccessMode {
69    ReadOnly,
70    ReadWrite,
71}
72
73/// MySql transaction isolation level.
74#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
75#[repr(u8)]
76pub enum IsolationLevel {
77    ReadUncommitted,
78    ReadCommitted,
79    RepeatableRead,
80    Serializable,
81}
82
83impl fmt::Display for IsolationLevel {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match *self {
86            IsolationLevel::ReadUncommitted => write!(f, "READ UNCOMMITTED"),
87            IsolationLevel::ReadCommitted => write!(f, "READ COMMITTED"),
88            IsolationLevel::RepeatableRead => write!(f, "REPEATABLE READ"),
89            IsolationLevel::Serializable => write!(f, "SERIALIZABLE"),
90        }
91    }
92}
93
94#[derive(Debug)]
95pub struct Transaction<'a> {
96    pub(crate) conn: ConnMut<'a, 'static, 'static>,
97    committed: bool,
98    rolled_back: bool,
99    restore_local_infile_handler: Option<LocalInfileHandler>,
100}
101
102impl Transaction<'_> {
103    pub(crate) fn new<'a>(conn: ConnMut<'a, 'static, 'static>) -> Transaction<'a> {
104        let handler = conn.0.local_infile_handler.clone();
105        Transaction {
106            conn,
107            committed: false,
108            rolled_back: false,
109            restore_local_infile_handler: handler,
110        }
111    }
112
113    /// Will consume and commit transaction.
114    pub fn commit(mut self) -> Result<()> {
115        self.conn.query_drop("COMMIT")?;
116        self.committed = true;
117        Ok(())
118    }
119
120    /// Will consume and rollback transaction. You also can rely on `Drop` implementation but it
121    /// will swallow errors.
122    pub fn rollback(mut self) -> Result<()> {
123        self.conn.query_drop("ROLLBACK")?;
124        self.rolled_back = true;
125        Ok(())
126    }
127
128    /// A way to override local infile handler for this transaction.
129    /// Destructor of transaction will restore original handler.
130    pub fn set_local_infile_handler(&mut self, handler: Option<LocalInfileHandler>) {
131        self.conn.set_local_infile_handler(handler);
132    }
133
134    /// Returns the number of affected rows, reported by the server.
135    pub fn affected_rows(&self) -> u64 {
136        self.conn.affected_rows()
137    }
138
139    /// Returns the last insert id of the last query, if any.
140    pub fn last_insert_id(&self) -> Option<u64> {
141        self.conn
142            .0
143            .ok_packet
144            .as_ref()
145            .and_then(OkPacket::last_insert_id)
146    }
147
148    /// Returns the warnings count, reported by the server.
149    pub fn warnings(&self) -> u16 {
150        self.conn.warnings()
151    }
152
153    /// [Info], reported by the server.
154    ///
155    /// Will be empty if not defined.
156    ///
157    /// [Info]: http://dev.mysql.com/doc/internals/en/packet-OK_Packet.html
158    pub fn info_ref(&self) -> &[u8] {
159        self.conn.info_ref()
160    }
161
162    /// [Info], reported by the server.
163    ///
164    /// Will be empty if not defined.
165    ///
166    /// [Info]: http://dev.mysql.com/doc/internals/en/packet-OK_Packet.html
167    pub fn info_str(&self) -> Cow<str> {
168        self.conn.info_str()
169    }
170}
171
172impl<'a> Queryable for Transaction<'a> {
173    fn query_iter<T: AsRef<str>>(&mut self, query: T) -> Result<QueryResult<'_, '_, '_, Text>> {
174        self.conn.query_iter(query)
175    }
176
177    fn prep<T: AsRef<str>>(&mut self, query: T) -> Result<Statement> {
178        self.conn.prep(query)
179    }
180
181    fn close(&mut self, stmt: Statement) -> Result<()> {
182        self.conn.close(stmt)
183    }
184
185    fn exec_iter<S, P>(&mut self, stmt: S, params: P) -> Result<QueryResult<'_, '_, '_, Binary>>
186    where
187        S: AsStatement,
188        P: Into<Params>,
189    {
190        self.conn.exec_iter(stmt, params)
191    }
192}
193
194impl<'a> Drop for Transaction<'a> {
195    /// Will rollback transaction.
196    fn drop(&mut self) {
197        if !self.committed && !self.rolled_back {
198            let _ = self.conn.query_drop("ROLLBACK");
199        }
200        self.conn.0.local_infile_handler = self.restore_local_infile_handler.take();
201    }
202}