mysql_handler/hton/xa_recover_collector.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//! Writer handle the engine uses to push prepared XA transactions into
24//! MySQL's `XA_recover_txn[]` array during `recover`.
25
26#![allow(unsafe_code)]
27
28use core::ffi::c_void;
29
30unsafe extern "C" {
31 fn mysql__xa_recover__set_entry(
32 xid_list: *mut c_void,
33 index: u32,
34 format_id: i64,
35 gtrid_ptr: *const u8,
36 gtrid_len: usize,
37 bqual_ptr: *const u8,
38 bqual_len: usize,
39 );
40}
41
42/// Bounded writer handle into the MySQL-owned `XA_recover_txn[]` array
43/// the server passes to `recover`. The engine pushes prepared XIDs via
44/// [`Self::add`]; the shim writes each one into the next free slot and
45/// sets the slot's `mod_tables` pointer to NULL (engines do not yet
46/// expose the modified-tables list). [`Self::filled`] is what the FFI
47/// callback returns to MySQL as the recovered-transaction count.
48#[derive(Debug)]
49pub struct XaRecoverCollector {
50 xid_list: *mut c_void,
51 capacity: u32,
52 filled: u32,
53}
54
55impl XaRecoverCollector {
56 /// Construct a collector that writes into `xid_list[0..capacity]`.
57 /// The pointer must outlive every [`Self::add`] call (it always
58 /// does — the shim only hands it to the engine for the duration
59 /// of one `recover` callback).
60 pub(crate) fn new(xid_list: *mut c_void, capacity: u32) -> Self {
61 Self {
62 xid_list,
63 capacity,
64 filled: 0,
65 }
66 }
67
68 /// Push one prepared XA transaction into the next free slot. Returns
69 /// `false` when the array is already full (the engine should stop
70 /// recovering in that case; MySQL will call back with a larger
71 /// buffer if more transactions are expected).
72 pub fn add(&mut self, format_id: i64, gtrid: &[u8], bqual: &[u8]) -> bool {
73 if self.filled >= self.capacity {
74 return false;
75 }
76 // SAFETY: xid_list is the MySQL-owned array valid for the
77 // duration of this call; the shim only reads from gtrid/bqual.
78 unsafe {
79 mysql__xa_recover__set_entry(
80 self.xid_list,
81 self.filled,
82 format_id,
83 gtrid.as_ptr(),
84 gtrid.len(),
85 bqual.as_ptr(),
86 bqual.len(),
87 );
88 }
89 self.filled += 1;
90 true
91 }
92
93 /// The number of XIDs pushed so far. Returned from the FFI callback.
94 #[must_use]
95 pub fn filled(&self) -> u32 {
96 self.filled
97 }
98}