Skip to main content

mysql_handler/hton/
transaction.rs

1// Copyright (C) 2026 ren-yamanashi
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of this program hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, see <https://www.gnu.org/licenses/>.
22
23//! Per-connection transaction state.
24
25use crate::engine::EngineResult;
26
27/// The transaction state for one connection.
28///
29/// A transactional [`Handlerton`](crate::hton::Handlerton) creates one of these
30/// per connection (via [`Handlerton::begin_transaction`]). MySQL stores it in
31/// the connection's `ha_data` slot and drives it through `commit` / `rollback`,
32/// so a `TxnSession` outlives the per-table handler and accumulates work across
33/// every statement of the transaction.
34///
35/// The `Send` bound is required because a connection may be served by different
36/// threads over its lifetime, so the session moves across threads — do not
37/// relax it.
38///
39/// `all` distinguishes the two boundaries MySQL signals on the same callback:
40/// `true` is a real transaction commit/rollback (the connection is in
41/// autocommit, or `COMMIT` / `ROLLBACK` ran); `false` is the end of a single
42/// statement within a larger transaction.
43///
44/// [`Handlerton::begin_transaction`]: crate::hton::Handlerton::begin_transaction
45pub trait TxnSession: Send {
46    /// Commit the work accumulated so far.
47    ///
48    /// # Errors
49    /// Returns an [`EngineError`](crate::engine::EngineError) if the commit
50    /// fails; MySQL surfaces it to the client and the statement / transaction
51    /// is reported as failed.
52    fn commit(&mut self, all: bool) -> EngineResult;
53
54    /// Discard the work accumulated so far.
55    ///
56    /// # Errors
57    /// Returns an [`EngineError`](crate::engine::EngineError) if the rollback
58    /// fails; this is reported to MySQL but the transaction is still considered
59    /// rolled back.
60    fn rollback(&mut self, all: bool) -> EngineResult;
61
62    /// Stage a row written into `table` as part of this transaction.
63    ///
64    /// A transactional engine receives each row write here (rather than on the
65    /// per-table handler) so the change is buffered until `commit` makes it
66    /// visible or `rollback` discards it. `row` is the MySQL row image
67    /// (`record[0]`). The default ignores the write; an engine that stores data
68    /// overrides this to buffer it.
69    ///
70    /// # Errors
71    /// Returns an [`EngineError`](crate::engine::EngineError) if the row cannot
72    /// be staged (e.g. out of memory).
73    fn write_row(&mut self, table: &str, row: &[u8]) -> EngineResult {
74        let _ = (table, row);
75        Ok(())
76    }
77
78    /// Stage a row update. `old` is the pre-image (for rollback /
79    /// pre-image-keyed map lookups) and `new` is the post-image.
80    /// Same buffering contract as [`Self::write_row`]; default is a
81    /// no-op.
82    ///
83    /// # Errors
84    /// Returns an [`EngineError`](crate::engine::EngineError) if the
85    /// update cannot be staged.
86    fn update_row(&mut self, table: &str, old: &[u8], new: &[u8]) -> EngineResult {
87        let _ = (table, old, new);
88        Ok(())
89    }
90
91    /// Stage a row deletion. `row` is the pre-image of the row about
92    /// to be removed. Same buffering contract as [`Self::write_row`];
93    /// default is a no-op.
94    ///
95    /// # Errors
96    /// Returns an [`EngineError`](crate::engine::EngineError) if the
97    /// delete cannot be staged.
98    fn delete_row(&mut self, table: &str, row: &[u8]) -> EngineResult {
99        let _ = (table, row);
100        Ok(())
101    }
102
103    /// Prepare phase: flush the transaction so a following `commit` is durable.
104    ///
105    /// MySQL drives this whenever the engine takes part in two-phase commit —
106    /// most importantly alongside the binary log, which is on by default — so a
107    /// transactional engine must handle it. The default reports success, which
108    /// is correct for an engine with nothing durable to prepare; override it to
109    /// flush real state.
110    ///
111    /// # Errors
112    /// Returns an [`EngineError`](crate::engine::EngineError) if the engine
113    /// cannot prepare; MySQL then rolls the transaction back.
114    fn prepare(&mut self, all: bool) -> EngineResult {
115        let _ = all;
116        Ok(())
117    }
118
119    /// Establish a savepoint at the transaction's current point. `sv` is the
120    /// engine's per-savepoint scratch (`savepoint_offset` bytes, declared by
121    /// [`Handlerton::savepoint_offset`](crate::hton::Handlerton::savepoint_offset)):
122    /// write whatever the engine needs to identify this savepoint on rollback.
123    /// `sv` is only byte-aligned, so write it through `copy_from_slice` /
124    /// `to_le_bytes` rather than a typed pointer store. Defaults to no-op success.
125    ///
126    /// # Errors
127    /// Returns an [`EngineError`](crate::engine::EngineError) if the savepoint
128    /// cannot be recorded.
129    fn savepoint_set(&mut self, sv: &mut [u8]) -> EngineResult {
130        let _ = sv;
131        Ok(())
132    }
133
134    /// Roll the transaction back to the savepoint whose scratch is `sv` (as
135    /// written by [`savepoint_set`](Self::savepoint_set)), discarding work done
136    /// since. Defaults to no-op success.
137    ///
138    /// # Errors
139    /// Returns an [`EngineError`](crate::engine::EngineError) if the rollback
140    /// to savepoint fails.
141    fn savepoint_rollback(&mut self, sv: &[u8]) -> EngineResult {
142        let _ = sv;
143        Ok(())
144    }
145
146    /// Release (forget) the savepoint whose scratch is `sv`, keeping its work
147    /// part of the transaction. Defaults to no-op success.
148    ///
149    /// # Errors
150    /// Returns an [`EngineError`](crate::engine::EngineError) if the release
151    /// fails.
152    fn savepoint_release(&mut self, sv: &[u8]) -> EngineResult {
153        let _ = sv;
154        Ok(())
155    }
156
157    /// Whether it is safe to release metadata locks acquired after a savepoint
158    /// when rolling back to it. Defaults to `true` (the engine holds no locks
159    /// that a savepoint rollback must preserve).
160    fn savepoint_rollback_can_release_mdl(&self) -> bool {
161        true
162    }
163}