Skip to main content

mysql_handler/dd/
index.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//! [`DdIndex`] / [`DdIndexElement`] safe accessors and the [`IndexType`] /
24//! [`IndexElementOrder`] enums.
25
26#![allow(unsafe_code)]
27
28use crate::dd::ffi;
29use crate::sys::{DdIndex, DdIndexElement};
30
31/// MySQL index type. Mirrors `dd::Index::enum_index_type`.
32#[non_exhaustive]
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[allow(missing_docs)] // Variants mirror upstream `dd::Index::enum_index_type` 1:1.
35pub enum IndexType {
36    Primary,
37    Unique,
38    Multiple,
39    FullText,
40    Spatial,
41    /// Unknown / out-of-range value.
42    Unknown,
43}
44
45impl IndexType {
46    /// Map the raw `dd::Index::enum_index_type` integer to an [`IndexType`].
47    #[must_use]
48    pub const fn from_raw(raw: i32) -> Self {
49        match raw {
50            1 => Self::Primary,
51            2 => Self::Unique,
52            3 => Self::Multiple,
53            4 => Self::FullText,
54            5 => Self::Spatial,
55            _ => Self::Unknown,
56        }
57    }
58}
59
60/// Sort order of an index element. Mirrors
61/// `dd::Index_element::enum_index_element_order`.
62#[non_exhaustive]
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64#[allow(missing_docs)] // Variants mirror upstream `enum_index_element_order` 1:1.
65pub enum IndexElementOrder {
66    Undefined,
67    Ascending,
68    Descending,
69    /// Unknown / out-of-range value.
70    Unknown,
71}
72
73impl IndexElementOrder {
74    /// Map the raw order integer to an [`IndexElementOrder`].
75    #[must_use]
76    pub const fn from_raw(raw: i32) -> Self {
77        match raw {
78            1 => Self::Undefined,
79            2 => Self::Ascending,
80            3 => Self::Descending,
81            _ => Self::Unknown,
82        }
83    }
84}
85
86impl DdIndex {
87    /// Index name as stored in the data dictionary (`PRIMARY` for the
88    /// primary key).
89    #[must_use]
90    pub fn name(&self) -> String {
91        let p: *const DdIndex = self;
92        // SAFETY: `self` is a valid borrow.
93        ffi::read_name(|buf, cap| unsafe { ffi::mysql__DdIndex__name(p, buf, cap) })
94    }
95
96    /// Index kind (primary / unique / non-unique / fulltext / spatial).
97    #[must_use]
98    pub fn index_type(&self) -> IndexType {
99        let p: *const DdIndex = self;
100        // SAFETY: `self` is a valid borrow.
101        let raw = unsafe { ffi::mysql__DdIndex__type(p) };
102        IndexType::from_raw(raw)
103    }
104
105    /// `true` for indexes the SE itself added and is not exposed via SQL.
106    #[must_use]
107    pub fn is_hidden(&self) -> bool {
108        let p: *const DdIndex = self;
109        // SAFETY: `self` is a valid borrow.
110        unsafe { ffi::mysql__DdIndex__is_hidden(p) }
111    }
112
113    /// Number of key parts in the index.
114    #[must_use]
115    pub fn element_count(&self) -> usize {
116        let p: *const DdIndex = self;
117        // SAFETY: `self` is a valid borrow.
118        unsafe { ffi::mysql__DdIndex__element_count(p) }
119    }
120
121    /// Borrow the `i`th key part (0-based). Returns `None` past the end.
122    #[must_use]
123    pub fn element_at(&self, i: usize) -> Option<&DdIndexElement> {
124        let p: *const DdIndex = self;
125        // SAFETY: `self` is a valid borrow; the FFI returns null when `i`
126        // is out of range or `p` is null.
127        let raw = unsafe { ffi::mysql__DdIndex__element_at(p, i) };
128        // SAFETY: a non-null result references a key part owned by the same
129        // dd::Table tree as `self` and is valid for `self`'s lifetime.
130        unsafe { raw.as_ref() }
131    }
132}
133
134impl DdIndexElement {
135    /// 1-based ordinal position of the underlying column in the table.
136    #[must_use]
137    pub fn column_ordinal(&self) -> u32 {
138        let p: *const DdIndexElement = self;
139        // SAFETY: `self` is a valid borrow.
140        unsafe { ffi::mysql__DdIndexElement__column_ordinal(p) }
141    }
142
143    /// Prefix length in bytes (0 means the whole column).
144    #[must_use]
145    pub fn length(&self) -> u32 {
146        let p: *const DdIndexElement = self;
147        // SAFETY: `self` is a valid borrow.
148        unsafe { ffi::mysql__DdIndexElement__length(p) }
149    }
150
151    /// Sort order declared on this key part.
152    #[must_use]
153    pub fn order(&self) -> IndexElementOrder {
154        let p: *const DdIndexElement = self;
155        // SAFETY: `self` is a valid borrow.
156        let raw = unsafe { ffi::mysql__DdIndexElement__order(p) };
157        IndexElementOrder::from_raw(raw)
158    }
159
160    /// `true` for key parts the SE added that are not exposed via SQL.
161    #[must_use]
162    pub fn is_hidden(&self) -> bool {
163        let p: *const DdIndexElement = self;
164        // SAFETY: `self` is a valid borrow.
165        unsafe { ffi::mysql__DdIndexElement__is_hidden(p) }
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::{IndexElementOrder, IndexType};
172
173    #[test]
174    fn index_type_from_raw_maps_known_variants() {
175        assert_eq!(IndexType::from_raw(1), IndexType::Primary);
176        assert_eq!(IndexType::from_raw(2), IndexType::Unique);
177        assert_eq!(IndexType::from_raw(3), IndexType::Multiple);
178        assert_eq!(IndexType::from_raw(4), IndexType::FullText);
179        assert_eq!(IndexType::from_raw(5), IndexType::Spatial);
180    }
181
182    #[test]
183    fn index_type_from_raw_returns_unknown_for_out_of_range() {
184        assert_eq!(IndexType::from_raw(0), IndexType::Unknown);
185        assert_eq!(IndexType::from_raw(99), IndexType::Unknown);
186    }
187
188    #[test]
189    fn order_from_raw_maps_known_variants() {
190        assert_eq!(IndexElementOrder::from_raw(1), IndexElementOrder::Undefined);
191        assert_eq!(IndexElementOrder::from_raw(2), IndexElementOrder::Ascending);
192        assert_eq!(
193            IndexElementOrder::from_raw(3),
194            IndexElementOrder::Descending
195        );
196        assert_eq!(IndexElementOrder::from_raw(4), IndexElementOrder::Unknown);
197    }
198}