mysql_handler/hton.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 handlerton interface.
24//!
25//! Where [`StorageEngine`](crate::engine::StorageEngine) is per-table, the
26//! handlerton is a per-engine singleton: one instance serves every connection.
27//! An engine opts into engine-level behaviour by implementing [`Handlerton`]
28//! and registering it with
29//! [`register_handlerton`](crate::runtime::register_handlerton). Engines that
30//! need nothing beyond table handling skip registration and keep the
31//! zero-config defaults.
32
33#[doc(hidden)]
34pub mod binlog;
35mod binlog_kind;
36mod capabilities;
37#[doc(hidden)]
38pub mod capability_ffi;
39#[doc(hidden)]
40pub mod clone;
41mod clone_kind;
42#[doc(hidden)]
43pub mod database;
44#[doc(hidden)]
45pub mod dict;
46mod dict_kind;
47#[doc(hidden)]
48pub mod discovery;
49#[doc(hidden)]
50pub mod engine_log;
51#[doc(hidden)]
52pub mod ffi;
53#[doc(hidden)]
54pub mod fk_hooks;
55mod flags;
56#[doc(hidden)]
57pub mod lifecycle;
58#[doc(hidden)]
59pub mod misc_optimizer;
60#[doc(hidden)]
61pub mod misc_stats;
62mod notification_kind;
63#[doc(hidden)]
64pub mod notifications;
65#[doc(hidden)]
66pub mod page_track;
67mod panic_function;
68mod result;
69#[doc(hidden)]
70pub mod savepoint_ffi;
71#[doc(hidden)]
72pub mod sdi;
73#[doc(hidden)]
74pub mod secondary_engine;
75#[doc(hidden)]
76pub mod secondary_engine_fail_reason;
77mod secondary_engine_kind;
78mod stat_print_sink;
79mod stat_type;
80#[doc(hidden)]
81pub mod status;
82#[doc(hidden)]
83pub mod tablespace;
84mod tablespace_kind;
85mod transaction;
86#[doc(hidden)]
87pub mod txn_context;
88#[doc(hidden)]
89pub mod txn_ffi;
90#[doc(hidden)]
91pub mod txn_row_ffi;
92#[doc(hidden)]
93pub mod xa;
94
95pub use binlog_kind::{BinlogCommand, BinlogFunc};
96pub use capabilities::HtonCapabilities;
97pub use clone_kind::{HaCloneMode, HaCloneType};
98pub use dict_kind::{DictInitMode, DictRecoveryMode};
99pub use flags::HtonFlags;
100pub use notification_kind::{HaNotificationType, SelectExecutedIn};
101pub use panic_function::HaPanicFunction;
102pub use secondary_engine_kind::{
103 SecondaryEngineGraphSimplificationRequest, SecondaryEngineOptimizerRequest,
104};
105pub use stat_print_sink::StatPrintSink;
106pub use stat_type::HaStatType;
107pub use tablespace_kind::{TablespaceType, TsCommandType};
108pub use transaction::TxnSession;
109
110use crate::engine::EngineResult;
111use crate::sys;
112
113/// The engine-level handlerton interface: the capabilities and `handlerton`
114/// struct fields that apply to the engine as a whole rather than to a single
115/// table.
116///
117/// Every method has a default, so an engine implements only what it needs and
118/// an empty `impl Handlerton for MyEngine {}` is a valid handler-only
119/// handlerton. The singleton is shared across all connection threads, hence
120/// the `Send + Sync` bound — do not relax it.
121///
122/// # Examples
123///
124/// ```
125/// use mysql_handler::hton::{Handlerton, HtonCapabilities, HtonFlags};
126///
127/// struct MyHandlerton;
128/// impl Handlerton for MyHandlerton {
129/// fn capabilities(&self) -> HtonCapabilities {
130/// HtonCapabilities::TRANSACTIONS
131/// }
132/// }
133///
134/// assert!(MyHandlerton.capabilities().contains(HtonCapabilities::TRANSACTIONS));
135/// assert_eq!(MyHandlerton.flags(), HtonFlags::CAN_RECREATE);
136/// ```
137pub trait Handlerton: Send + Sync {
138 /// The engine-level callback groups this handlerton implements.
139 ///
140 /// Each capability gates a group of `handlerton` callbacks; a group is
141 /// wired into MySQL only when its bit is set here. Defaults to
142 /// [`HtonCapabilities::empty`] — a handler-only engine.
143 fn capabilities(&self) -> HtonCapabilities {
144 HtonCapabilities::empty()
145 }
146
147 /// The `handlerton` flags (`HTON_*`).
148 ///
149 /// Defaults to [`HtonFlags::CAN_RECREATE`], matching the flag the
150 /// zero-config engine sets today. Return [`HtonFlags::NONE`] to opt out.
151 fn flags(&self) -> HtonFlags {
152 HtonFlags::CAN_RECREATE
153 }
154
155 /// Bytes of per-savepoint scratch the engine needs (`handlerton`'s
156 /// `savepoint_offset`). MySQL allocates this much per savepoint and hands it
157 /// to the savepoint callbacks as their `sv` buffer. Only consulted when the
158 /// engine declares [`HtonCapabilities::SAVEPOINTS`]; defaults to 0.
159 fn savepoint_offset(&self) -> u32 {
160 0
161 }
162
163 /// Called when a connection that has touched this engine closes, so the
164 /// engine can release per-connection state.
165 ///
166 /// MySQL only invokes this for a connection whose `thd->ha_data` slot is
167 /// non-empty, so a handler-only engine that never stores per-connection
168 /// state does not see it. Defaults to success (nothing to release).
169 ///
170 /// # Errors
171 /// Returns an [`EngineError`](crate::engine::EngineError) if releasing the
172 /// connection's state fails; MySQL logs it and the connection still closes.
173 fn close_connection(&self, _thd: Option<&sys::THD>) -> EngineResult {
174 Ok(())
175 }
176
177 /// Notification that a connection or its current statement is being
178 /// terminated (`KILL`). Defaults to no-op.
179 fn kill_connection(&self, _thd: Option<&sys::THD>) {}
180
181 /// Called before the data dictionary shuts down so the engine can stop
182 /// background tasks that might still access it. Defaults to no-op.
183 fn pre_dd_shutdown(&self) {}
184
185 /// Reset session-scoped plugin variables before the connection ends.
186 /// Defaults to no-op.
187 fn reset_plugin_vars(&self, _thd: Option<&sys::THD>) {}
188
189 /// Create the per-connection [`TxnSession`] for a new transaction.
190 ///
191 /// Invoked (through the shim) when a connection first joins a transaction,
192 /// but only for an engine that declares
193 /// [`HtonCapabilities::TRANSACTIONS`]. The returned session is stored in
194 /// the connection's `ha_data` and driven through `commit` / `rollback`
195 /// until the transaction ends. The default returns an inert session, so an
196 /// engine declaring `TRANSACTIONS` must override this to do real work.
197 fn begin_transaction(&self) -> Box<dyn TxnSession> {
198 Box::new(NoopTxnSession)
199 }
200
201 /// Commit the prepared XA transaction identified by `xid`, found during
202 /// recovery. Wired only under [`HtonCapabilities::XA`]; `xid` is opaque
203 /// (inspect only the bytes the engine needs). Defaults to unsupported.
204 ///
205 /// # Errors
206 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
207 /// by default; an XA engine overrides this and errors only on a real failure.
208 fn commit_by_xid(&self, xid: Option<&sys::XID>) -> EngineResult {
209 let _ = xid;
210 Err(crate::engine::EngineError::Unsupported)
211 }
212
213 /// Roll back the prepared XA transaction identified by `xid`. Wired only
214 /// under [`HtonCapabilities::XA`]. Defaults to unsupported.
215 ///
216 /// # Errors
217 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
218 /// by default.
219 fn rollback_by_xid(&self, xid: Option<&sys::XID>) -> EngineResult {
220 let _ = xid;
221 Err(crate::engine::EngineError::Unsupported)
222 }
223
224 /// Mark the connection's externally-coordinated transactions as prepared in
225 /// the server transaction coordinator. Wired only under
226 /// [`HtonCapabilities::XA`]. Defaults to unsupported.
227 ///
228 /// # Errors
229 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
230 /// by default.
231 fn set_prepared_in_tc(&self, thd: Option<&sys::THD>) -> EngineResult {
232 let _ = thd;
233 Err(crate::engine::EngineError::Unsupported)
234 }
235
236 /// Mark the prepared XA transaction identified by `xid` as prepared in the
237 /// server transaction coordinator. Wired only under
238 /// [`HtonCapabilities::XA`]. Defaults to unsupported.
239 ///
240 /// # Errors
241 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
242 /// by default.
243 fn set_prepared_in_tc_by_xid(&self, xid: Option<&sys::XID>) -> EngineResult {
244 let _ = xid;
245 Err(crate::engine::EngineError::Unsupported)
246 }
247
248 /// Shutdown notification: the server is invoking `ha_panic` to wind every
249 /// engine down. Defaults to success — the engine has nothing to flush on
250 /// process exit.
251 ///
252 /// # Errors
253 /// Returns an [`EngineError`](crate::engine::EngineError) if the engine
254 /// fails to shut down cleanly; MySQL logs it and continues stopping.
255 fn panic(&self, flag: HaPanicFunction) -> EngineResult {
256 let _ = flag;
257 Ok(())
258 }
259
260 /// Start a consistent-snapshot read for the connection, as requested by
261 /// `START TRANSACTION WITH CONSISTENT SNAPSHOT`. Wired only when the engine
262 /// declares [`HtonCapabilities::TRANSACTIONS`]; defaults to success (the
263 /// engine has no snapshot semantics to install).
264 ///
265 /// # Errors
266 /// Returns an [`EngineError`](crate::engine::EngineError) if the snapshot
267 /// cannot be set up.
268 fn start_consistent_snapshot(&self, _thd: Option<&sys::THD>) -> EngineResult {
269 Ok(())
270 }
271
272 /// Flush durable state to disk. `binlog_group_flush` is true when the
273 /// invocation comes from the binary log group-commit flush stage, false
274 /// from `FLUSH LOGS` or shutdown. Defaults to success (nothing to flush).
275 ///
276 /// # Errors
277 /// Returns an [`EngineError`](crate::engine::EngineError) if the flush
278 /// fails; MySQL reports the failure to the client.
279 fn flush_logs(&self, _binlog_group_flush: bool) -> EngineResult {
280 Ok(())
281 }
282
283 /// Populate `SHOW ENGINE <name> STATUS` / `LOGS` / `MUTEX` output by
284 /// emitting rows through `sink`. The trait default emits no rows, leaving
285 /// the engine status empty.
286 ///
287 /// # Errors
288 /// Returns an [`EngineError`](crate::engine::EngineError) if collecting or
289 /// emitting status fails.
290 fn show_status(
291 &self,
292 _thd: Option<&sys::THD>,
293 _sink: &StatPrintSink<'_>,
294 _stat: HaStatType,
295 ) -> EngineResult {
296 Ok(())
297 }
298
299 /// Per-partition capability bitfield (`HA_*_PARTITION_*` flags from
300 /// `sql_partition.h`). Consulted only when the engine declares
301 /// [`HtonCapabilities::PARTITIONING`]; defaults to 0 — no special
302 /// partitioning behaviour.
303 fn partition_flags(&self) -> u32 {
304 0
305 }
306
307 /// Fill engine-defined rows of an `INFORMATION_SCHEMA` table. The default
308 /// adds no rows, which is correct for an engine with no engine-only I_S
309 /// surface.
310 ///
311 /// # Errors
312 /// Returns an [`EngineError`](crate::engine::EngineError) if collecting
313 /// rows fails.
314 fn fill_is_table(&self, _thd: Option<&sys::THD>) -> EngineResult {
315 Ok(())
316 }
317
318 /// Roll the engine's log files forward as part of an in-place server
319 /// upgrade. Defaults to success — the engine has no upgrade-specific log
320 /// work to do.
321 ///
322 /// # Errors
323 /// Returns an [`EngineError`](crate::engine::EngineError) if the upgrade
324 /// step fails; MySQL aborts the upgrade.
325 fn upgrade_logs(&self, _thd: Option<&sys::THD>) -> EngineResult {
326 Ok(())
327 }
328
329 /// Finalize upgrade-specific state, called regardless of whether the
330 /// upgrade succeeded. `failed_upgrade` is true when MySQL rolled back the
331 /// upgrade. Defaults to success.
332 ///
333 /// # Errors
334 /// Returns an [`EngineError`](crate::engine::EngineError) if the cleanup
335 /// step fails.
336 fn finish_upgrade(&self, _thd: Option<&sys::THD>, _failed_upgrade: bool) -> EngineResult {
337 Ok(())
338 }
339
340 /// Whether the supplied database name is reserved by the engine and must
341 /// not be created at the SQL layer. Defaults to `false` — the engine
342 /// reserves no names.
343 fn is_reserved_db_name(&self, _name: &str) -> bool {
344 false
345 }
346
347 /// Recover a table whose dictionary entry is missing by reading the
348 /// engine-side description back into `db.name`. Defaults to "not found"
349 /// (the trait returns `Unsupported`, which the shim translates to
350 /// `HA_ERR_NO_SUCH_TABLE`).
351 ///
352 /// The shim does not yet marshal the engine's SDI blob back through the
353 /// `frmblob` / `frmlen` output parameters, so overriding this to return
354 /// `Ok(())` would claim "found" without supplying the table definition and
355 /// MySQL would fail downstream with `ER_NO_SUCH_TABLE` anyway. Keep the
356 /// default until that return path is wired.
357 ///
358 /// # Errors
359 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
360 /// by default to report "no such table".
361 fn discover(&self, _thd: Option<&sys::THD>, _db: &str, _name: &str) -> EngineResult {
362 Err(crate::engine::EngineError::Unsupported)
363 }
364
365 /// List the tables (or directory entries) the engine knows about under
366 /// `db` / `path`. `wild` is an optional shell-style filter; `dir` is true
367 /// when MySQL is asking for sub-directories. Defaults to success with no
368 /// entries reported.
369 ///
370 /// # Errors
371 /// Returns an [`EngineError`](crate::engine::EngineError) if enumeration
372 /// fails.
373 fn find_files(
374 &self,
375 _thd: Option<&sys::THD>,
376 _db: &str,
377 _path: &str,
378 _wild: Option<&str>,
379 _dir: bool,
380 ) -> EngineResult {
381 Ok(())
382 }
383
384 /// Whether `db.name` exists in the engine. The handler.cc caller maps
385 /// `true` to `HA_ERR_TABLE_EXIST` and `false` to `HA_ERR_NO_SUCH_TABLE`,
386 /// so the default `false` matches "engine has no such table".
387 fn table_exists_in_engine(&self, _thd: Option<&sys::THD>, _db: &str, _name: &str) -> bool {
388 false
389 }
390
391 /// Whether `db.table_name` is a system table this engine supports.
392 /// `is_sql_layer_system_table` is `true` when the table is an SQL-layer
393 /// system table (such as `mysql.*`); the engine should answer `false`
394 /// unless it specifically supports those at the engine layer. Defaults to
395 /// `false` — the engine supports no system tables.
396 fn is_supported_system_table(
397 &self,
398 _db: &str,
399 _table_name: &str,
400 _is_sql_layer_system_table: bool,
401 ) -> bool {
402 false
403 }
404
405 /// Notification fired after a `SELECT` completed, with the engine that
406 /// actually executed it. Defaults to no-op.
407 fn notify_after_select(&self, _thd: Option<&sys::THD>, _executed_in: SelectExecutedIn) {}
408
409 /// Notification fired when a table is created, with the bare `db` /
410 /// `table_name` strings. The `HA_CREATE_INFO` parameter is opaque to Rust
411 /// today and is not surfaced. Defaults to no-op.
412 fn notify_create_table(&self, _db: &str, _table_name: &str) {}
413
414 /// Notification fired when a table is dropped. The `Table_ref` parameter
415 /// is opaque to Rust and is not surfaced. Defaults to no-op.
416 fn notify_drop_table(&self) {}
417
418 /// Pre/post notification around an exclusive metadata lock acquisition.
419 /// Returning an error from the pre-event hook tells MySQL to abort
420 /// acquisition (the shim translates `Err` to `true`, matching MySQL's
421 /// "failure" convention). Defaults to success.
422 ///
423 /// # Errors
424 /// Returns an [`EngineError`](crate::engine::EngineError) to veto the
425 /// pre-event lock acquisition. Returning `Err` from a post-event
426 /// notification is logged by MySQL but does not undo the lock.
427 fn notify_exclusive_mdl(
428 &self,
429 _thd: Option<&sys::THD>,
430 _mdl_key: Option<&sys::MdlKey>,
431 _kind: HaNotificationType,
432 ) -> EngineResult {
433 Ok(())
434 }
435
436 /// Pre/post notification around an `ALTER TABLE`. Defaults to success.
437 ///
438 /// # Errors
439 /// Returns an [`EngineError`](crate::engine::EngineError) to veto a
440 /// pre-event alter.
441 fn notify_alter_table(
442 &self,
443 _thd: Option<&sys::THD>,
444 _mdl_key: Option<&sys::MdlKey>,
445 _kind: HaNotificationType,
446 ) -> EngineResult {
447 Ok(())
448 }
449
450 /// Pre/post notification around a `RENAME TABLE`. The old / new
451 /// db.table names are passed as borrowed `&str`. Defaults to success.
452 ///
453 /// # Errors
454 /// Returns an [`EngineError`](crate::engine::EngineError) to veto a
455 /// pre-event rename.
456 #[allow(clippy::too_many_arguments)]
457 fn notify_rename_table(
458 &self,
459 _thd: Option<&sys::THD>,
460 _mdl_key: Option<&sys::MdlKey>,
461 _kind: HaNotificationType,
462 _old_db: &str,
463 _old_name: &str,
464 _new_db: &str,
465 _new_name: &str,
466 ) -> EngineResult {
467 Ok(())
468 }
469
470 /// Pre/post notification around a `TRUNCATE TABLE`. Defaults to success.
471 ///
472 /// # Errors
473 /// Returns an [`EngineError`](crate::engine::EngineError) to veto a
474 /// pre-event truncate.
475 fn notify_truncate_table(
476 &self,
477 _thd: Option<&sys::THD>,
478 _mdl_key: Option<&sys::MdlKey>,
479 _kind: HaNotificationType,
480 ) -> EngineResult {
481 Ok(())
482 }
483
484 /// Binlog-related operation MySQL is asking the engine to handle
485 /// (`BFN_*`). Defaults to success (engine is binlog-agnostic).
486 ///
487 /// # Errors
488 /// Returns an [`EngineError`](crate::engine::EngineError) if the operation
489 /// fails.
490 fn binlog_func(&self, _thd: Option<&sys::THD>, _func: BinlogFunc) -> EngineResult {
491 Ok(())
492 }
493
494 /// Notification of a DDL command being written to the binary log. The
495 /// `query` text is bounded; per the security rule the trait must not log
496 /// it. Defaults to no-op.
497 fn binlog_log_query(
498 &self,
499 _thd: Option<&sys::THD>,
500 _command: BinlogCommand,
501 _query: &str,
502 _db: &str,
503 _table: &str,
504 ) {
505 }
506
507 /// Notification of an ACL change (`GRANT` / `REVOKE` / privilege table
508 /// modification). The `Acl_change_notification` parameter is opaque to
509 /// Rust today and is not surfaced. Defaults to no-op.
510 fn acl_notify(&self, _thd: Option<&sys::THD>) {}
511
512 /// Notification of `DROP DATABASE`. `path` is the schema's storage path
513 /// (typically `./<dbname>`). Always wired on a registered handlerton.
514 /// Defaults to no-op.
515 fn drop_database(&self, _path: &str) {}
516
517 /// Whether `tablespace_name` is acceptable for the given DDL command. Wired
518 /// only under [`HtonCapabilities::TABLESPACES`]; defaults to `true` so the
519 /// engine does not reject names for an unrelated reason.
520 fn is_valid_tablespace_name(&self, _cmd: TsCommandType, _tablespace_name: &str) -> bool {
521 true
522 }
523
524 /// Look up the tablespace name that holds `db.table_name`. The current
525 /// binding does not yet round-trip the output back to MySQL — the shim
526 /// leaves the `LEX_CSTRING*` empty — so an override returning `Ok(())` is
527 /// equivalent to "no tablespace information available".
528 ///
529 /// # Errors
530 /// Returns an [`EngineError`](crate::engine::EngineError) on lookup
531 /// failure.
532 fn get_tablespace(
533 &self,
534 _thd: Option<&sys::THD>,
535 _db_name: &str,
536 _table_name: &str,
537 ) -> EngineResult {
538 Ok(())
539 }
540
541 /// Apply a tablespace DDL (`CREATE TABLESPACE`, `ALTER TABLESPACE`, ...).
542 /// `ts_info` is opaque today. Wired only under
543 /// [`HtonCapabilities::TABLESPACES`]; defaults to success.
544 ///
545 /// # Errors
546 /// Returns an [`EngineError`](crate::engine::EngineError) if the DDL fails.
547 fn alter_tablespace(
548 &self,
549 _thd: Option<&sys::THD>,
550 _ts_info: Option<&sys::StAlterTablespace>,
551 ) -> EngineResult {
552 Ok(())
553 }
554
555 /// Default file-extension MySQL appends to tablespace data files. Wired
556 /// only under [`HtonCapabilities::TABLESPACES`]; default `None` produces a
557 /// NULL pointer at the C boundary (no extension).
558 fn tablespace_filename_ext(&self) -> Option<&'static core::ffi::CStr> {
559 None
560 }
561
562 /// Upgrade tablespace-level state from a previous server version.
563 /// Defaults to success.
564 ///
565 /// # Errors
566 /// Returns an [`EngineError`](crate::engine::EngineError) if the upgrade
567 /// step fails.
568 fn upgrade_tablespace(&self, _thd: Option<&sys::THD>) -> EngineResult {
569 Ok(())
570 }
571
572 /// Upgrade the on-disk version of `tablespace`. Defaults to success.
573 ///
574 /// # Errors
575 /// Returns an [`EngineError`](crate::engine::EngineError) if the upgrade
576 /// step fails.
577 fn upgrade_space_version(&self, _tablespace: Option<&sys::DdTablespace>) -> EngineResult {
578 Ok(())
579 }
580
581 /// Classification of the given tablespace. Defaults to `None`, which the
582 /// shim reports back to MySQL as failure to determine the type (so MySQL
583 /// keeps whatever it already knows).
584 fn get_tablespace_type(
585 &self,
586 _tablespace: Option<&sys::DdTablespace>,
587 ) -> Option<TablespaceType> {
588 None
589 }
590
591 /// Classification of the tablespace identified by `tablespace_name`.
592 /// Defaults to `None` (see [`Self::get_tablespace_type`]).
593 fn get_tablespace_type_by_name(&self, _tablespace_name: &str) -> Option<TablespaceType> {
594 None
595 }
596
597 /// Initialise the engine as the data-dictionary backend. The DD-tables /
598 /// DD-tablespaces output lists are not surfaced today (they are produced
599 /// only by the DD backend). Wired only under
600 /// [`HtonCapabilities::DICT_BACKEND`]; defaults to success.
601 ///
602 /// # Errors
603 /// Returns an [`EngineError`](crate::engine::EngineError) on init failure.
604 fn dict_init(&self, _mode: DictInitMode, _version: u32) -> EngineResult {
605 Ok(())
606 }
607
608 /// DD-backend variant of [`Self::dict_init`] used by the DDSE-specific
609 /// startup path. Defaults to success.
610 ///
611 /// # Errors
612 /// Returns an [`EngineError`](crate::engine::EngineError) on init failure.
613 fn ddse_dict_init(&self, _mode: DictInitMode, _version: u32) -> EngineResult {
614 Ok(())
615 }
616
617 /// Register the hard-coded DD table-id range with the engine. `table_id`
618 /// is `dd::Object_id` (a 64-bit integer). Defaults to no-op.
619 fn dict_register_dd_table_id(&self, _table_id: u64) {}
620
621 /// Invalidate the engine's local cache entry for `schema.table`.
622 /// Defaults to no-op.
623 fn dict_cache_reset(&self, _schema_name: &str, _table_name: &str) {}
624
625 /// Invalidate every table and tablespace entry in the engine's local
626 /// dictionary cache. Defaults to no-op.
627 fn dict_cache_reset_tables_and_tablespaces(&self) {}
628
629 /// Perform engine-side recovery work as part of dictionary initialisation.
630 /// Defaults to success.
631 ///
632 /// # Errors
633 /// Returns an [`EngineError`](crate::engine::EngineError) if recovery
634 /// fails.
635 fn dict_recover(&self, _mode: DictRecoveryMode, _version: u32) -> EngineResult {
636 Ok(())
637 }
638
639 /// Read back the server version stored in the dictionary tablespace
640 /// header. Defaults to `None`, which the shim reports back as failure.
641 fn dict_get_server_version(&self) -> Option<u32> {
642 None
643 }
644
645 /// Persist the current server version into the dictionary tablespace
646 /// header. Defaults to success.
647 ///
648 /// # Errors
649 /// Returns an [`EngineError`](crate::engine::EngineError) on write
650 /// failure.
651 fn dict_set_server_version(&self) -> EngineResult {
652 Ok(())
653 }
654
655 /// Create the SDI store for `tablespace`. Wired only under
656 /// [`HtonCapabilities::SDI`]; defaults to unsupported.
657 ///
658 /// # Errors
659 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
660 /// by default.
661 fn sdi_create(&self, _tablespace: Option<&sys::DdTablespace>) -> EngineResult {
662 Err(crate::engine::EngineError::Unsupported)
663 }
664
665 /// Drop the SDI store from `tablespace`. Defaults to unsupported.
666 ///
667 /// # Errors
668 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
669 /// by default.
670 fn sdi_drop(&self, _tablespace: Option<&sys::DdTablespace>) -> EngineResult {
671 Err(crate::engine::EngineError::Unsupported)
672 }
673
674 /// Populate `vector` with the SDI keys present in `tablespace`. The
675 /// `sdi_vector_t` output cannot be filled through the opaque
676 /// pass-through today; defaults to unsupported.
677 ///
678 /// # Errors
679 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
680 /// by default.
681 fn sdi_get_keys(
682 &self,
683 _tablespace: Option<&sys::DdTablespace>,
684 _vector: Option<&sys::SdiVector>,
685 ) -> EngineResult {
686 Err(crate::engine::EngineError::Unsupported)
687 }
688
689 /// Look up the SDI payload identified by `key` and write it into `buf`.
690 /// On success the engine must set `*len_out` to the bytes actually
691 /// written. The shim treats `buf` as a `&mut [u8]` whose length is the
692 /// caller-provided capacity. Defaults to unsupported.
693 ///
694 /// MySQL distinguishes the two error paths by inspecting `*len_out`:
695 /// - **Buffer too small** — return an error and set `*len_out` to the
696 /// required payload size so the caller can retry with a larger buffer.
697 /// - **Genuine error** (key not found, I/O failure, ...) — return an
698 /// error and set `*len_out = u64::MAX`. Without this sentinel MySQL
699 /// re-enters the call expecting a retry and may loop indefinitely.
700 ///
701 /// # Errors
702 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
703 /// by default. Engines that store SDI follow the two-path convention
704 /// above.
705 fn sdi_get(
706 &self,
707 _tablespace: Option<&sys::DdTablespace>,
708 _key: Option<&sys::SdiKey>,
709 _buf: &mut [u8],
710 _len_out: &mut u64,
711 ) -> EngineResult {
712 Err(crate::engine::EngineError::Unsupported)
713 }
714
715 /// Store `payload` as the SDI value for `key` against `table` /
716 /// `tablespace`. Defaults to unsupported.
717 ///
718 /// # Errors
719 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
720 /// by default.
721 fn sdi_set(
722 &self,
723 _tablespace: Option<&sys::DdTablespace>,
724 _table: Option<&sys::DdTable>,
725 _key: Option<&sys::SdiKey>,
726 _payload: &[u8],
727 ) -> EngineResult {
728 Err(crate::engine::EngineError::Unsupported)
729 }
730
731 /// Delete the SDI value identified by `key`. Defaults to unsupported.
732 ///
733 /// # Errors
734 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
735 /// by default.
736 fn sdi_delete(
737 &self,
738 _tablespace: Option<&sys::DdTablespace>,
739 _table: Option<&sys::DdTable>,
740 _key: Option<&sys::SdiKey>,
741 ) -> EngineResult {
742 Err(crate::engine::EngineError::Unsupported)
743 }
744
745 /// Acquire the engine-log mutex so a backup tool can snapshot a
746 /// consistent log state. Wired only under
747 /// [`HtonCapabilities::ENGINE_LOG`]; defaults to success.
748 ///
749 /// # Errors
750 /// Returns an [`EngineError`](crate::engine::EngineError) when the lock
751 /// cannot be taken.
752 fn lock_hton_log(&self) -> EngineResult {
753 Ok(())
754 }
755
756 /// Release the mutex taken by [`Self::lock_hton_log`]. Defaults to
757 /// success.
758 ///
759 /// # Errors
760 /// Returns an [`EngineError`](crate::engine::EngineError) on release
761 /// failure.
762 fn unlock_hton_log(&self) -> EngineResult {
763 Ok(())
764 }
765
766 /// Append the engine's redo / transaction log status into the
767 /// `performance_schema.log_status` collector. The `Json_dom` parameter is
768 /// opaque today; an engine with log info will need a reverse callback to
769 /// populate the JSON tree. Defaults to no-op success.
770 ///
771 /// # Errors
772 /// Returns an [`EngineError`](crate::engine::EngineError) on collection
773 /// failure.
774 fn collect_hton_log_info(&self, _json: Option<&sys::JsonDom>) -> EngineResult {
775 Ok(())
776 }
777
778 /// Whether the engine considers the two foreign-key column type
779 /// descriptors compatible (used when MySQL validates an `ADD FOREIGN KEY`
780 /// referencing this engine's tables). `Ha_fk_column_type` is opaque
781 /// today; the trait default returns `true` (accept any), which matches the
782 /// permissive base-engine behaviour. Override to enforce stricter typing.
783 fn check_fk_column_compat(
784 &self,
785 _child: Option<&sys::HaFkColumnType>,
786 _parent: Option<&sys::HaFkColumnType>,
787 _check_charsets: bool,
788 ) -> bool {
789 true
790 }
791
792 /// Plugin-observer hook fired before a transaction commits. The shim
793 /// discards the observer's `void*` argument because it belongs to the
794 /// observer plugin that registered the hook, not to the storage engine;
795 /// engines that need observer data must coordinate with the observer
796 /// through a separate channel. Defaults to no-op.
797 fn se_before_commit(&self) {}
798
799 /// Plugin-observer hook fired after a transaction commits. See
800 /// [`Self::se_before_commit`] for the observer-arg discussion. Defaults
801 /// to no-op.
802 fn se_after_commit(&self) {}
803
804 /// Plugin-observer hook fired before a transaction rolls back. See
805 /// [`Self::se_before_commit`] for the observer-arg discussion. Defaults
806 /// to no-op.
807 fn se_before_rollback(&self) {}
808
809 /// Prepare the secondary engine for executing a statement. `lex` is
810 /// opaque today. Wired only under
811 /// [`HtonCapabilities::SECONDARY_ENGINE`]; defaults to success.
812 ///
813 /// # Errors
814 /// Returns an [`EngineError`](crate::engine::EngineError) if preparation
815 /// fails; MySQL falls back to the primary engine.
816 fn prepare_secondary_engine(
817 &self,
818 _thd: Option<&sys::THD>,
819 _lex: Option<&sys::Lex>,
820 ) -> EngineResult {
821 Ok(())
822 }
823
824 /// Optimize a statement for execution on the secondary engine. `lex` is
825 /// opaque today. Defaults to success.
826 ///
827 /// # Errors
828 /// Returns an [`EngineError`](crate::engine::EngineError) if the
829 /// optimization fails; MySQL reprepares for the primary engine.
830 fn optimize_secondary_engine(
831 &self,
832 _thd: Option<&sys::THD>,
833 _lex: Option<&sys::Lex>,
834 ) -> EngineResult {
835 Ok(())
836 }
837
838 /// Compare the cost of `join` against the best plan seen so far. The
839 /// trait returns `(use_best_so_far, cheaper, secondary_engine_cost)`;
840 /// `None` defaults the triple to `(false, false, optimizer_cost)`, which
841 /// the shim writes back to the C `bool*` / `double*` outputs.
842 ///
843 /// # Errors
844 /// Returns an [`EngineError`](crate::engine::EngineError) on error; the
845 /// optimizer drops the candidate plan.
846 fn compare_secondary_engine_cost(
847 &self,
848 _thd: Option<&sys::THD>,
849 _join: Option<&sys::Join>,
850 _optimizer_cost: f64,
851 ) -> EngineResult<Option<(bool, bool, f64)>> {
852 Ok(None)
853 }
854
855 /// Evaluate (and potentially modify) the cost estimates on `access_path`
856 /// from the hypergraph optimizer. `access_path` is opaque to Rust today,
857 /// so the default cannot modify costs; returning `Ok(())` accepts the
858 /// path unchanged.
859 ///
860 /// # Errors
861 /// Returns an [`EngineError`](crate::engine::EngineError) to reject the
862 /// access path entirely.
863 fn secondary_engine_modify_access_path_cost(
864 &self,
865 _thd: Option<&sys::THD>,
866 _hypergraph: Option<&sys::JoinHypergraph>,
867 _access_path: Option<&sys::AccessPath>,
868 ) -> EngineResult {
869 Ok(())
870 }
871
872 /// Whether `EXPLAIN` references tables not loaded into the secondary
873 /// engine. Defaults to `false` (all referenced tables are loaded).
874 fn external_engine_explain_check(&self, _thd: Option<&sys::THD>) -> bool {
875 false
876 }
877
878 // `get_secondary_engine_offload_or_exec_fail_reason` and
879 // `find_secondary_engine_offload_fail_reason` are wired at the FFI layer
880 // but intentionally not surfaced as trait methods today: returning
881 // engine-owned bytes by value would drop the buffer before MySQL wrapped
882 // it in `std::string_view`, exposing freed heap memory. The FFI returns an
883 // empty view until a future setter reverse-callback can hand
884 // statement-scoped bytes to MySQL safely. `set_*` below is unaffected.
885
886 /// Persist `reason` as the offload failure reason for the query
887 /// represented by `thd`. Defaults to success.
888 ///
889 /// # Errors
890 /// Returns an [`EngineError`](crate::engine::EngineError) if the engine
891 /// could not record the reason.
892 fn set_secondary_engine_offload_fail_reason(
893 &self,
894 _thd: Option<&sys::THD>,
895 _reason: &str,
896 ) -> EngineResult {
897 Ok(())
898 }
899
900 /// Hook the hypergraph optimizer calls after
901 /// [`Self::secondary_engine_modify_access_path_cost`] to decide whether
902 /// to keep optimizing, restart with a different subgraph budget, etc.
903 /// Defaults to [`SecondaryEngineOptimizerRequest::keep_going`]. The
904 /// `JoinHypergraph` / `AccessPath` / `trace` parameters are opaque.
905 fn secondary_engine_check_optimizer_request(
906 &self,
907 _thd: Option<&sys::THD>,
908 _hypergraph: Option<&sys::JoinHypergraph>,
909 _access_path: Option<&sys::AccessPath>,
910 _current_subgraph_pairs: i32,
911 _current_subgraph_pairs_limit: i32,
912 _is_root_access_path: bool,
913 ) -> SecondaryEngineOptimizerRequest {
914 SecondaryEngineOptimizerRequest::keep_going()
915 }
916
917 /// Pre-prepare hook called early in optimization to decide whether the
918 /// secondary engine's full prepare path should run. Defaults to `false`
919 /// (skip the prepare path), matching the upstream "no further prepare"
920 /// signal.
921 fn secondary_engine_pre_prepare_hook(&self, _thd: Option<&sys::THD>) -> bool {
922 false
923 }
924
925 /// Whether the engine's data dictionary is currently read-only. Always
926 /// wired on a registered handlerton; defaults to `false`.
927 fn is_dict_readonly(&self) -> bool {
928 false
929 }
930
931 /// Clean up engine-owned temporary tables. The `List<LEX_STRING>*` MySQL
932 /// passes in is opaque today and dropped. Always wired; defaults to
933 /// success.
934 ///
935 /// # Errors
936 /// Returns an [`EngineError`](crate::engine::EngineError) on cleanup
937 /// failure.
938 fn rm_tmp_tables(&self, _thd: Option<&sys::THD>) -> EngineResult {
939 Ok(())
940 }
941
942 /// Provide engine-specific optimizer cost constants. The C signature
943 /// returns an engine-allocated `SE_cost_constants*` that MySQL takes
944 /// ownership of; today the shim cannot allocate one safely through the
945 /// opaque pass-through, so this trait method is bound for completeness
946 /// but the FFI pointer stays NULL on the handlerton. Engines wanting
947 /// custom costs will need a follow-up that allocates through a setter
948 /// reverse-callback.
949 fn get_cost_constants(&self, _storage_category: u32) {}
950
951 /// Notification that MySQL is swapping the connection's native engine
952 /// transaction. The `void *` / `void **` arguments are opaque to Rust
953 /// (observer-style) and are dropped at the FFI boundary. Always wired;
954 /// defaults to no-op.
955 fn replace_native_transaction_in_thd(&self, _thd: Option<&sys::THD>) {}
956
957 /// Optimizer pushdown at the handlerton layer. The `AccessPath` / `JOIN`
958 /// pointers are opaque to Rust today and are dropped at the FFI
959 /// boundary; this trait method is bound for completeness but the FFI
960 /// pointer stays NULL on the handlerton (a non-NULL `push_to_engine`
961 /// signals MySQL that the engine handles pushdown).
962 ///
963 /// # Errors
964 /// Returns an [`EngineError`](crate::engine::EngineError) if pushdown
965 /// fails.
966 fn push_to_engine(&self, _thd: Option<&sys::THD>) -> EngineResult {
967 Ok(())
968 }
969
970 /// Rotate the encryption master key. Wired only under
971 /// [`HtonCapabilities::ENCRYPTION`]; defaults to success.
972 ///
973 /// # Errors
974 /// Returns an [`EngineError`](crate::engine::EngineError) on rotation
975 /// failure.
976 fn rotate_encryption_master_key(&self) -> EngineResult {
977 Ok(())
978 }
979
980 /// Enable or disable engine redo logging. Wired only under
981 /// [`HtonCapabilities::ENGINE_LOG`]; defaults to success.
982 ///
983 /// # Errors
984 /// Returns an [`EngineError`](crate::engine::EngineError) on toggle
985 /// failure.
986 fn redo_log_set_state(&self, _thd: Option<&sys::THD>, _enable: bool) -> EngineResult {
987 Ok(())
988 }
989
990 /// Retrieve table statistics into MySQL's `ha_statistics`. The output
991 /// struct is opaque to Rust today, so this trait method is bound for
992 /// completeness but the FFI pointer stays NULL on the handlerton —
993 /// engines wanting to publish stats will need a follow-up that wires a
994 /// setter reverse-callback.
995 ///
996 /// # Errors
997 /// Returns an [`EngineError`](crate::engine::EngineError) on retrieval
998 /// failure.
999 fn get_table_statistics(
1000 &self,
1001 _db_name: &str,
1002 _table_name: &str,
1003 _se_private_id: u64,
1004 _flags: u32,
1005 ) -> EngineResult {
1006 Ok(())
1007 }
1008
1009 /// Retrieve the cardinality of a single index column. Same opaque-output
1010 /// situation as [`Self::get_table_statistics`]; the FFI pointer stays
1011 /// NULL on the handlerton today.
1012 ///
1013 /// # Errors
1014 /// Returns an [`EngineError`](crate::engine::EngineError) on retrieval
1015 /// failure.
1016 fn get_index_column_cardinality(
1017 &self,
1018 _db_name: &str,
1019 _table_name: &str,
1020 _index_name: &str,
1021 _index_ordinal_position: u32,
1022 _column_ordinal_position: u32,
1023 _se_private_id: u64,
1024 ) -> EngineResult<Option<u64>> {
1025 Ok(None)
1026 }
1027
1028 /// Retrieve tablespace statistics into MySQL's `ha_tablespace_statistics`.
1029 /// Same opaque-output situation as [`Self::get_table_statistics`].
1030 ///
1031 /// # Errors
1032 /// Returns an [`EngineError`](crate::engine::EngineError) on retrieval
1033 /// failure.
1034 fn get_tablespace_statistics(&self, _tablespace_name: &str, _file_name: &str) -> EngineResult {
1035 Ok(())
1036 }
1037
1038 /// Notification fired after a DDL completes. Always wired on a registered
1039 /// handlerton; defaults to no-op.
1040 fn post_ddl(&self, _thd: Option<&sys::THD>) {}
1041
1042 /// Notification fired after server recovery completes. Always wired on a
1043 /// registered handlerton; defaults to no-op.
1044 fn post_recover(&self) {}
1045
1046 /// Report the engine's clone capability bits. Wired only under
1047 /// [`HtonCapabilities::CLONE`]; defaults to 0 (no clone features
1048 /// supported). The trait surfaces the bits as a `u64` mirroring
1049 /// `std::bitset<HA_CLONE_TYPE_MAX>` width on the C side.
1050 fn clone_capability(&self) -> u64 {
1051 0
1052 }
1053
1054 /// Begin a clone copy on the source engine. The `Ha_clone_cbk` data-
1055 /// transfer object and the in/out locator parameters are opaque to Rust
1056 /// today; the trait sees only the high-level request. Defaults to
1057 /// unsupported.
1058 ///
1059 /// # Errors
1060 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1061 /// by default.
1062 fn clone_begin(
1063 &self,
1064 _thd: Option<&sys::THD>,
1065 _clone_type: HaCloneType,
1066 _mode: HaCloneMode,
1067 ) -> EngineResult {
1068 Err(crate::engine::EngineError::Unsupported)
1069 }
1070
1071 /// Copy a chunk of clone data via the engine-owned callback. Defaults
1072 /// to unsupported.
1073 ///
1074 /// # Errors
1075 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1076 /// by default.
1077 fn clone_copy(
1078 &self,
1079 _thd: Option<&sys::THD>,
1080 _task_id: u32,
1081 _cbk: Option<&sys::HaCloneCbk>,
1082 ) -> EngineResult {
1083 Err(crate::engine::EngineError::Unsupported)
1084 }
1085
1086 /// Acknowledge clone data already transferred. Defaults to unsupported.
1087 ///
1088 /// # Errors
1089 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1090 /// by default.
1091 fn clone_ack(
1092 &self,
1093 _thd: Option<&sys::THD>,
1094 _task_id: u32,
1095 _in_err: i32,
1096 _cbk: Option<&sys::HaCloneCbk>,
1097 ) -> EngineResult {
1098 Err(crate::engine::EngineError::Unsupported)
1099 }
1100
1101 /// End a clone copy. Defaults to unsupported.
1102 ///
1103 /// # Errors
1104 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1105 /// by default.
1106 fn clone_end(&self, _thd: Option<&sys::THD>, _task_id: u32, _in_err: i32) -> EngineResult {
1107 Err(crate::engine::EngineError::Unsupported)
1108 }
1109
1110 /// Begin applying clone data on the destination engine. `data_dir` is
1111 /// the target data directory. Defaults to unsupported.
1112 ///
1113 /// # Errors
1114 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1115 /// by default.
1116 fn clone_apply_begin(
1117 &self,
1118 _thd: Option<&sys::THD>,
1119 _mode: HaCloneMode,
1120 _data_dir: &str,
1121 ) -> EngineResult {
1122 Err(crate::engine::EngineError::Unsupported)
1123 }
1124
1125 /// Apply a chunk of clone data via the engine-owned callback. Defaults
1126 /// to unsupported.
1127 ///
1128 /// # Errors
1129 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1130 /// by default.
1131 fn clone_apply(
1132 &self,
1133 _thd: Option<&sys::THD>,
1134 _task_id: u32,
1135 _in_err: i32,
1136 _cbk: Option<&sys::HaCloneCbk>,
1137 ) -> EngineResult {
1138 Err(crate::engine::EngineError::Unsupported)
1139 }
1140
1141 /// End a clone apply. Defaults to unsupported.
1142 ///
1143 /// # Errors
1144 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1145 /// by default.
1146 fn clone_apply_end(
1147 &self,
1148 _thd: Option<&sys::THD>,
1149 _task_id: u32,
1150 _in_err: i32,
1151 ) -> EngineResult {
1152 Err(crate::engine::EngineError::Unsupported)
1153 }
1154
1155 /// Start page tracking. Returns the engine's start ID (LSN-like sequence
1156 /// number). Wired only under [`HtonCapabilities::PAGE_TRACKING`];
1157 /// defaults to unsupported.
1158 ///
1159 /// # Errors
1160 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1161 /// by default.
1162 fn page_track_start(&self) -> EngineResult<u64> {
1163 Err(crate::engine::EngineError::Unsupported)
1164 }
1165
1166 /// Stop page tracking. Returns the engine's stop ID. Defaults to
1167 /// unsupported.
1168 ///
1169 /// # Errors
1170 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1171 /// by default.
1172 fn page_track_stop(&self) -> EngineResult<u64> {
1173 Err(crate::engine::EngineError::Unsupported)
1174 }
1175
1176 /// Purge page-tracking data up to `purge_id`. Returns the ID actually
1177 /// purged through. Defaults to unsupported.
1178 ///
1179 /// # Errors
1180 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1181 /// by default.
1182 fn page_track_purge(&self, _purge_id: u64) -> EngineResult<u64> {
1183 Err(crate::engine::EngineError::Unsupported)
1184 }
1185
1186 /// Fetch tracked page IDs between `start_id` and `stop_id`. The MySQL
1187 /// `Page_Track_Callback` and its context are opaque to the Rust trait
1188 /// today (engines that fetch will need a reverse-callback surface).
1189 /// Defaults to unsupported.
1190 ///
1191 /// # Errors
1192 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1193 /// by default.
1194 fn page_track_get_page_ids(
1195 &self,
1196 _start_id: u64,
1197 _stop_id: u64,
1198 _buffer: &mut [u8],
1199 ) -> EngineResult {
1200 Err(crate::engine::EngineError::Unsupported)
1201 }
1202
1203 /// Approximate the number of tracked pages between `start_id` and
1204 /// `stop_id`. Defaults to unsupported.
1205 ///
1206 /// # Errors
1207 /// Returns [`EngineError::Unsupported`](crate::engine::EngineError::Unsupported)
1208 /// by default.
1209 fn page_track_get_num_page_ids(&self, _start_id: u64, _stop_id: u64) -> EngineResult<u64> {
1210 Err(crate::engine::EngineError::Unsupported)
1211 }
1212
1213 /// Fetch the page-tracking status. The C signature returns a
1214 /// `std::vector<std::pair<uint64_t, bool>>` by value; that container
1215 /// cannot be synthesised through the opaque pass-through today, so the
1216 /// trait method is bound for completeness and the shim writes an empty
1217 /// status. Defaults to no-op.
1218 fn page_track_get_status(&self) {}
1219}
1220
1221/// Inert default session returned by [`Handlerton::begin_transaction`] (see
1222/// there). Accepts and discards transaction boundaries without doing work.
1223#[derive(Debug)]
1224struct NoopTxnSession;
1225
1226impl TxnSession for NoopTxnSession {
1227 fn commit(&mut self, _all: bool) -> EngineResult {
1228 Ok(())
1229 }
1230
1231 fn rollback(&mut self, _all: bool) -> EngineResult {
1232 Ok(())
1233 }
1234}