Skip to main content

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}