mysql_handler/engine.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//! Safe storage-engine interface for downstream implementations
24
25use core::ffi::c_void;
26use std::ffi::CStr;
27
28use crate::sys;
29
30mod bulk_access;
31mod cost_estimate;
32mod error;
33mod parallel_scan_init;
34mod range_key;
35mod reset_cached_state;
36mod rkey_function;
37mod sampling_method;
38
39pub use bulk_access::BulkAccess;
40pub use cost_estimate::CostEstimate;
41pub use error::{EngineError, EngineResult};
42pub use parallel_scan_init::ParallelScanInit;
43pub use range_key::RangeKey;
44pub use reset_cached_state::ResetCachedState;
45pub use rkey_function::RKeyFunction;
46pub use sampling_method::SamplingMethod;
47
48/// The safe interface every storage engine implements.
49///
50/// MySQL constructs one instance per opened table per session worker thread,
51/// so the trait requires `Send`. The `EngineContext` that owns a
52/// `Box<dyn StorageEngine>` crosses the C++ FFI boundary as a raw pointer;
53/// the `Send` bound is the only compile-time guarantee that this stays sound.
54#[allow(clippy::missing_errors_doc)]
55pub trait StorageEngine: Send {
56 /// Engine display name shown by `SHOW ENGINES` and used as the `ENGINE=`
57 /// value in `CREATE TABLE`. Must be a null-terminated `'static` C string
58 /// (e.g. `c"RUSTY"`) because the pointer is handed straight to MySQL.
59 fn table_type(&self) -> &'static CStr;
60
61 /// `HA_*` capability bitfield advertised to the optimizer
62 fn table_flags(&self) -> u64;
63
64 /// Per-index capability bitfield. `idx` is the index, `part` the key part;
65 /// when `all_parts` is set MySQL wants the combined flags up to and
66 /// including `part`.
67 fn index_flags(&self, idx: u32, part: u32, all_parts: bool) -> u32;
68
69 /// Create the on-disk representation for a new table named `name`.
70 /// `table_def` is the data-dictionary descriptor of the table being
71 /// created; it carries column and key metadata the engine should snapshot
72 /// out, since the borrow is only valid for the duration of the call.
73 ///
74 /// # Errors
75 /// Implementation-defined.
76 fn create(&mut self, name: &str, table_def: Option<&sys::DdTable>) -> EngineResult;
77
78 /// Open an existing table named `name` in the given `mode`. `table_def`
79 /// is the data-dictionary descriptor for the table; it carries the same
80 /// column / key metadata as on [`create`](Self::create) and is similarly
81 /// borrowed only for the call.
82 ///
83 /// # Errors
84 /// Implementation-defined.
85 fn open(&mut self, name: &str, mode: i32, table_def: Option<&sys::DdTable>) -> EngineResult;
86
87 /// Release any resources acquired by [`open`](Self::open).
88 /// Errors are implementation-defined.
89 fn close(&mut self) -> EngineResult;
90
91 /// Begin a full table scan. `scan == false` indicates the optimizer will
92 /// only use positioned access (`rnd_pos`). Errors are implementation-defined.
93 fn rnd_init(&mut self, scan: bool) -> EngineResult;
94
95 /// End the full table scan started by [`rnd_init`](Self::rnd_init),
96 /// releasing any cursor state. MySQL may call [`rnd_init`](Self::rnd_init)
97 /// again without an intervening `rnd_end`, so implementations must tolerate
98 /// a re-init.
99 ///
100 /// # Errors
101 /// The default returns `Ok(())`, matching the handler base.
102 fn rnd_end(&mut self) -> EngineResult {
103 Ok(())
104 }
105
106 /// Fetch the next row into `buf`.
107 ///
108 /// # Errors
109 /// Returns [`EngineError::EndOfFile`] once the scan is exhausted; other
110 /// variants are implementation-defined.
111 fn rnd_next(&mut self, buf: &mut [u8]) -> EngineResult;
112
113 /// Fetch a row by the position previously recorded with
114 /// [`position`](Self::position).
115 ///
116 /// # Errors
117 /// Returns [`EngineError::WrongCommand`] when the engine has no positioned
118 /// access path; other variants are implementation-defined.
119 fn rnd_pos(&mut self, buf: &mut [u8], pos: &[u8]) -> EngineResult;
120
121 /// Record the position of the row just read so a later
122 /// [`rnd_pos`](Self::rnd_pos) can replay it. `ref_out` is MySQL's
123 /// `handler::ref` buffer (`ref_length` bytes); write the engine's rowid or
124 /// primary-key encoding for `record` into it, and `rnd_pos` receives the
125 /// same bytes back. `record` holds the row in MySQL's internal record
126 /// format; neither borrow may be retained past the call.
127 fn position(&mut self, record: &[u8], ref_out: &mut [u8]);
128
129 /// Read the row whose primary key matches the one encoded in `record` (in
130 /// MySQL's internal record format), overwriting `record` with the full row.
131 /// Only meaningful for engines that advertise
132 /// `HA_PRIMARY_KEY_REQUIRED_FOR_POSITION`.
133 ///
134 /// The handler base implements this by orchestrating
135 /// [`rnd_init`](Self::rnd_init) / [`position`](Self::position) /
136 /// [`rnd_pos`](Self::rnd_pos) / [`rnd_end`](Self::rnd_end) across several
137 /// calls through its internal `ref` buffer; the binding hands the whole
138 /// operation to the engine in one call instead of replicating that
139 /// orchestration. The borrow may not be retained past the call.
140 ///
141 /// # Errors
142 /// The default returns [`EngineError::WrongCommand`].
143 fn rnd_pos_by_record(&mut self, _record: &mut [u8]) -> EngineResult {
144 Err(EngineError::WrongCommand)
145 }
146
147 /// Refresh statistics (rows, deleted rows, data length, ...) for the
148 /// optimizer. Errors are implementation-defined.
149 fn info(&mut self, flag: u32) -> EngineResult;
150
151 /// Drop a table. `table_def` is the data-dictionary descriptor of the
152 /// table being deleted; it may be `None` for temporary tables created
153 /// by the optimizer.
154 ///
155 /// May be invoked twice per `DROP TABLE` of a temporary table: once
156 /// directly, and once as part of the `handler::drop_table` chain (close +
157 /// delete_table with `table_def = None`) that fires before
158 /// [`drop_table`](Self::drop_table). Implementations that count calls
159 /// must tolerate the repeat.
160 ///
161 /// # Errors
162 /// The default returns [`EngineError::WrongCommand`]. This deliberately
163 /// diverges from MySQL's `handler::delete_table` base, which deletes the
164 /// on-disk artefact via `my_delete`; the binding leaves any artefact
165 /// cleanup to the engine implementation.
166 fn delete_table(&mut self, _name: &str, _table_def: Option<&sys::DdTable>) -> EngineResult {
167 Err(EngineError::WrongCommand)
168 }
169
170 /// Rename a table from `from` to `to`. `from_table_def` and `to_table_def`
171 /// are the data-dictionary descriptors before and after the rename.
172 ///
173 /// # Errors
174 /// The default returns [`EngineError::WrongCommand`].
175 fn rename_table(
176 &mut self,
177 _from: &str,
178 _to: &str,
179 _from_table_def: Option<&sys::DdTable>,
180 _to_table_def: Option<&sys::DdTable>,
181 ) -> EngineResult {
182 Err(EngineError::WrongCommand)
183 }
184
185 /// Notification that MySQL is dropping the table, invoked from
186 /// `ha_drop_table` on temporary-table cleanup paths. The binding mirrors
187 /// upstream's `handler::drop_table` chain (`close()` then
188 /// [`delete_table`](Self::delete_table) with `table_def = None`) on the
189 /// C++ side, so this callback fires after the chain completes and serves
190 /// purely as a post-cleanup hook. Default is a no-op.
191 ///
192 /// Any error returned by the in-chain [`delete_table`](Self::delete_table)
193 /// is swallowed by MySQL's void `handler::drop_table`; engines that need
194 /// to surface a failure during cleanup must do so out-of-band.
195 fn drop_table(&mut self, _name: &str) {}
196
197 /// Reset the table to an empty state without dropping it.
198 ///
199 /// # Errors
200 /// The default returns [`EngineError::WrongCommand`], matching the
201 /// MySQL handler base implementation.
202 fn truncate(&mut self, _table_def: Option<&sys::DdTable>) -> EngineResult {
203 Err(EngineError::WrongCommand)
204 }
205
206 /// Notification that MySQL has reassigned the underlying `TABLE` and
207 /// `TABLE_SHARE`. The base C++ handler updates its own pointers; this
208 /// callback lets the engine react if it caches per-table state. Default
209 /// is a no-op.
210 fn change_table_ptr(&mut self, _table: Option<&sys::TABLE>, _share: Option<&sys::TABLE_SHARE>) {
211 }
212
213 /// Populate engine-private metadata in `dd_table`. `reset` distinguishes
214 /// the case where the data-dictionary entry has been reset and any cached
215 /// state must be re-emitted. Returns `true` when private data was written.
216 /// The default returns `false`.
217 fn se_private_data(
218 &mut self,
219 _dd_table: Option<&sys::DdTable>,
220 _reset: ResetCachedState,
221 ) -> bool {
222 false
223 }
224
225 /// Inject implicit columns and indexes the engine requires for `table_obj`
226 /// to be created.
227 ///
228 /// # Errors
229 /// The default never errors; overrides choose which [`EngineError`]
230 /// variants they emit.
231 fn extra_columns_and_keys(
232 &mut self,
233 _create_info: Option<&sys::HA_CREATE_INFO>,
234 _create_list: Option<&sys::ListCreateField>,
235 _key_info: Option<&sys::KEY>,
236 _key_count: u32,
237 _table_obj: Option<&sys::DdTable>,
238 ) -> EngineResult {
239 Ok(())
240 }
241
242 /// Adjust the data-dictionary entry of an old-format table during a server
243 /// upgrade. Returning `Err` aborts the upgrade (mapped to C++ `bool true`).
244 ///
245 /// # Errors
246 /// The default returns `Ok(())`; overrides surface an [`EngineError`] to
247 /// abort the upgrade.
248 fn upgrade_table(
249 &mut self,
250 _thd: Option<&sys::THD>,
251 _dbname: &str,
252 _table_name: &str,
253 _dd_table: Option<&sys::DdTable>,
254 ) -> EngineResult {
255 Ok(())
256 }
257
258 /// Insert the row held in `buf`, encoded in MySQL's internal record format
259 /// (the contents of `record[0]`). The engine must copy out whatever it
260 /// needs during the call; the borrow may not be retained afterwards.
261 ///
262 /// # Errors
263 /// The default returns [`EngineError::WrongCommand`], matching the MySQL
264 /// handler base which rejects writes on engines that do not support them.
265 fn write_row(&mut self, _buf: &[u8]) -> EngineResult {
266 Err(EngineError::WrongCommand)
267 }
268
269 /// Replace the row whose existing image is `old` with the new image `new`,
270 /// both in MySQL's internal record format. Neither borrow may be retained
271 /// past the call.
272 ///
273 /// # Errors
274 /// The default returns [`EngineError::WrongCommand`].
275 fn update_row(&mut self, _old: &[u8], _new: &[u8]) -> EngineResult {
276 Err(EngineError::WrongCommand)
277 }
278
279 /// Delete the row whose current image is `buf`, in MySQL's internal record
280 /// format. The borrow may not be retained past the call.
281 ///
282 /// # Errors
283 /// The default returns [`EngineError::WrongCommand`].
284 fn delete_row(&mut self, _buf: &[u8]) -> EngineResult {
285 Err(EngineError::WrongCommand)
286 }
287
288 /// Delete every row in the table in a single operation, the fast path MySQL
289 /// takes for an unqualified `DELETE` when the engine advertises support.
290 ///
291 /// # Errors
292 /// The default returns [`EngineError::WrongCommand`].
293 fn delete_all_rows(&mut self) -> EngineResult {
294 Err(EngineError::WrongCommand)
295 }
296
297 /// Hint that a multi-row INSERT is about to begin; `rows` is MySQL's
298 /// estimate of how many rows will be written (`0` when unknown). Engines may
299 /// pre-size buffers here. The default is a no-op, matching the handler base.
300 fn start_bulk_insert(&mut self, _rows: u64) {}
301
302 /// Flush any rows buffered since
303 /// [`start_bulk_insert`](Self::start_bulk_insert).
304 ///
305 /// # Errors
306 /// The default returns `Ok(())`, matching the handler base which always
307 /// succeeds.
308 fn end_bulk_insert(&mut self) -> EngineResult {
309 Ok(())
310 }
311
312 /// Decide whether to batch the rows of a multi-row UPDATE.
313 /// [`BulkAccess::Batched`] routes subsequent rows through
314 /// [`bulk_update_row`](Self::bulk_update_row) and
315 /// [`exec_bulk_update`](Self::exec_bulk_update); [`BulkAccess::PerRow`] keeps
316 /// MySQL on the per-row [`update_row`](Self::update_row) path. The default is
317 /// [`BulkAccess::PerRow`], matching the handler base.
318 fn start_bulk_update(&mut self) -> BulkAccess {
319 BulkAccess::PerRow
320 }
321
322 /// Apply all updates buffered since
323 /// [`start_bulk_update`](Self::start_bulk_update), returning the number of
324 /// duplicate-key collisions encountered. MySQL may continue batching after
325 /// this call until [`end_bulk_update`](Self::end_bulk_update).
326 ///
327 /// # Errors
328 /// The default returns [`EngineError::WrongCommand`], matching the handler
329 /// base which rejects the bulk path unless the engine opts in.
330 fn exec_bulk_update(&mut self) -> EngineResult<u32> {
331 Err(EngineError::WrongCommand)
332 }
333
334 /// Release any state held for the bulk-update batch, called once the
335 /// statement's updates are concluded. The default is a no-op.
336 fn end_bulk_update(&mut self) {}
337
338 /// Buffer one row update for a later
339 /// [`exec_bulk_update`](Self::exec_bulk_update), replacing the image `old`
340 /// with `new` (both in MySQL's internal record format). Returns the running
341 /// count of duplicate-key collisions. Neither borrow may be retained past
342 /// the call.
343 ///
344 /// # Errors
345 /// The default returns [`EngineError::WrongCommand`], matching the handler
346 /// base.
347 fn bulk_update_row(&mut self, _old: &[u8], _new: &[u8]) -> EngineResult<u32> {
348 Err(EngineError::WrongCommand)
349 }
350
351 /// Decide whether to batch the rows of a multi-row DELETE.
352 /// [`BulkAccess::Batched`] routes the deletes through the bulk path closed
353 /// by [`end_bulk_delete`](Self::end_bulk_delete); [`BulkAccess::PerRow`]
354 /// keeps MySQL on [`delete_row`](Self::delete_row). The default is
355 /// [`BulkAccess::PerRow`], matching the handler base.
356 fn start_bulk_delete(&mut self) -> BulkAccess {
357 BulkAccess::PerRow
358 }
359
360 /// Execute all buffered deletes and close the bulk-delete batch.
361 ///
362 /// # Errors
363 /// The default returns [`EngineError::WrongCommand`], matching the handler
364 /// base.
365 fn end_bulk_delete(&mut self) -> EngineResult {
366 Err(EngineError::WrongCommand)
367 }
368
369 /// Begin an index scan on index `idx`. `sorted` requests that subsequent
370 /// reads return rows in index order. The base handler merely records the
371 /// active index and returns success.
372 ///
373 /// # Errors
374 /// The default returns `Ok(())`, matching the MySQL handler base.
375 fn index_init(&mut self, _idx: u32, _sorted: bool) -> EngineResult {
376 Ok(())
377 }
378
379 /// End the index scan started by [`index_init`](Self::index_init).
380 ///
381 /// # Errors
382 /// The default returns `Ok(())`, matching the MySQL handler base.
383 fn index_end(&mut self) -> EngineResult {
384 Ok(())
385 }
386
387 /// Position the index cursor at `key` according to `find_flag` and read the
388 /// matching row into `buf`. `key` is the leading key bytes whose length the
389 /// shim resolved from the original `key_part_map`; it is empty when MySQL
390 /// passed a null key (begin at the first key of the index). Neither borrow
391 /// may be retained past the call.
392 ///
393 /// # Errors
394 /// The default returns [`EngineError::WrongCommand`]; engines return
395 /// [`EngineError::EndOfFile`] when no row matches.
396 fn index_read_map(
397 &mut self,
398 _buf: &mut [u8],
399 _key: &[u8],
400 _find_flag: RKeyFunction,
401 ) -> EngineResult {
402 Err(EngineError::WrongCommand)
403 }
404
405 /// Read the next row in the index scan into `buf`.
406 ///
407 /// # Errors
408 /// The default returns [`EngineError::WrongCommand`]; engines return
409 /// [`EngineError::EndOfFile`] once the scan is exhausted.
410 fn index_next(&mut self, _buf: &mut [u8]) -> EngineResult {
411 Err(EngineError::WrongCommand)
412 }
413
414 /// Read the previous row in the index scan into `buf`.
415 ///
416 /// # Errors
417 /// The default returns [`EngineError::WrongCommand`]; engines return
418 /// [`EngineError::EndOfFile`] once the scan is exhausted.
419 fn index_prev(&mut self, _buf: &mut [u8]) -> EngineResult {
420 Err(EngineError::WrongCommand)
421 }
422
423 /// Read the first row of the index into `buf`.
424 ///
425 /// # Errors
426 /// The default returns [`EngineError::WrongCommand`]; engines return
427 /// [`EngineError::EndOfFile`] when the index is empty.
428 fn index_first(&mut self, _buf: &mut [u8]) -> EngineResult {
429 Err(EngineError::WrongCommand)
430 }
431
432 /// Read the last row of the index into `buf`.
433 ///
434 /// # Errors
435 /// The default returns [`EngineError::WrongCommand`]; engines return
436 /// [`EngineError::EndOfFile`] when the index is empty.
437 fn index_last(&mut self, _buf: &mut [u8]) -> EngineResult {
438 Err(EngineError::WrongCommand)
439 }
440
441 /// Read the next row that shares the leading `key` bytes with the current
442 /// position, into `buf`. The borrow on `key` may not be retained past the
443 /// call.
444 ///
445 /// # Errors
446 /// The default returns [`EngineError::WrongCommand`]; engines return
447 /// [`EngineError::EndOfFile`] when no further row shares the key.
448 fn index_next_same(&mut self, _buf: &mut [u8], _key: &[u8]) -> EngineResult {
449 Err(EngineError::WrongCommand)
450 }
451
452 /// Position the index cursor at `key` according to `find_flag` and read the
453 /// matching row into `buf`. This is the explicit-length sibling of
454 /// [`index_read_map`](Self::index_read_map): MySQL supplied the key length
455 /// directly rather than as a `key_part_map`, but the shim resolves both to
456 /// the same leading key bytes. `key` is empty when MySQL passed a null key
457 /// (begin at the first key). Neither borrow may be retained past the call.
458 ///
459 /// # Errors
460 /// The default returns [`EngineError::WrongCommand`]; engines return
461 /// [`EngineError::EndOfFile`] when no row matches.
462 fn index_read(
463 &mut self,
464 _buf: &mut [u8],
465 _key: &[u8],
466 _find_flag: RKeyFunction,
467 ) -> EngineResult {
468 Err(EngineError::WrongCommand)
469 }
470
471 /// Read from index `index` (rather than the active index) at `key` per
472 /// `find_flag`, into `buf`. The base handler brackets this with an
473 /// `index_init` / `index_end` pair; the binding instead passes `index`
474 /// explicitly so the engine never has to track an implicit active index.
475 /// `key` is empty for a null key. Neither borrow may be retained past the
476 /// call.
477 ///
478 /// # Errors
479 /// The default returns [`EngineError::WrongCommand`]; engines return
480 /// [`EngineError::EndOfFile`] when no row matches.
481 fn index_read_idx_map(
482 &mut self,
483 _buf: &mut [u8],
484 _index: u32,
485 _key: &[u8],
486 _find_flag: RKeyFunction,
487 ) -> EngineResult {
488 Err(EngineError::WrongCommand)
489 }
490
491 /// Read the last row matching `key` (or its prefix) on the active index
492 /// into `buf`. The explicit-length counterpart of
493 /// [`index_read_last_map`](Self::index_read_last_map). `key` is empty for a
494 /// null key. Neither borrow may be retained past the call.
495 ///
496 /// # Errors
497 /// The default returns [`EngineError::WrongCommand`]; engines return
498 /// [`EngineError::EndOfFile`] when no row matches.
499 fn index_read_last(&mut self, _buf: &mut [u8], _key: &[u8]) -> EngineResult {
500 Err(EngineError::WrongCommand)
501 }
502
503 /// Read the last row matching `key` (or its prefix) on the active index
504 /// into `buf`, with the key length resolved from the original
505 /// `key_part_map`. `key` is empty for a null key. Neither borrow may be
506 /// retained past the call.
507 ///
508 /// # Errors
509 /// The default returns [`EngineError::WrongCommand`]; engines return
510 /// [`EngineError::EndOfFile`] when no row matches.
511 fn index_read_last_map(&mut self, _buf: &mut [u8], _key: &[u8]) -> EngineResult {
512 Err(EngineError::WrongCommand)
513 }
514
515 /// Position the index cursor at `key` (resolved from a `key_part_map` like
516 /// [`index_read_map`](Self::index_read_map)) and read the matching row into
517 /// `buf` as the root of a pushed join. Pushed-join execution is
518 /// engine-specific (NDB-style); the binding exposes the callback so a
519 /// participating engine can implement it, but there is no `find_flag` —
520 /// MySQL only ever issues an exact-key lookup here. `key` is empty for a
521 /// null key. Neither borrow may be retained past the call.
522 ///
523 /// # Errors
524 /// The default returns [`EngineError::WrongCommand`], matching the handler
525 /// base.
526 fn index_read_pushed(&mut self, _buf: &mut [u8], _key: &[u8]) -> EngineResult {
527 Err(EngineError::WrongCommand)
528 }
529
530 /// Read the next row of the pushed-join result started by
531 /// [`index_read_pushed`](Self::index_read_pushed) into `buf`.
532 ///
533 /// # Errors
534 /// The default returns [`EngineError::WrongCommand`], matching the handler
535 /// base.
536 fn index_next_pushed(&mut self, _buf: &mut [u8]) -> EngineResult {
537 Err(EngineError::WrongCommand)
538 }
539
540 /// Begin a range scan and read its first row into `buf`. `start` and `end`
541 /// are the lower and upper bounds; either is `None` for an open end.
542 /// `eq_range` marks an equality range (`start == end`), and `sorted`
543 /// requests rows in index order. The handler base implements this by
544 /// orchestrating the index read and navigation methods plus its own
545 /// end-of-range comparison; the binding hands the whole operation to the
546 /// engine, so an overriding engine owns range-boundary enforcement.
547 ///
548 /// # Errors
549 /// The default returns [`EngineError::WrongCommand`]; engines return
550 /// [`EngineError::EndOfFile`] when the range is empty.
551 fn read_range_first(
552 &mut self,
553 _buf: &mut [u8],
554 _start: Option<RangeKey<'_>>,
555 _end: Option<RangeKey<'_>>,
556 _eq_range: bool,
557 _sorted: bool,
558 ) -> EngineResult {
559 Err(EngineError::WrongCommand)
560 }
561
562 /// Read the next row of the range scan started by
563 /// [`read_range_first`](Self::read_range_first) into `buf`.
564 ///
565 /// # Errors
566 /// The default returns [`EngineError::WrongCommand`]; engines return
567 /// [`EngineError::EndOfFile`] once the range is exhausted.
568 fn read_range_next(&mut self, _buf: &mut [u8]) -> EngineResult {
569 Err(EngineError::WrongCommand)
570 }
571
572 /// Estimate the number of rows on index `inx` between `min` and `max`
573 /// (either `None` for an open end). Used by the optimizer to cost an index
574 /// access path. Return `None` to signal "cannot estimate" (MySQL's
575 /// `HA_POS_ERROR`); the default returns `Some(10)`, mirroring the handler
576 /// base's fixed guess.
577 fn records_in_range(
578 &mut self,
579 _inx: u32,
580 _min: Option<RangeKey<'_>>,
581 _max: Option<RangeKey<'_>>,
582 ) -> Option<u64> {
583 Some(10)
584 }
585
586 /// Report whether the table is ready for a bulk load on session `thd`.
587 /// The default returns `false`, matching the handler base; engines that
588 /// support `ALTER TABLE ... SECONDARY_LOAD`-style bulk loads return `true`.
589 fn bulk_load_check(&self, _thd: Option<&sys::THD>) -> bool {
590 false
591 }
592
593 /// Report the memory budget (in bytes) the engine can devote to a bulk
594 /// load on session `thd`. The default returns `0`, matching the handler
595 /// base.
596 fn bulk_load_available_memory(&self, _thd: Option<&sys::THD>) -> usize {
597 0
598 }
599
600 /// Begin a parallel bulk load, returning an engine-owned context pointer
601 /// that [`bulk_load_execute`](Self::bulk_load_execute) and
602 /// [`bulk_load_end`](Self::bulk_load_end) receive back unchanged. `data_size`
603 /// is the total bytes to load, `memory` the budget granted, `num_threads`
604 /// the concurrency. The binding round-trips the pointer through MySQL
605 /// verbatim and never dereferences it; the engine owns its lifetime and
606 /// must free it in [`bulk_load_end`](Self::bulk_load_end). The default
607 /// returns a null pointer, matching the handler base (load not started).
608 fn bulk_load_begin(
609 &mut self,
610 _thd: Option<&sys::THD>,
611 _data_size: usize,
612 _memory: usize,
613 _num_threads: usize,
614 ) -> *mut c_void {
615 core::ptr::null_mut()
616 }
617
618 /// Load `rows` into the table on thread `thread_idx`, using the context
619 /// from [`bulk_load_begin`](Self::bulk_load_begin). `rows` and
620 /// `stat_callbacks` are opaque MySQL handles the binding cannot yet read
621 /// into, so a functioning bulk load is not expressible until that wiring
622 /// lands; the callback exists so the surface is complete. `load_ctx` is the
623 /// engine's own pointer and must be dereferenced only by the engine.
624 ///
625 /// # Errors
626 /// The default returns [`EngineError::Unsupported`], matching the handler
627 /// base which reports `HA_ERR_UNSUPPORTED` until the engine opts in.
628 fn bulk_load_execute(
629 &mut self,
630 _thd: Option<&sys::THD>,
631 _load_ctx: *mut c_void,
632 _thread_idx: usize,
633 _rows: Option<&sys::RowsMysql>,
634 _stat_callbacks: Option<&sys::BulkLoadStatCallbacks>,
635 ) -> EngineResult {
636 Err(EngineError::Unsupported)
637 }
638
639 /// End the bulk load and release the context from
640 /// [`bulk_load_begin`](Self::bulk_load_begin). Always called once after all
641 /// execute threads finish, even when `is_error` is `true`, so the engine
642 /// can free `load_ctx` on both paths.
643 ///
644 /// # Errors
645 /// The default returns `Ok(())`, matching the handler base.
646 fn bulk_load_end(
647 &mut self,
648 _thd: Option<&sys::THD>,
649 _load_ctx: *mut c_void,
650 _is_error: bool,
651 ) -> EngineResult {
652 Ok(())
653 }
654
655 /// Load `table` (opened in the primary engine) into this secondary engine;
656 /// its read-set selects which columns to load. Returns whether MySQL should
657 /// skip updating the data-dictionary metadata for this load.
658 ///
659 /// # Errors
660 /// The default returns [`EngineError::WrongCommand`]. This diverges from the
661 /// handler base, which asserts (secondary-engine-only); the binding returns
662 /// the error instead of aborting in debug builds.
663 fn load_table(&mut self, _table: Option<&sys::TABLE>) -> EngineResult<bool> {
664 Err(EngineError::WrongCommand)
665 }
666
667 /// Unload the table named `db_name`.`table_name` from this secondary engine.
668 /// When `error_if_not_loaded` is `false`, a missing table must fail
669 /// silently so a `DROP TABLE` cleanup path is not blocked.
670 ///
671 /// # Errors
672 /// The default returns [`EngineError::WrongCommand`]. This diverges from the
673 /// handler base, which asserts (secondary-engine-only); the binding returns
674 /// the error instead of aborting in debug builds.
675 fn unload_table(
676 &mut self,
677 _db_name: &str,
678 _table_name: &str,
679 _error_if_not_loaded: bool,
680 ) -> EngineResult {
681 Err(EngineError::WrongCommand)
682 }
683
684 /// Initialize a parallel scan, returning the engine-owned scan context and
685 /// the number of worker threads the engine will drive (see
686 /// [`ParallelScanInit`]). `use_reserved_threads` permits dipping into the
687 /// reserved pool when the parallel-read cap is hit; `max_desired_threads`
688 /// caps the thread count (`0` means no cap). The default returns a null
689 /// context and zero threads, matching the handler base (no parallel scan).
690 ///
691 /// # Errors
692 /// The default never errors; overrides choose their own variants.
693 fn parallel_scan_init(
694 &mut self,
695 _use_reserved_threads: bool,
696 _max_desired_threads: usize,
697 ) -> EngineResult<ParallelScanInit> {
698 Ok(ParallelScanInit::new(core::ptr::null_mut(), 0))
699 }
700
701 /// Run the parallel read using the context from
702 /// [`parallel_scan_init`](Self::parallel_scan_init). `thread_ctxs` is the
703 /// caller's per-thread context array; `init_fn` / `load_fn` / `end_fn` are
704 /// MySQL `std::function` callbacks passed as opaque pointers. The binding
705 /// cannot invoke those callbacks from Rust yet, so a functioning parallel
706 /// read is not expressible until that wiring lands; the callback exists so
707 /// the surface is complete. None of these pointers may be dereferenced
708 /// except by the code that owns them.
709 ///
710 /// # Errors
711 /// The default returns `Ok(())`, matching the handler base.
712 fn parallel_scan(
713 &mut self,
714 _scan_ctx: *mut c_void,
715 _thread_ctxs: *mut *mut c_void,
716 _init_fn: *const c_void,
717 _load_fn: *const c_void,
718 _end_fn: *const c_void,
719 ) -> EngineResult {
720 Ok(())
721 }
722
723 /// Release the parallel-scan context from
724 /// [`parallel_scan_init`](Self::parallel_scan_init). The default is a no-op.
725 fn parallel_scan_end(&mut self, _scan_ctx: *mut c_void) {}
726
727 /// Initialize sampling, returning the engine-owned scan context used by
728 /// [`sample_next`](Self::sample_next). `sampling_percentage` is the share of
729 /// rows to return (0–100), `sampling_seed` seeds the engine RNG,
730 /// `sampling_method` selects the algorithm, and `tablesample` marks an SQL
731 /// `TABLESAMPLE` rather than an internal sample. The context pointer is
732 /// round-tripped verbatim and never dereferenced by the binding.
733 ///
734 /// # Errors
735 /// The default delegates to [`rnd_init`](Self::rnd_init) with `scan = true`
736 /// and returns a null context, mirroring the handler base which samples by
737 /// scanning. The percentage filter the base applies relies on handler-
738 /// internal RNG state the binding does not expose, so the default yields
739 /// every row (an effective 100% sample) until an engine overrides this.
740 fn sample_init(
741 &mut self,
742 _sampling_percentage: f64,
743 _sampling_seed: i32,
744 _sampling_method: SamplingMethod,
745 _tablesample: bool,
746 ) -> EngineResult<*mut c_void> {
747 match self.rnd_init(true) {
748 Ok(()) => Ok(core::ptr::null_mut()),
749 Err(e) => Err(e),
750 }
751 }
752
753 /// Read the next sampled row into `buf`, using the context from
754 /// [`sample_init`](Self::sample_init).
755 ///
756 /// # Errors
757 /// The default delegates to [`rnd_next`](Self::rnd_next) (no percentage
758 /// filtering); engines return [`EngineError::EndOfFile`] once the sample is
759 /// exhausted.
760 fn sample_next(&mut self, _scan_ctx: *mut c_void, buf: &mut [u8]) -> EngineResult {
761 self.rnd_next(buf)
762 }
763
764 /// End sampling and release the context from
765 /// [`sample_init`](Self::sample_init).
766 ///
767 /// # Errors
768 /// The default delegates to [`rnd_end`](Self::rnd_end), matching the handler
769 /// base.
770 fn sample_end(&mut self, _scan_ctx: *mut c_void) -> EngineResult {
771 self.rnd_end()
772 }
773
774 /// Begin a full-text search scan.
775 ///
776 /// # Errors
777 /// The default returns [`EngineError::WrongCommand`], matching the handler
778 /// base.
779 fn ft_init(&mut self) -> EngineResult {
780 Err(EngineError::WrongCommand)
781 }
782
783 /// Create a full-text search handle for index `inx` and query `key`, with
784 /// `flags` selecting the search mode. Returns an engine-owned
785 /// `FT_INFO`-compatible pointer that MySQL drives through its vtable, or
786 /// null when the engine cannot serve the search. `key` is MySQL's `String`
787 /// query object, opaque to the binding. The pointer is round-tripped
788 /// verbatim and never dereferenced by the binding; the engine owns its
789 /// lifetime. The default returns null, matching the handler base (which
790 /// raises `ER_TABLE_CANT_HANDLE_FT`).
791 fn ft_init_ext(
792 &mut self,
793 _flags: u32,
794 _inx: u32,
795 _key: Option<&sys::MysqlString>,
796 ) -> *mut c_void {
797 core::ptr::null_mut()
798 }
799
800 /// Hint-aware variant of [`ft_init_ext`](Self::ft_init_ext). `flags` is
801 /// pre-extracted from `hints` by the shim (the binding cannot read the
802 /// opaque `hints` object from Rust); `hints` is still passed for engines
803 /// that grow richer hint handling. The default delegates to
804 /// [`ft_init_ext`](Self::ft_init_ext), mirroring the handler base.
805 fn ft_init_ext_with_hints(
806 &mut self,
807 flags: u32,
808 inx: u32,
809 key: Option<&sys::MysqlString>,
810 _hints: Option<&sys::FtHints>,
811 ) -> *mut c_void {
812 self.ft_init_ext(flags, inx, key)
813 }
814
815 /// Read the next row matching the active full-text search into `buf`.
816 ///
817 /// # Errors
818 /// The default returns [`EngineError::WrongCommand`]; engines return
819 /// [`EngineError::EndOfFile`] once the matches are exhausted.
820 fn ft_read(&mut self, _buf: &mut [u8]) -> EngineResult {
821 Err(EngineError::WrongCommand)
822 }
823
824 /// Estimate the cost of a multi-range read over a known set of ranges on
825 /// index `keyno`, for the optimizer's const-range path. `seq` is MySQL's
826 /// `RANGE_SEQ_IF` range-sequence interface, `seq_init_param` its init
827 /// argument (round-tripped without dereference), and `cost` the
828 /// `Cost_estimate` accumulator. These are opaque MySQL objects the binding
829 /// cannot drive from Rust yet, so a custom estimate is not expressible until
830 /// that wiring lands; the callback exists so the surface is complete.
831 ///
832 /// Return `None` (the default) to use the base disk-sweep MRR
833 /// implementation, which is built on
834 /// [`read_range_first`](Self::read_range_first) /
835 /// [`read_range_next`](Self::read_range_next). Engines providing a custom
836 /// multi-range read return `Some(rows)`.
837 fn multi_range_read_info_const(
838 &mut self,
839 _keyno: u32,
840 _seq: Option<&sys::RangeSeqIf>,
841 _seq_init_param: *mut c_void,
842 _n_ranges: u32,
843 _cost: Option<&sys::CostEstimate>,
844 ) -> Option<u64> {
845 None
846 }
847
848 /// Estimate the cost of a multi-range read over `n_ranges` ranges spanning
849 /// `keys` rows on index `keyno`. `cost` is the `Cost_estimate` accumulator,
850 /// an opaque MySQL object the binding cannot drive from Rust yet.
851 ///
852 /// Return `None` (the default) to use the base disk-sweep MRR
853 /// implementation; engines providing a custom multi-range read return
854 /// `Some(rows)`.
855 fn multi_range_read_info(
856 &mut self,
857 _keyno: u32,
858 _n_ranges: u32,
859 _keys: u32,
860 _cost: Option<&sys::CostEstimate>,
861 ) -> Option<u64> {
862 None
863 }
864
865 /// Initialize a multi-range read scan over the ranges from `seq` (init
866 /// argument `seq_init_param`), with `mode` carrying the `HA_MRR_*` flags and
867 /// `buf` a caller-owned `HANDLER_BUFFER` scratch area. `seq` and `buf` are
868 /// opaque MySQL objects the binding cannot drive from Rust yet.
869 ///
870 /// Return `None` (the default) to use the base disk-sweep MRR
871 /// implementation, which drives
872 /// [`read_range_first`](Self::read_range_first) /
873 /// [`read_range_next`](Self::read_range_next). Engines providing a custom
874 /// multi-range read return `Some(result)`.
875 fn multi_range_read_init(
876 &mut self,
877 _seq: Option<&sys::RangeSeqIf>,
878 _seq_init_param: *mut c_void,
879 _n_ranges: u32,
880 _mode: u32,
881 _buf: Option<&sys::HandlerBuffer>,
882 ) -> Option<EngineResult> {
883 None
884 }
885
886 /// Read the next row of the multi-range read scan into `buf`, writing the
887 /// range association through `range_info` (an opaque `char**` out-pointer
888 /// the binding round-trips without dereference).
889 ///
890 /// Return `None` (the default) to use the base disk-sweep MRR
891 /// implementation; engines providing a custom multi-range read return
892 /// `Some(result)`, where [`EngineError::EndOfFile`] marks the end of the
893 /// scan.
894 fn multi_range_read_next(
895 &mut self,
896 _buf: &mut [u8],
897 _range_info: *mut *mut c_void,
898 ) -> Option<EngineResult> {
899 None
900 }
901
902 /// Maximum row length the engine supports, in bytes. Return `None` (the
903 /// default) to use the handler base (`HA_MAX_REC_LENGTH`); engines with a
904 /// tighter cap return `Some(len)`.
905 fn max_supported_record_length(&self) -> Option<u32> {
906 None
907 }
908
909 /// Maximum number of indexes the engine supports on one table. Return `None`
910 /// (the default) to use the handler base (`0`, i.e. no indexes); engines
911 /// that support indexes return `Some(count)` — this is the gate MySQL checks
912 /// before allowing `CREATE TABLE ... KEY(...)`.
913 fn max_supported_keys(&self) -> Option<u32> {
914 None
915 }
916
917 /// Maximum number of key parts in one index. Return `None` (the default) to
918 /// use the handler base (`MAX_REF_PARTS`); engines with a tighter cap return
919 /// `Some(parts)`.
920 fn max_supported_key_parts(&self) -> Option<u32> {
921 None
922 }
923
924 /// Maximum total key length in bytes. Return `None` (the default) to use the
925 /// handler base (`MAX_KEY_LENGTH`); engines with a tighter cap return
926 /// `Some(len)`.
927 fn max_supported_key_length(&self) -> Option<u32> {
928 None
929 }
930
931 /// Maximum length in bytes of a single key part for the table described by
932 /// `create_info` (an opaque MySQL `HA_CREATE_INFO`). Return `None` (the
933 /// default) to use the handler base (`255`); engines with a different cap
934 /// return `Some(len)`.
935 fn max_supported_key_part_length(
936 &self,
937 _create_info: Option<&sys::HA_CREATE_INFO>,
938 ) -> Option<u32> {
939 None
940 }
941
942 /// Minimum row length in bytes for a table created with `options` (the
943 /// `HA_CREATE_INFO` table-option bitfield). Return `None` (the default) to
944 /// use the handler base (`1`); engines with a larger floor return
945 /// `Some(len)`.
946 fn min_record_length(&self, _options: u32) -> Option<u32> {
947 None
948 }
949
950 /// Extra per-record buffer space the engine needs beyond the row image, in
951 /// bytes. Return `None` (the default) to use the handler base (`0`); engines
952 /// needing scratch space return `Some(len)`.
953 fn extra_rec_buf_length(&self) -> Option<u32> {
954 None
955 }
956
957 /// In-memory buffer size the engine reports to the optimizer, in bytes, or a
958 /// negative value when not applicable. Return `None` (the default) to use the
959 /// handler base (`-1`); engines return `Some(bytes)`.
960 fn memory_buffer_size(&self) -> Option<i64> {
961 None
962 }
963
964 /// Whether the engine stores multi-byte values low byte first
965 /// (little-endian). Return `None` (the default) to use the handler base
966 /// (`true`); engines return `Some(flag)`.
967 fn low_byte_first(&self) -> Option<bool> {
968 None
969 }
970
971 /// Live checksum of the table, or `None` (the default) to use the handler
972 /// base (`0`, no checksum). Engines that maintain one return `Some(sum)`.
973 fn checksum(&self) -> Option<u32> {
974 None
975 }
976
977 /// Whether the table is marked crashed and needs repair. Return `None` (the
978 /// default) to use the handler base (`false`); engines return `Some(flag)`.
979 fn is_crashed(&self) -> Option<bool> {
980 None
981 }
982
983 /// Whether MySQL should attempt automatic repair when the table is found
984 /// crashed on open. Return `None` (the default) to use the handler base
985 /// (`false`); engines return `Some(flag)`.
986 fn auto_repair(&self) -> Option<bool> {
987 None
988 }
989
990 /// Whether the primary key is clustered (rows stored in PK order). Return
991 /// `None` (the default) to use the handler base (`false`); engines return
992 /// `Some(flag)`.
993 fn primary_key_is_clustered(&self) -> Option<bool> {
994 None
995 }
996
997 /// Resolve the real `row_type` for a table created from `create_info` (an
998 /// opaque MySQL `HA_CREATE_INFO`), as the raw `enum row_type` integer.
999 /// Return `None` (the default) to use the handler base, which derives the
1000 /// type from the create options; engines return `Some(row_type)`.
1001 fn real_row_type(&self, _create_info: Option<&sys::HA_CREATE_INFO>) -> Option<i32> {
1002 None
1003 }
1004
1005 /// Default index algorithm as the raw `enum ha_key_alg` integer, used when
1006 /// the user did not specify one. Return `None` (the default) to use the
1007 /// handler base (`HA_KEY_ALG_SE_SPECIFIC`); engines return `Some(alg)`.
1008 fn default_index_algorithm(&self) -> Option<i32> {
1009 None
1010 }
1011
1012 /// Whether the engine supports index algorithm `key_alg` (a raw
1013 /// `enum ha_key_alg` integer). Return `None` (the default) to use the
1014 /// handler base (supports only its default algorithm); engines return
1015 /// `Some(flag)`.
1016 fn is_index_algorithm_supported(&self, _key_alg: i32) -> Option<bool> {
1017 None
1018 }
1019
1020 /// Whether the engine wants MySQL to allocate a record buffer for
1021 /// prefetching, and for how many rows. Return `Some(max_rows)` to request a
1022 /// buffer sized for `max_rows`; `None` (the default) uses the handler base
1023 /// (no buffer wanted).
1024 fn record_buffer_wanted(&self) -> Option<u64> {
1025 None
1026 }
1027
1028 /// Engine-specific text appended to the `Extra` column of `EXPLAIN`. Return
1029 /// `None` (the default) to use the handler base (empty string); engines
1030 /// return `Some(text)`.
1031 fn explain_extra(&self) -> Option<String> {
1032 None
1033 }
1034
1035 /// Whether indexes are currently disabled (e.g. after `ALTER TABLE ...
1036 /// DISABLE KEYS`), as the raw handler int (`0` = enabled). Return `None`
1037 /// (the default) to use the handler base (`0`); engines return `Some(code)`.
1038 fn indexes_are_disabled(&mut self) -> Option<i32> {
1039 None
1040 }
1041
1042 /// Estimated cost of a full table scan, in MySQL's legacy cost unit. Return
1043 /// `None` (the default) to use the handler base, which derives it from
1044 /// `stats.data_file_length`; engines return `Some(time)`.
1045 ///
1046 /// MySQL recommends overriding this rather than
1047 /// [`table_scan_cost`](Self::table_scan_cost), whose base implementation is
1048 /// built from this value.
1049 fn scan_time(&mut self) -> Option<f64> {
1050 None
1051 }
1052
1053 /// Estimated cost of reading `ranges` ranges totalling `rows` rows through
1054 /// index `index`, in MySQL's legacy cost unit. Return `None` (the default)
1055 /// to use the handler base; engines return `Some(time)`.
1056 fn read_time(&mut self, _index: u32, _ranges: u32, _rows: u64) -> Option<f64> {
1057 None
1058 }
1059
1060 /// Estimated cost of an index-only read of `records` rows through index
1061 /// `keynr`, in MySQL's legacy cost unit. Return `None` (the default) to use
1062 /// the handler base; engines return `Some(time)`.
1063 fn index_only_read_time(&mut self, _keynr: u32, _records: f64) -> Option<f64> {
1064 None
1065 }
1066
1067 /// Cost estimate for a full table scan. Return `None` (the default) to use
1068 /// the handler base, which derives it from [`scan_time`](Self::scan_time);
1069 /// engines return `Some(cost)`.
1070 fn table_scan_cost(&mut self) -> Option<CostEstimate> {
1071 None
1072 }
1073
1074 /// Cost estimate for reading `ranges` ranges spanning `rows` rows from index
1075 /// `index` without fetching the full row. Return `None` (the default) to use
1076 /// the handler base, derived from
1077 /// [`index_only_read_time`](Self::index_only_read_time); engines return
1078 /// `Some(cost)`.
1079 fn index_scan_cost(&mut self, _index: u32, _ranges: f64, _rows: f64) -> Option<CostEstimate> {
1080 None
1081 }
1082
1083 /// Cost estimate for reading `ranges` ranges spanning `rows` rows from index
1084 /// `index`, including fetching the full rows. Return `None` (the default) to
1085 /// use the handler base, derived from [`read_time`](Self::read_time); engines
1086 /// return `Some(cost)`.
1087 fn read_cost(&mut self, _index: u32, _ranges: f64, _rows: f64) -> Option<CostEstimate> {
1088 None
1089 }
1090
1091 /// Estimated cost of `reads` non-sequential accesses against index `index`,
1092 /// in the same unit as [`worst_seek_times`](Self::worst_seek_times). Return
1093 /// `None` (the default) to use the handler base (`Cost_model::page_read_cost`);
1094 /// engines return `Some(cost)`.
1095 fn page_read_cost(&mut self, _index: u32, _reads: f64) -> Option<f64> {
1096 None
1097 }
1098
1099 /// Upper-bound cost of `reads` seek-and-read key lookups, in the same unit as
1100 /// [`page_read_cost`](Self::page_read_cost). Return `None` (the default) to
1101 /// use the handler base; engines return `Some(cost)`.
1102 fn worst_seek_times(&mut self, _reads: f64) -> Option<f64> {
1103 None
1104 }
1105
1106 /// Exact number of rows in the table. Return `None` (the default) to use the
1107 /// handler base, which counts rows with a full table scan; engines that can
1108 /// answer directly return `Some(Ok(rows))`, or `Some(Err(_))` to surface a
1109 /// failure.
1110 ///
1111 /// # Errors
1112 /// The error variant is implementation-defined and maps to the matching
1113 /// `HA_ERR_*` code at the FFI boundary.
1114 fn records(&mut self) -> Option<EngineResult<u64>> {
1115 None
1116 }
1117
1118 /// Exact number of rows counted through index `index`. Return `None` (the
1119 /// default) to use the handler base, which counts rows with an index scan;
1120 /// engines return `Some(Ok(rows))` or `Some(Err(_))`.
1121 ///
1122 /// # Errors
1123 /// The error variant is implementation-defined and maps to the matching
1124 /// `HA_ERR_*` code at the FFI boundary.
1125 fn records_from_index(&mut self, _index: u32) -> Option<EngineResult<u64>> {
1126 None
1127 }
1128
1129 /// Upper bound on the number of rows a full table scan may return. Return
1130 /// `None` (the default) to use the handler base (`stats.records` plus a
1131 /// margin); engines return `Some(rows)`.
1132 fn estimate_rows_upper_bound(&mut self) -> Option<u64> {
1133 None
1134 }
1135
1136 /// Hash value of the key columns in `field_array` for hash partitioning.
1137 /// `field_array` is a null-terminated `Field**` the binding round-trips as an
1138 /// opaque pointer valid for the call only (it cannot yet drive `Field` from
1139 /// Rust). Return `None` (the default) to use the handler base, which asserts
1140 /// — so only engines advertising hash partitioning should override and return
1141 /// `Some(hash)`.
1142 fn calculate_key_hash_value(&mut self, _field_array: *const c_void) -> Option<u32> {
1143 None
1144 }
1145
1146 /// Acquire or release a table-level lock for the session `thd`. `lock_type`
1147 /// is the raw `F_RDLCK` / `F_WRLCK` / `F_UNLCK` integer.
1148 ///
1149 /// # Errors
1150 /// The default returns `Ok(())`, matching the handler base (always succeeds).
1151 fn external_lock(&mut self, _thd: Option<&sys::THD>, _lock_type: i32) -> EngineResult {
1152 Ok(())
1153 }
1154
1155 /// Number of `THR_LOCK` entries the engine hands MySQL via `store_lock`. The
1156 /// default is `1`, matching the handler base.
1157 fn lock_count(&self) -> u32 {
1158 1
1159 }
1160
1161 /// Choose the `THR_LOCK_DATA` lock type for this handler given the server's
1162 /// proposal. `requested` is the raw `enum thr_lock_type` (`TL_READ`,
1163 /// `TL_WRITE`, …). Returning the request unchanged is the default,
1164 /// matching the canonical upgrade the shim performs for engines that do
1165 /// not override. Engines that need different priorities (e.g. always
1166 /// `TL_WRITE_LOW_PRIORITY`) can return their own choice; returning the
1167 /// `TL_IGNORE` sentinel keeps the current lock type as-is.
1168 fn store_lock(&mut self, requested: i32) -> i32 {
1169 requested
1170 }
1171
1172 /// Release the lock held on the most recently read row. The default is a
1173 /// no-op, matching the handler base.
1174 fn unlock_row(&mut self) {}
1175
1176 /// Begin a statement while the table is already locked (called instead of
1177 /// [`external_lock`](Self::external_lock) under `LOCK TABLES`). `lock_type`
1178 /// is the raw `thr_lock_type` integer.
1179 ///
1180 /// # Errors
1181 /// The default returns `Ok(())`, matching the handler base.
1182 fn start_stmt(&mut self, _thd: Option<&sys::THD>, _lock_type: i32) -> EngineResult {
1183 Ok(())
1184 }
1185
1186 /// Whether the last row was read with a semi-consistent read (skipped under
1187 /// an existing lock rather than waiting). The default is `false`, matching
1188 /// the handler base.
1189 fn was_semi_consistent_read(&mut self) -> bool {
1190 false
1191 }
1192
1193 /// Enable or disable semi-consistent reads for subsequent row reads. The
1194 /// default is a no-op, matching the handler base.
1195 fn try_semi_consistent_read(&mut self, _enable: bool) {}
1196
1197 /// Begin read-before-write removal (`HA_READ_BEFORE_WRITE_REMOVAL`). Return
1198 /// `None` (the default) to use the handler base, which asserts — only engines
1199 /// advertising the capability should override and return `Some(active)`.
1200 fn start_read_removal(&mut self) -> Option<bool> {
1201 None
1202 }
1203
1204 /// End read-before-write removal and report the number of rows actually
1205 /// written. Return `None` (the default) to use the handler base, which
1206 /// asserts; engines advertising the capability return `Some(rows)`.
1207 fn end_read_removal(&mut self) -> Option<u64> {
1208 None
1209 }
1210
1211 /// Reserve a block of auto-increment values. `offset` and `increment` define
1212 /// the value series and `nb_desired` how many values MySQL wants. Return
1213 /// `Some((first_value, nb_reserved))` to supply the block, or `None` (the
1214 /// default) to use the handler base, which derives values from table stats.
1215 fn get_auto_increment(
1216 &mut self,
1217 _offset: u64,
1218 _increment: u64,
1219 _nb_desired: u64,
1220 ) -> Option<(u64, u64)> {
1221 None
1222 }
1223
1224 /// Release auto-increment values reserved by
1225 /// [`get_auto_increment`](Self::get_auto_increment) but not used. The default
1226 /// is a no-op, matching the handler base.
1227 fn release_auto_increment(&mut self) {}
1228
1229 /// Print a diagnostic for handler error code `error` (`errflag` carries the
1230 /// `myf` formatting flags). Return `true` when the engine emitted its own
1231 /// message; `false` (the default) lets the handler base print the standard
1232 /// `HA_ERR_*` diagnostic.
1233 fn print_error(&mut self, _error: i32, _errflag: u64) -> bool {
1234 false
1235 }
1236
1237 /// Engine-specific message for handler error code `error`, paired with a
1238 /// flag marking the error as transient. Return `Some((message, temporary))`
1239 /// to surface `message` to the client — formatted as a temporary error when
1240 /// `temporary` is `true` — or `None` (the default) to use the handler base
1241 /// (no engine message).
1242 fn error_message(&mut self, _error: i32) -> Option<(String, bool)> {
1243 None
1244 }
1245
1246 /// Names of the child table and key for the most recent
1247 /// `HA_ERR_FOREIGN_DUPLICATE_KEY`. Return `Some((table, key))` to report
1248 /// them, or `None` (the default) to use the handler base (names unavailable).
1249 fn foreign_dup_key(&mut self) -> Option<(String, String)> {
1250 None
1251 }
1252
1253 /// Whether handler error code `error` may be ignored (e.g. duplicate-key
1254 /// under `INSERT IGNORE`). Return `None` (the default) to use the handler
1255 /// base classification; engines return `Some(flag)` to override it.
1256 fn is_ignorable_error(&mut self, _error: i32) -> Option<bool> {
1257 None
1258 }
1259
1260 /// Whether handler error code `error` is fatal to the running statement.
1261 /// Return `None` (the default) to use the handler base classification;
1262 /// engines return `Some(flag)` to override it.
1263 fn is_fatal_error(&mut self, _error: i32) -> Option<bool> {
1264 None
1265 }
1266
1267 /// Perform an `HA_EXTRA_*` hint operation (`operation` is the raw
1268 /// `ha_extra_function` integer). Hints are advisory.
1269 ///
1270 /// # Errors
1271 /// The default returns `Ok(())`, matching the handler base (hints ignored).
1272 fn extra(&mut self, _operation: i32) -> EngineResult {
1273 Ok(())
1274 }
1275
1276 /// Perform an `HA_EXTRA_*` hint with a size argument (`cache_size`). The
1277 /// default forwards to [`extra`](Self::extra), matching the handler base.
1278 ///
1279 /// # Errors
1280 /// Propagates whatever [`extra`](Self::extra) returns.
1281 fn extra_opt(&mut self, operation: i32, _cache_size: u64) -> EngineResult {
1282 self.extra(operation)
1283 }
1284
1285 /// Reset per-statement state so the handler can be reused for the next
1286 /// statement (clears hints, range state, etc.).
1287 ///
1288 /// # Errors
1289 /// The default returns `Ok(())`, matching the handler base.
1290 fn reset(&mut self) -> EngineResult {
1291 Ok(())
1292 }
1293
1294 /// Notify the engine that MySQL changed the read/write column bitmaps. The
1295 /// default is a no-op, matching the handler base.
1296 fn column_bitmaps_signal(&mut self) {}
1297
1298 /// Prepare engine state for use through the SQL `HANDLER` interface. The
1299 /// default is a no-op, matching the handler base.
1300 fn init_table_handle_for_handler(&mut self) {}
1301
1302 /// Report which in-place `ALTER TABLE` algorithm the engine supports for the
1303 /// change described by `alter_info` on `altered_table`, as the raw
1304 /// `enum_alter_inplace_result` integer. Return `None` (the default) to use
1305 /// the handler base, which classifies the change from the alter flags;
1306 /// engines return `Some(result)` to override.
1307 fn check_if_supported_inplace_alter(
1308 &mut self,
1309 _altered_table: Option<&sys::TABLE>,
1310 _alter_info: Option<&sys::AlterInplaceInfo>,
1311 ) -> Option<i32> {
1312 None
1313 }
1314
1315 /// Prepare an in-place `ALTER TABLE` (allocate resources, validate) before
1316 /// the change is applied. Return `Some(true)` on error, `Some(false)` on
1317 /// success, or `None` (the default) to use the handler base (success).
1318 fn prepare_inplace_alter_table(
1319 &mut self,
1320 _altered_table: Option<&sys::TABLE>,
1321 _alter_info: Option<&sys::AlterInplaceInfo>,
1322 _old_table_def: Option<&sys::DdTable>,
1323 _new_table_def: Option<&sys::DdTable>,
1324 ) -> Option<bool> {
1325 None
1326 }
1327
1328 /// Apply an in-place `ALTER TABLE` change. Return `Some(true)` on error,
1329 /// `Some(false)` on success, or `None` (the default) to use the handler base
1330 /// (success / no-op).
1331 fn inplace_alter_table(
1332 &mut self,
1333 _altered_table: Option<&sys::TABLE>,
1334 _alter_info: Option<&sys::AlterInplaceInfo>,
1335 _old_table_def: Option<&sys::DdTable>,
1336 _new_table_def: Option<&sys::DdTable>,
1337 ) -> Option<bool> {
1338 None
1339 }
1340
1341 /// Commit (`commit == true`) or roll back an in-place `ALTER TABLE`. Return
1342 /// `Some(true)` on error, `Some(false)` on success, or `None` (the default)
1343 /// to use the handler base, which clears the group-commit context.
1344 fn commit_inplace_alter_table(
1345 &mut self,
1346 _altered_table: Option<&sys::TABLE>,
1347 _alter_info: Option<&sys::AlterInplaceInfo>,
1348 _commit: bool,
1349 _old_table_def: Option<&sys::DdTable>,
1350 _new_table_def: Option<&sys::DdTable>,
1351 ) -> Option<bool> {
1352 None
1353 }
1354
1355 /// Notify the engine that an in-place `ALTER TABLE` finished and the table
1356 /// definition was updated. The default is a no-op, matching the handler
1357 /// base. No error may be reported here.
1358 fn notify_table_changed(&mut self, _alter_info: Option<&sys::AlterInplaceInfo>) {}
1359
1360 /// Whether the create options in `create_info` (with `table_changes` flags)
1361 /// are incompatible with the existing data, for the deprecated copy-based
1362 /// ALTER path. Return `None` (the default) to use the handler base
1363 /// (`COMPATIBLE_DATA_NO`, i.e. incompatible); engines return `Some(flag)`.
1364 fn check_if_incompatible_data(
1365 &mut self,
1366 _create_info: Option<&sys::HA_CREATE_INFO>,
1367 _table_changes: u32,
1368 ) -> Option<bool> {
1369 None
1370 }
1371
1372 /// Run `CHECK TABLE` for `check_opt`, returning a raw `HA_ADMIN_*` code.
1373 /// Return `None` (the default) to use the handler base
1374 /// (`HA_ADMIN_NOT_IMPLEMENTED`); engines return `Some(code)`.
1375 fn check(
1376 &mut self,
1377 _thd: Option<&sys::THD>,
1378 _check_opt: Option<&sys::HaCheckOpt>,
1379 ) -> Option<i32> {
1380 None
1381 }
1382
1383 /// Run `REPAIR TABLE` for `check_opt`, returning a raw `HA_ADMIN_*` code.
1384 /// Return `None` (the default) to use the handler base
1385 /// (`HA_ADMIN_NOT_IMPLEMENTED`); engines that advertise `HA_CAN_REPAIR`
1386 /// return `Some(code)`.
1387 fn repair(
1388 &mut self,
1389 _thd: Option<&sys::THD>,
1390 _check_opt: Option<&sys::HaCheckOpt>,
1391 ) -> Option<i32> {
1392 None
1393 }
1394
1395 /// Run `OPTIMIZE TABLE` for `check_opt`, returning a raw `HA_ADMIN_*` code.
1396 /// Return `None` (the default) to use the handler base
1397 /// (`HA_ADMIN_NOT_IMPLEMENTED`); engines return `Some(code)`.
1398 fn optimize(
1399 &mut self,
1400 _thd: Option<&sys::THD>,
1401 _check_opt: Option<&sys::HaCheckOpt>,
1402 ) -> Option<i32> {
1403 None
1404 }
1405
1406 /// Run `ANALYZE TABLE` for `check_opt`, returning a raw `HA_ADMIN_*` code.
1407 /// Return `None` (the default) to use the handler base
1408 /// (`HA_ADMIN_NOT_IMPLEMENTED`); engines return `Some(code)`.
1409 fn analyze(
1410 &mut self,
1411 _thd: Option<&sys::THD>,
1412 _check_opt: Option<&sys::HaCheckOpt>,
1413 ) -> Option<i32> {
1414 None
1415 }
1416
1417 /// Check and, if needed, repair the table on crash recovery. Return
1418 /// `Some(true)` on error / not supported, `Some(false)` on success, or
1419 /// `None` (the default) to use the handler base (`true`).
1420 fn check_and_repair(&mut self, _thd: Option<&sys::THD>) -> Option<bool> {
1421 None
1422 }
1423
1424 /// Check whether the table needs upgrading, returning a raw `HA_ADMIN_*`
1425 /// code. Return `None` (the default) to use the handler base (`0`, no
1426 /// upgrade needed); engines return `Some(code)`.
1427 fn check_for_upgrade(&mut self, _check_opt: Option<&sys::HaCheckOpt>) -> Option<i32> {
1428 None
1429 }
1430
1431 /// Preload indexes into a named key cache (`ASSIGN_TO_KEYCACHE`), returning a
1432 /// raw `HA_ADMIN_*` code. Return `None` (the default) to use the handler base
1433 /// (`HA_ADMIN_NOT_IMPLEMENTED`); engines return `Some(code)`.
1434 fn assign_to_keycache(
1435 &mut self,
1436 _thd: Option<&sys::THD>,
1437 _check_opt: Option<&sys::HaCheckOpt>,
1438 ) -> Option<i32> {
1439 None
1440 }
1441
1442 /// Preload index blocks into the default key cache (`LOAD INDEX`), returning
1443 /// a raw `HA_ADMIN_*` code. Return `None` (the default) to use the handler
1444 /// base (`HA_ADMIN_NOT_IMPLEMENTED`); engines return `Some(code)`.
1445 fn preload_keys(
1446 &mut self,
1447 _thd: Option<&sys::THD>,
1448 _check_opt: Option<&sys::HaCheckOpt>,
1449 ) -> Option<i32> {
1450 None
1451 }
1452
1453 /// Disable indexes in the given `mode` (`ALTER TABLE ... DISABLE KEYS`),
1454 /// returning a raw handler code. Return `None` (the default) to use the
1455 /// handler base (`HA_ERR_WRONG_COMMAND`); engines return `Some(code)`.
1456 fn disable_indexes(&mut self, _mode: u32) -> Option<i32> {
1457 None
1458 }
1459
1460 /// Enable indexes in the given `mode` (`ALTER TABLE ... ENABLE KEYS`),
1461 /// returning a raw handler code. Return `None` (the default) to use the
1462 /// handler base (`HA_ERR_WRONG_COMMAND`); engines return `Some(code)`.
1463 fn enable_indexes(&mut self, _mode: u32) -> Option<i32> {
1464 None
1465 }
1466
1467 /// Discard (`discard == true`) or import the tablespace for `table_def`,
1468 /// returning a raw handler code. Return `None` (the default) to use the
1469 /// handler base (`HA_ERR_WRONG_COMMAND`); engines return `Some(code)`.
1470 fn discard_or_import_tablespace(
1471 &mut self,
1472 _discard: bool,
1473 _table_def: Option<&sys::DdTable>,
1474 ) -> Option<i32> {
1475 None
1476 }
1477
1478 /// Offer the WHERE condition `cond` (an opaque `Item *` the binding
1479 /// round-trips without dereference) for engine-side evaluation. Return the
1480 /// part the engine will *not* handle: `cond` (the default) means no
1481 /// pushdown, a null pointer means the engine took the whole condition.
1482 /// Engines cannot yet construct `Item`s, so only pass-through or null are
1483 /// expressible.
1484 fn cond_push(&mut self, cond: *const c_void) -> *const c_void {
1485 cond
1486 }
1487
1488 /// Offer the index condition `idx_cond` on index `keyno` for engine-side
1489 /// evaluation (an opaque `Item *` the binding round-trips without
1490 /// dereference). Return the part not handled: `idx_cond` (the default) means
1491 /// no pushdown, null means fully handled.
1492 fn idx_cond_push(&mut self, _keyno: u32, idx_cond: *mut c_void) -> *mut c_void {
1493 idx_cond
1494 }
1495
1496 /// Discard any index condition previously accepted via
1497 /// [`idx_cond_push`](Self::idx_cond_push). The default is a no-op; the shim
1498 /// always resets the handler base's pushed-condition state regardless.
1499 fn cancel_pushed_idx_cond(&mut self) {}
1500
1501 /// The `handlerton *` of the secondary engine this handler can push work
1502 /// down to, as an opaque pointer. Return null (the default) when the engine
1503 /// supports no pushdown; round-trip a handlerton pointer otherwise.
1504 fn hton_supporting_engine_pushdown(&mut self) -> *const c_void {
1505 core::ptr::null()
1506 }
1507
1508 /// Number of joins pushed down to the engine for the current query. The
1509 /// default is `0`, matching the handler base.
1510 fn number_of_pushed_joins(&self) -> u32 {
1511 0
1512 }
1513
1514 /// The `TABLE *` of this handler's member in a pushed join, as an opaque
1515 /// pointer, or null (the default) when not part of a pushed join.
1516 fn member_of_pushed_join(&self) -> *const c_void {
1517 core::ptr::null()
1518 }
1519
1520 /// The `TABLE *` of the root of this handler's pushed join, as an opaque
1521 /// pointer, or null (the default) when not part of a pushed join.
1522 fn parent_of_pushed_join(&self) -> *const c_void {
1523 core::ptr::null()
1524 }
1525
1526 /// Bitmap (`table_map`) of the tables in this handler's pushed join. The
1527 /// default is `0`, matching the handler base.
1528 fn tables_in_pushed_join(&self) -> u64 {
1529 0
1530 }
1531
1532 /// Populate engine-specific fields of `create_info` (an opaque MySQL
1533 /// `HA_CREATE_INFO`) before `SHOW CREATE TABLE`. The default is a no-op,
1534 /// matching the handler base; the binding cannot mutate `HA_CREATE_INFO`
1535 /// from Rust yet, so this is a notification.
1536 fn update_create_info(&mut self, _create_info: Option<&sys::HA_CREATE_INFO>) {}
1537
1538 /// Engine-specific text appended to the `CREATE TABLE` statement (after the
1539 /// closing paren). Return `Some(text)` to append it, or `None` (the default)
1540 /// to append nothing, matching the handler base.
1541 fn append_create_info(&mut self) -> Option<String> {
1542 None
1543 }
1544
1545 /// Prepare the handler to position rows by a hidden primary key. The default
1546 /// is a no-op notification; the shim always runs the handler base, which
1547 /// sets up the hidden-key iteration state.
1548 fn use_hidden_primary_key(&mut self) {}
1549
1550 /// Adopt the shared `Handler_share` state (`arg` is an opaque
1551 /// `Handler_share **` the binding round-trips). Return `Some(false)` on
1552 /// success, `Some(true)` on error, or `None` (the default) to use the handler
1553 /// base, which stores the reference for cross-handler sharing.
1554 fn set_ha_share_ref(&mut self, _arg: *mut c_void) -> Option<bool> {
1555 None
1556 }
1557
1558 /// Compare two row-position references `ref1` and `ref2` (each the handler's
1559 /// `ref_length` bytes). Return `None` (the default) to use the handler base
1560 /// (`memcmp`); engines with a structured position return
1561 /// `Some(ordering)`.
1562 fn cmp_ref(&mut self, _ref1: &[u8], _ref2: &[u8]) -> Option<core::cmp::Ordering> {
1563 None
1564 }
1565
1566 /// Record `reason` as the error to raise for a failed external (secondary)
1567 /// engine offload. The default is a no-op, matching the handler base.
1568 fn set_external_table_offload_error(&mut self, _reason: &str) {}
1569
1570 /// Raise the error previously recorded by
1571 /// [`set_external_table_offload_error`](Self::set_external_table_offload_error).
1572 /// The default is a no-op, matching the handler base.
1573 fn external_table_offload_error(&self) {}
1574
1575 /// Create a clone of this handler for `name` allocated in `mem_root` (an
1576 /// opaque `MEM_ROOT *`), returning an opaque `handler *`. Return a null
1577 /// pointer (the default) to use the handler base, which builds a fresh
1578 /// handler of the same type — engines cannot construct a `handler` from Rust.
1579 fn clone_handler(&mut self, _name: &str, _mem_root: *mut c_void) -> *mut c_void {
1580 core::ptr::null_mut()
1581 }
1582
1583 /// Capacity for multi-valued index keys as `(max_keys, max_total_bytes)`.
1584 /// Return `None` (the default) to use the handler base (`(0, 0)`, no
1585 /// multi-valued index support); engines return `Some((keys, bytes))`.
1586 fn mv_key_capacity(&self) -> Option<(u32, u64)> {
1587 None
1588 }
1589
1590 /// The engine's `Partition_handler *` as an opaque pointer, or null (the
1591 /// default) when the engine does not implement native partitioning.
1592 fn get_partition_handler(&mut self) -> *mut c_void {
1593 core::ptr::null_mut()
1594 }
1595}