reinhardt_query/query/create_index.rs
1//! CREATE INDEX statement builder
2//!
3//! This module provides the `CreateIndexStatement` type for building SQL CREATE INDEX queries.
4
5use crate::{
6 backend::QueryBuilder,
7 expr::SimpleExpr,
8 types::{DynIden, IntoIden, IntoTableRef, Order, TableRef},
9};
10
11use super::traits::{QueryBuilderTrait, QueryStatementBuilder, QueryStatementWriter};
12
13/// CREATE INDEX statement builder
14///
15/// This struct provides a fluent API for constructing CREATE INDEX queries.
16///
17/// # Examples
18///
19/// ```rust,ignore
20/// use reinhardt_query::prelude::*;
21///
22/// let query = Query::create_index()
23/// .name("idx_email")
24/// .table("users")
25/// .col("email")
26/// .unique();
27/// ```
28#[derive(Debug, Clone)]
29pub struct CreateIndexStatement {
30 pub(crate) name: Option<DynIden>,
31 pub(crate) table: Option<TableRef>,
32 pub(crate) columns: Vec<IndexColumn>,
33 pub(crate) unique: bool,
34 pub(crate) if_not_exists: bool,
35 pub(crate) r#where: Option<SimpleExpr>,
36 pub(crate) using: Option<IndexMethod>,
37}
38
39/// Index column specification
40///
41/// This struct represents a column in an index, including its name and sort order.
42#[derive(Debug, Clone)]
43pub struct IndexColumn {
44 pub(crate) name: DynIden,
45 pub(crate) order: Option<Order>,
46}
47
48/// Index method (PostgreSQL and MySQL)
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[non_exhaustive]
51pub enum IndexMethod {
52 /// BTREE - B-Tree index (default for most databases)
53 BTree,
54 /// HASH - Hash index
55 Hash,
56 /// GIST - Generalized Search Tree (PostgreSQL)
57 Gist,
58 /// GIN - Generalized Inverted Index (PostgreSQL)
59 Gin,
60 /// BRIN - Block Range Index (PostgreSQL)
61 Brin,
62 /// FULLTEXT - Full-text index (MySQL)
63 FullText,
64 /// SPATIAL - Spatial index (MySQL)
65 Spatial,
66}
67
68impl IndexMethod {
69 /// Get the SQL keyword for this index method
70 pub fn as_str(&self) -> &'static str {
71 match self {
72 Self::BTree => "BTREE",
73 Self::Hash => "HASH",
74 Self::Gist => "GIST",
75 Self::Gin => "GIN",
76 Self::Brin => "BRIN",
77 Self::FullText => "FULLTEXT",
78 Self::Spatial => "SPATIAL",
79 }
80 }
81}
82
83impl CreateIndexStatement {
84 /// Create a new CREATE INDEX statement
85 pub fn new() -> Self {
86 Self {
87 name: None,
88 table: None,
89 columns: Vec::new(),
90 unique: false,
91 if_not_exists: false,
92 r#where: None,
93 using: None,
94 }
95 }
96
97 /// Take the ownership of data in the current [`CreateIndexStatement`]
98 pub fn take(&mut self) -> Self {
99 Self {
100 name: self.name.take(),
101 table: self.table.take(),
102 columns: std::mem::take(&mut self.columns),
103 unique: self.unique,
104 if_not_exists: self.if_not_exists,
105 r#where: self.r#where.take(),
106 using: self.using.take(),
107 }
108 }
109
110 /// Set the index name
111 ///
112 /// # Examples
113 ///
114 /// ```rust,ignore
115 /// use reinhardt_query::prelude::*;
116 ///
117 /// let query = Query::create_index()
118 /// .name("idx_email");
119 /// ```
120 pub fn name<T>(&mut self, name: T) -> &mut Self
121 where
122 T: IntoIden,
123 {
124 self.name = Some(name.into_iden());
125 self
126 }
127
128 /// Set the table
129 ///
130 /// # Examples
131 ///
132 /// ```rust,ignore
133 /// use reinhardt_query::prelude::*;
134 ///
135 /// let query = Query::create_index()
136 /// .name("idx_email")
137 /// .table("users");
138 /// ```
139 pub fn table<T>(&mut self, tbl: T) -> &mut Self
140 where
141 T: IntoTableRef,
142 {
143 self.table = Some(tbl.into_table_ref());
144 self
145 }
146
147 /// Add a column to the index
148 ///
149 /// # Examples
150 ///
151 /// ```rust,ignore
152 /// use reinhardt_query::prelude::*;
153 ///
154 /// let query = Query::create_index()
155 /// .name("idx_name_email")
156 /// .table("users")
157 /// .col("name")
158 /// .col("email");
159 /// ```
160 pub fn col<C>(&mut self, column: C) -> &mut Self
161 where
162 C: IntoIden,
163 {
164 self.columns.push(IndexColumn {
165 name: column.into_iden(),
166 order: None,
167 });
168 self
169 }
170
171 /// Add a column with sort order
172 ///
173 /// # Examples
174 ///
175 /// ```rust,ignore
176 /// use reinhardt_query::prelude::*;
177 /// use reinhardt_query::types::Order;
178 ///
179 /// let query = Query::create_index()
180 /// .name("idx_created_at")
181 /// .table("posts")
182 /// .col_order("created_at", Order::Desc);
183 /// ```
184 pub fn col_order<C>(&mut self, column: C, order: Order) -> &mut Self
185 where
186 C: IntoIden,
187 {
188 self.columns.push(IndexColumn {
189 name: column.into_iden(),
190 order: Some(order),
191 });
192 self
193 }
194
195 /// Add multiple columns to the index
196 ///
197 /// # Examples
198 ///
199 /// ```rust,ignore
200 /// use reinhardt_query::prelude::*;
201 ///
202 /// let query = Query::create_index()
203 /// .name("idx_name_email")
204 /// .table("users")
205 /// .cols(vec!["name", "email"]);
206 /// ```
207 pub fn cols<I, C>(&mut self, columns: I) -> &mut Self
208 where
209 I: IntoIterator<Item = C>,
210 C: IntoIden,
211 {
212 for col in columns {
213 self.col(col);
214 }
215 self
216 }
217
218 /// Set UNIQUE attribute
219 ///
220 /// # Examples
221 ///
222 /// ```rust,ignore
223 /// use reinhardt_query::prelude::*;
224 ///
225 /// let query = Query::create_index()
226 /// .name("idx_email")
227 /// .table("users")
228 /// .col("email")
229 /// .unique();
230 /// ```
231 pub fn unique(&mut self) -> &mut Self {
232 self.unique = true;
233 self
234 }
235
236 /// Add IF NOT EXISTS clause
237 ///
238 /// # Examples
239 ///
240 /// ```rust,ignore
241 /// use reinhardt_query::prelude::*;
242 ///
243 /// let query = Query::create_index()
244 /// .name("idx_email")
245 /// .table("users")
246 /// .col("email")
247 /// .if_not_exists();
248 /// ```
249 pub fn if_not_exists(&mut self) -> &mut Self {
250 self.if_not_exists = true;
251 self
252 }
253
254 /// Add WHERE clause for partial index
255 ///
256 /// Partial indexes are supported by PostgreSQL and SQLite.
257 ///
258 /// # Examples
259 ///
260 /// ```rust,ignore
261 /// use reinhardt_query::prelude::*;
262 ///
263 /// let query = Query::create_index()
264 /// .name("idx_active_users")
265 /// .table("users")
266 /// .col("email")
267 /// .r#where(Expr::col("active").eq(true));
268 /// ```
269 pub fn r#where(&mut self, condition: SimpleExpr) -> &mut Self {
270 self.r#where = Some(condition);
271 self
272 }
273
274 /// Set index method using USING clause
275 ///
276 /// # Examples
277 ///
278 /// ```rust,ignore
279 /// use reinhardt_query::prelude::*;
280 /// use reinhardt_query::query::IndexMethod;
281 ///
282 /// let query = Query::create_index()
283 /// .name("idx_email")
284 /// .table("users")
285 /// .col("email")
286 /// .using(IndexMethod::Hash);
287 /// ```
288 pub fn using(&mut self, method: IndexMethod) -> &mut Self {
289 self.using = Some(method);
290 self
291 }
292}
293
294impl Default for CreateIndexStatement {
295 fn default() -> Self {
296 Self::new()
297 }
298}
299
300impl QueryStatementBuilder for CreateIndexStatement {
301 fn build_any(&self, query_builder: &dyn QueryBuilderTrait) -> (String, crate::value::Values) {
302 // Downcast to concrete QueryBuilder type
303 use std::any::Any;
304 if let Some(builder) =
305 (query_builder as &dyn Any).downcast_ref::<crate::backend::PostgresQueryBuilder>()
306 {
307 return builder.build_create_index(self);
308 }
309 if let Some(builder) =
310 (query_builder as &dyn Any).downcast_ref::<crate::backend::MySqlQueryBuilder>()
311 {
312 return builder.build_create_index(self);
313 }
314 if let Some(builder) =
315 (query_builder as &dyn Any).downcast_ref::<crate::backend::SqliteQueryBuilder>()
316 {
317 return builder.build_create_index(self);
318 }
319 panic!("Unsupported query builder type");
320 }
321}
322
323impl QueryStatementWriter for CreateIndexStatement {}