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