Skip to main content

mysql_handler/hton/
capabilities.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//! Engine-level capabilities declared by a [`Handlerton`](crate::hton::Handlerton).
24
25/// The set of engine-level features a [`Handlerton`](crate::hton::Handlerton)
26/// opts into.
27///
28/// Each capability gates a group of `handlerton` callbacks. A group will be
29/// wired into the `handlerton` struct only when its bit is set here, because
30/// MySQL reads a non-NULL function pointer as a declaration that the engine
31/// supports that feature — a non-NULL `commit`, for example, marks the engine
32/// transactional. Declaring a capability the engine does not implement would
33/// route work to callbacks that cannot honour it, so default to the smallest
34/// set that is actually backed by code.
35///
36/// Combine capabilities with `|`:
37///
38/// ```
39/// use mysql_handler::hton::HtonCapabilities;
40///
41/// let caps = HtonCapabilities::TRANSACTIONS | HtonCapabilities::SAVEPOINTS;
42/// assert!(caps.contains(HtonCapabilities::TRANSACTIONS));
43/// assert!(!caps.contains(HtonCapabilities::XA));
44/// ```
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46#[non_exhaustive]
47pub struct HtonCapabilities(u32);
48
49impl HtonCapabilities {
50    /// Transaction callbacks (`commit`, `rollback`, `prepare`)
51    pub const TRANSACTIONS: Self = Self(1 << 0);
52    /// XA / 2PC recovery callbacks (`recover`, `commit_by_xid`, ...)
53    pub const XA: Self = Self(1 << 1);
54    /// Savepoint callbacks (`savepoint_set`, `savepoint_rollback`, ...)
55    pub const SAVEPOINTS: Self = Self(1 << 2);
56    /// Serialized Dictionary Information callbacks (`sdi_*`)
57    pub const SDI: Self = Self(1 << 3);
58    /// Secondary-engine callbacks (`prepare_secondary_engine`, ...)
59    pub const SECONDARY_ENGINE: Self = Self(1 << 4);
60    /// Clone-interface sub-callbacks
61    pub const CLONE: Self = Self(1 << 5);
62    /// Page-tracking sub-callbacks
63    pub const PAGE_TRACKING: Self = Self(1 << 6);
64    /// Partitioning support: gates the `partition_flags` accessor on the
65    /// handlerton. A non-NULL `partition_flags` pointer signals MySQL that the
66    /// engine implements `handler::get_partition_handler`, so this must stay
67    /// off for a non-partitioning engine.
68    pub const PARTITIONING: Self = Self(1 << 7);
69    /// Tablespace ownership: gates the tablespace callbacks (`get_tablespace`,
70    /// `alter_tablespace`, `upgrade_tablespace`, ...). Non-NULL pointers tell
71    /// MySQL the engine owns tablespaces (InnoDB-style); leave off for a
72    /// tablespace-less engine to keep MySQL from routing tablespace work here.
73    pub const TABLESPACES: Self = Self(1 << 8);
74    /// Data-dictionary backend: gates the `dict_*` callbacks. A non-DD-backend
75    /// engine (anything other than the DD storage backend itself) must keep
76    /// these unwired, otherwise MySQL routes dictionary work to an engine that
77    /// cannot honour it.
78    pub const DICT_BACKEND: Self = Self(1 << 9);
79    /// Engine-owned redo / transaction log: gates `lock_hton_log`,
80    /// `unlock_hton_log`, `collect_hton_log_info`, and `redo_log_set_state`.
81    /// Declared by engines whose log is collected by
82    /// `performance_schema.log_status`.
83    pub const ENGINE_LOG: Self = Self(1 << 10);
84    /// Encryption support: gates `rotate_encryption_master_key`. Non-NULL
85    /// signals MySQL that the engine participates in master-key rotation, so
86    /// leave it off for a non-encrypting engine.
87    pub const ENCRYPTION: Self = Self(1 << 11);
88
89    /// An empty capability set: a handler-only engine (the zero-config default)
90    #[must_use]
91    pub const fn empty() -> Self {
92        Self(0)
93    }
94
95    /// The raw bits, for handing the set across the FFI boundary
96    #[must_use]
97    pub const fn bits(self) -> u32 {
98        self.0
99    }
100
101    /// Whether every capability in `other` is present in `self`
102    #[must_use]
103    pub const fn contains(self, other: Self) -> bool {
104        self.0 & other.0 == other.0
105    }
106
107    /// The union of two capability sets
108    #[must_use]
109    pub const fn union(self, other: Self) -> Self {
110        Self(self.0 | other.0)
111    }
112}
113
114impl core::ops::BitOr for HtonCapabilities {
115    type Output = Self;
116
117    fn bitor(self, rhs: Self) -> Self {
118        self.union(rhs)
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn empty_contains_only_empty() {
128        let e = HtonCapabilities::empty();
129        assert_eq!(e.bits(), 0);
130        assert!(e.contains(HtonCapabilities::empty()));
131        assert!(!e.contains(HtonCapabilities::TRANSACTIONS));
132    }
133
134    #[test]
135    fn union_sets_both_bits() {
136        let c = HtonCapabilities::TRANSACTIONS | HtonCapabilities::SAVEPOINTS;
137        assert!(c.contains(HtonCapabilities::TRANSACTIONS));
138        assert!(c.contains(HtonCapabilities::SAVEPOINTS));
139        assert!(!c.contains(HtonCapabilities::XA));
140    }
141
142    #[test]
143    fn each_capability_has_a_distinct_bit() {
144        let all = [
145            HtonCapabilities::TRANSACTIONS,
146            HtonCapabilities::XA,
147            HtonCapabilities::SAVEPOINTS,
148            HtonCapabilities::SDI,
149            HtonCapabilities::SECONDARY_ENGINE,
150            HtonCapabilities::CLONE,
151            HtonCapabilities::PAGE_TRACKING,
152            HtonCapabilities::PARTITIONING,
153            HtonCapabilities::TABLESPACES,
154            HtonCapabilities::DICT_BACKEND,
155            HtonCapabilities::ENGINE_LOG,
156            HtonCapabilities::ENCRYPTION,
157        ];
158        for (i, a) in all.iter().enumerate() {
159            for b in &all[i + 1..] {
160                assert_ne!(a.bits(), b.bits());
161            }
162        }
163    }
164}