Skip to main content

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 {}