Skip to main content

drizzle_mysql/
index.rs

1//! `MySQL` index options (method, online-DDL algorithm, lock) and key parts,
2//! as declared with `#[MySQLIndex]` and read by migrations.
3
4/// Storage/access method requested by a generated MySQL index.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum MySQLIndexMethod {
7    /// Balanced-tree index.
8    BTree,
9    /// Hash index.
10    Hash,
11}
12
13impl MySQLIndexMethod {
14    /// Returns the MySQL keyword for this method.
15    #[must_use]
16    pub const fn as_sql(self) -> &'static str {
17        match self {
18            Self::BTree => "BTREE",
19            Self::Hash => "HASH",
20        }
21    }
22}
23
24/// Online-DDL algorithm requested for a generated MySQL index.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub enum MySQLIndexAlgorithm {
27    /// Lets MySQL choose the DDL algorithm.
28    Default,
29    /// Requests in-place DDL.
30    Inplace,
31    /// Requests table-copy DDL.
32    Copy,
33}
34
35impl MySQLIndexAlgorithm {
36    /// Returns the MySQL keyword for this algorithm.
37    #[must_use]
38    pub const fn as_sql(self) -> &'static str {
39        match self {
40            Self::Default => "DEFAULT",
41            Self::Inplace => "INPLACE",
42            Self::Copy => "COPY",
43        }
44    }
45}
46
47/// Metadata-lock policy requested for a generated MySQL index.
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub enum MySQLIndexLock {
50    /// Lets MySQL choose the metadata-lock policy.
51    Default,
52    /// Permits concurrent reads and writes when supported.
53    None,
54    /// Permits concurrent reads but blocks writes.
55    Shared,
56    /// Blocks concurrent access.
57    Exclusive,
58}
59
60impl MySQLIndexLock {
61    /// Returns the MySQL keyword for this lock policy.
62    #[must_use]
63    pub const fn as_sql(self) -> &'static str {
64        match self {
65            Self::Default => "DEFAULT",
66            Self::None => "NONE",
67            Self::Shared => "SHARED",
68            Self::Exclusive => "EXCLUSIVE",
69        }
70    }
71}
72
73/// Sort direction for one MySQL index key part.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub enum IndexOrder {
76    /// Ascending order.
77    Asc,
78    /// Descending order.
79    Desc,
80}
81
82/// One column or trusted SQL expression in a generated MySQL index.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84pub enum IndexKeyPart {
85    /// A named table column.
86    Column {
87        /// Physical column name.
88        name: &'static str,
89        /// Optional prefix length.
90        length: Option<u32>,
91        /// Optional key-part direction.
92        order: Option<IndexOrder>,
93    },
94    /// A trusted functional-index SQL expression.
95    Expression {
96        /// Raw expression SQL without the outer MySQL key-part parentheses.
97        sql: &'static str,
98        /// Optional key-part direction.
99        order: Option<IndexOrder>,
100    },
101}
102
103/// SQL type marker accepted by a prefix-length MySQL index key part.
104///
105/// This trait is public only so `#[MySQLIndex]` can validate generated column
106/// types in downstream crates.
107#[doc(hidden)]
108#[diagnostic::on_unimplemented(
109    message = "`{Self}` does not support a MySQL index prefix length",
110    note = "prefix lengths are supported for character, binary, TEXT, and BLOB columns"
111)]
112pub trait IndexPrefixType {}
113
114/// SQL type marker accepted by a MySQL index key part without a prefix.
115///
116/// This trait is public only so generated custom-column implementations can
117/// preserve the same indexability checks as built-in columns.
118#[doc(hidden)]
119#[diagnostic::on_unimplemented(
120    message = "`{Self}` cannot be used directly in a MySQL index",
121    note = "TEXT and BLOB columns need a prefix length; JSON needs an indexed scalar expression"
122)]
123pub trait IndexType {}
124
125macro_rules! impl_index_types {
126    ($trait:ident: $($type:ty),+ $(,)?) => {
127        $(impl $trait for $type {})+
128    };
129}
130
131impl_index_types! { IndexPrefixType:
132    crate::types::Char,
133    crate::types::Varchar,
134    crate::types::TinyText,
135    crate::types::Text,
136    crate::types::MediumText,
137    crate::types::LongText,
138    crate::types::Binary,
139    crate::types::Varbinary,
140    crate::types::TinyBlob,
141    crate::types::Blob,
142    crate::types::MediumBlob,
143    crate::types::LongBlob,
144}
145
146impl_index_types! { IndexType:
147    crate::types::TinyInt,
148    crate::types::TinyIntUnsigned,
149    crate::types::SmallInt,
150    crate::types::SmallIntUnsigned,
151    crate::types::MediumInt,
152    crate::types::MediumIntUnsigned,
153    crate::types::Int,
154    crate::types::IntUnsigned,
155    crate::types::BigInt,
156    crate::types::BigIntUnsigned,
157    crate::types::Float,
158    crate::types::Double,
159    crate::types::Decimal,
160    crate::types::Boolean,
161    crate::types::Char,
162    crate::types::Varchar,
163    crate::types::Binary,
164    crate::types::Varbinary,
165    crate::types::Date,
166    crate::types::Time,
167    crate::types::DateTime,
168    crate::types::Timestamp,
169    crate::types::Year,
170    crate::types::Enum,
171    crate::types::Set,
172    crate::types::Bit,
173}
174
175/// Internal sealing marker for generated MySQL schema items.
176///
177/// The module is public only because procedural-macro output is expanded in a
178/// downstream crate. Consumers should use [`MySQLSchemaItemMetadata`] rather
179/// than implementing this marker themselves.
180#[doc(hidden)]
181pub mod __private {
182    pub trait MySQLSchemaItemSealed {}
183}
184
185/// Inline MySQL column-type metadata retained separately from rendered SQL.
186///
187/// `ENUM` and `SET` values affect migration safety analysis, so schema
188/// producers carry them as values instead of reverse-engineering a generated
189/// DDL string.
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
191pub enum MySQLInlineTypeMetadata {
192    /// Inline `ENUM` values in declaration order.
193    Enum(&'static [&'static str]),
194    /// Inline `SET` values in declaration order.
195    Set(&'static [&'static str]),
196}
197
198/// MySQL metadata shared by table and index schema items.
199///
200/// Migration snapshot producers inspect this trait rather than reparsing an
201/// item's generated SQL. Tables use the default empty index settings; index
202/// derives provide their declared method, online-DDL algorithm, and lock.
203pub trait MySQLSchemaItemMetadata: __private::MySQLSchemaItemSealed {
204    /// Ordered key parts when this item is an index.
205    const INDEX_KEY_PARTS: &'static [IndexKeyPart] = &[];
206    /// Optional index access method.
207    const INDEX_METHOD: Option<MySQLIndexMethod> = None;
208    /// Optional online-DDL algorithm.
209    const INDEX_ALGORITHM: Option<MySQLIndexAlgorithm> = None;
210    /// Optional metadata-lock policy.
211    const INDEX_LOCK: Option<MySQLIndexLock> = None;
212
213    /// Inline type values for a named table column, if this item is a table
214    /// with an `ENUM` or `SET` declaration.
215    fn inline_type(_column: &str) -> Option<MySQLInlineTypeMetadata> {
216        None
217    }
218
219    /// A MySQL `COMMENT` attached to a named table column.
220    ///
221    /// This stays separate from generated DDL so migration snapshots can
222    /// distinguish a comment change from a column-definition change.
223    fn column_comment(_column: &str) -> Option<&'static str> {
224        None
225    }
226}