mysql_handler/hton/cost_constants.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-specific optimizer cost constants.
24
25/// Engine-specific cost constants the handlerton returns from
26/// [`get_cost_constants`](crate::hton::Handlerton::get_cost_constants).
27///
28/// Both fields mirror the per-storage-engine values MySQL stores in the
29/// `mysql.engine_cost` table; the defaults match what
30/// `SE_cost_constants(Optimizer::kOriginal)` would have produced on the
31/// C++ side, so an engine that supplies only one field starts from a sane
32/// fallback for the other.
33#[derive(Debug, Clone, Copy, PartialEq)]
34#[non_exhaustive]
35pub struct CostConstants {
36 memory_block_read_cost: f64,
37 io_block_read_cost: f64,
38}
39
40impl CostConstants {
41 /// Construct a cost-constants tuple with the given per-block read
42 /// costs. Both values must be positive; MySQL's
43 /// `SE_cost_constants::set` rejects zero or negative values with
44 /// `INVALID_COST_VALUE`, so the shim does the same and an engine
45 /// returning either value reverts to the defaults.
46 #[must_use]
47 pub const fn new(memory_block_read_cost: f64, io_block_read_cost: f64) -> Self {
48 Self {
49 memory_block_read_cost,
50 io_block_read_cost,
51 }
52 }
53
54 /// The defaults `SE_cost_constants(Optimizer::kOriginal)` initialises
55 /// — `memory_block_read_cost = 0.25`, `io_block_read_cost = 1.0`.
56 /// Engines that want to override one field commonly start from these.
57 #[must_use]
58 pub const fn defaults() -> Self {
59 Self::new(0.25, 1.0)
60 }
61
62 /// Cost of reading one random block from an in-memory buffer.
63 #[must_use]
64 pub const fn memory_block_read_cost(&self) -> f64 {
65 self.memory_block_read_cost
66 }
67
68 /// Cost of reading one random block from disk.
69 #[must_use]
70 pub const fn io_block_read_cost(&self) -> f64 {
71 self.io_block_read_cost
72 }
73}
74
75impl Default for CostConstants {
76 fn default() -> Self {
77 Self::defaults()
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn defaults_match_se_cost_constants_original() {
87 // Values pinned to mysql-server's
88 // `SE_cost_constants(Optimizer::kOriginal)` initialiser.
89 let c = CostConstants::defaults();
90 assert!((c.memory_block_read_cost() - 0.25).abs() < f64::EPSILON);
91 assert!((c.io_block_read_cost() - 1.0).abs() < f64::EPSILON);
92 }
93
94 #[test]
95 fn new_preserves_values() {
96 let c = CostConstants::new(0.5, 2.5);
97 assert!((c.memory_block_read_cost() - 0.5).abs() < f64::EPSILON);
98 assert!((c.io_block_read_cost() - 2.5).abs() < f64::EPSILON);
99 }
100}