sea_query/index/create.rs
1use inherent::inherent;
2
3use crate::{backend::SchemaBuilder, types::*, SchemaStatementBuilder};
4use crate::{ConditionHolder, ConditionalStatement, IntoCondition};
5
6use super::common::*;
7
8/// Create an index for an existing table
9///
10/// # Examples
11///
12/// ```
13/// use sea_query::{tests_cfg::*, *};
14///
15/// let index = Index::create()
16/// .name("idx-glyph-aspect")
17/// .table(Glyph::Table)
18/// .col(Glyph::Aspect)
19/// .to_owned();
20///
21/// assert_eq!(
22/// index.to_string(MysqlQueryBuilder),
23/// r#"CREATE INDEX `idx-glyph-aspect` ON `glyph` (`aspect`)"#
24/// );
25/// assert_eq!(
26/// index.to_string(PostgresQueryBuilder),
27/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect")"#
28/// );
29/// assert_eq!(
30/// index.to_string(SqliteQueryBuilder),
31/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect")"#
32/// );
33/// ```
34/// Create index if not exists
35/// ```
36/// use sea_query::{tests_cfg::*, *};
37///
38/// let index = Index::create()
39/// .if_not_exists()
40/// .name("idx-glyph-aspect")
41/// .table(Glyph::Table)
42/// .col(Glyph::Aspect)
43/// .to_owned();
44///
45/// assert_eq!(
46/// index.to_string(MysqlQueryBuilder),
47/// r#"CREATE INDEX `idx-glyph-aspect` ON `glyph` (`aspect`)"#
48/// );
49/// assert_eq!(
50/// index.to_string(PostgresQueryBuilder),
51/// r#"CREATE INDEX IF NOT EXISTS "idx-glyph-aspect" ON "glyph" ("aspect")"#
52/// );
53/// assert_eq!(
54/// index.to_string(SqliteQueryBuilder),
55/// r#"CREATE INDEX IF NOT EXISTS "idx-glyph-aspect" ON "glyph" ("aspect")"#
56/// );
57/// ```
58/// Index with prefix
59/// ```
60/// use sea_query::{tests_cfg::*, *};
61///
62/// let index = Index::create()
63/// .name("idx-glyph-aspect")
64/// .table(Glyph::Table)
65/// .col((Glyph::Aspect, 128))
66/// .to_owned();
67///
68/// assert_eq!(
69/// index.to_string(MysqlQueryBuilder),
70/// r#"CREATE INDEX `idx-glyph-aspect` ON `glyph` (`aspect` (128))"#
71/// );
72/// assert_eq!(
73/// index.to_string(PostgresQueryBuilder),
74/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" (128))"#
75/// );
76/// assert_eq!(
77/// index.to_string(SqliteQueryBuilder),
78/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect")"#
79/// );
80/// ```
81/// Index with order
82/// ```
83/// use sea_query::{tests_cfg::*, *};
84///
85/// let index = Index::create()
86/// .name("idx-glyph-aspect")
87/// .table(Glyph::Table)
88/// .col((Glyph::Aspect, IndexOrder::Desc))
89/// .to_owned();
90///
91/// assert_eq!(
92/// index.to_string(MysqlQueryBuilder),
93/// r#"CREATE INDEX `idx-glyph-aspect` ON `glyph` (`aspect` DESC)"#
94/// );
95/// assert_eq!(
96/// index.to_string(PostgresQueryBuilder),
97/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" DESC)"#
98/// );
99/// assert_eq!(
100/// index.to_string(SqliteQueryBuilder),
101/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" DESC)"#
102/// );
103/// ```
104/// Index on multi-columns
105/// ```
106/// use sea_query::{tests_cfg::*, *};
107///
108/// let index = Index::create()
109/// .name("idx-glyph-aspect")
110/// .table(Glyph::Table)
111/// .col((Glyph::Image, IndexOrder::Asc))
112/// .col((Glyph::Aspect, IndexOrder::Desc))
113/// .unique()
114/// .to_owned();
115///
116/// assert_eq!(
117/// index.to_string(MysqlQueryBuilder),
118/// r#"CREATE UNIQUE INDEX `idx-glyph-aspect` ON `glyph` (`image` ASC, `aspect` DESC)"#
119/// );
120/// assert_eq!(
121/// index.to_string(PostgresQueryBuilder),
122/// r#"CREATE UNIQUE INDEX "idx-glyph-aspect" ON "glyph" ("image" ASC, "aspect" DESC)"#
123/// );
124/// assert_eq!(
125/// index.to_string(SqliteQueryBuilder),
126/// r#"CREATE UNIQUE INDEX "idx-glyph-aspect" ON "glyph" ("image" ASC, "aspect" DESC)"#
127/// );
128/// ```
129/// Index with prefix and order
130/// ```
131/// use sea_query::{tests_cfg::*, *};
132///
133/// let index = Index::create()
134/// .name("idx-glyph-aspect")
135/// .table(Glyph::Table)
136/// .col((Glyph::Aspect, 64, IndexOrder::Asc))
137/// .to_owned();
138///
139/// assert_eq!(
140/// index.to_string(MysqlQueryBuilder),
141/// r#"CREATE INDEX `idx-glyph-aspect` ON `glyph` (`aspect` (64) ASC)"#
142/// );
143/// assert_eq!(
144/// index.to_string(PostgresQueryBuilder),
145/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" (64) ASC)"#
146/// );
147/// assert_eq!(
148/// index.to_string(SqliteQueryBuilder),
149/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" ASC)"#
150/// );
151/// ```
152///
153/// Partial Index with prefix and order
154/// ```
155/// use sea_query::{tests_cfg::*, *};
156///
157/// let index = Index::create()
158/// .name("idx-glyph-aspect")
159/// .table(Glyph::Table)
160/// .col((Glyph::Aspect, 64, IndexOrder::Asc))
161/// .and_where(Expr::col((Glyph::Table, Glyph::Aspect)).is_in(vec![3, 4]))
162/// .to_owned();
163///
164/// assert_eq!(
165/// index.to_string(PostgresQueryBuilder),
166/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" (64) ASC) WHERE "glyph"."aspect" IN (3, 4)"#
167/// );
168/// assert_eq!(
169/// index.to_string(SqliteQueryBuilder),
170/// r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect" ASC) WHERE "glyph"."aspect" IN (3, 4)"#
171/// );
172/// ```
173///
174/// Index include non-key columns
175/// ```
176/// use sea_query::{tests_cfg::*, *};
177///
178/// let index = Index::create()
179/// .name("idx-font-name-include-language")
180/// .table(Font::Table)
181/// .col(Font::Name)
182/// .include(Font::Language)
183/// .to_owned();
184///
185/// assert_eq!(
186/// index.to_string(PostgresQueryBuilder),
187/// r#"CREATE INDEX "idx-font-name-include-language" ON "font" ("name") INCLUDE ("language")"#
188/// )
189/// ```
190///
191/// Functional Index
192/// ```
193/// use sea_query::{tests_cfg::*, *};
194///
195/// let index = Index::create()
196/// .name("idx-character-area")
197/// .table(Character::Table)
198/// .col(Expr::col(Character::SizeH).mul(Expr::col(Character::SizeW)))
199/// .to_owned();
200///
201/// assert_eq!(
202/// index.to_string(MysqlQueryBuilder),
203/// "CREATE INDEX `idx-character-area` ON `character` ((`size_h` * `size_w`))"
204/// );
205/// assert_eq!(
206/// index.to_string(PostgresQueryBuilder),
207/// r#"CREATE INDEX "idx-character-area" ON "character" (("size_h" * "size_w"))"#
208/// );
209/// ```
210#[derive(Default, Debug, Clone)]
211pub struct IndexCreateStatement {
212 pub(crate) table: Option<TableRef>,
213 pub(crate) index: TableIndex,
214 pub(crate) primary: bool,
215 pub(crate) unique: bool,
216 pub(crate) nulls_not_distinct: bool,
217 pub(crate) index_type: Option<IndexType>,
218 pub(crate) if_not_exists: bool,
219 pub(crate) r#where: ConditionHolder,
220 pub(crate) include_columns: Vec<DynIden>,
221}
222
223/// Specification of a table index
224#[derive(Debug, Clone)]
225pub enum IndexType {
226 BTree,
227 FullText,
228 Hash,
229 Custom(DynIden),
230}
231
232impl IndexCreateStatement {
233 /// Construct a new [`IndexCreateStatement`]
234 pub fn new() -> Self {
235 Self {
236 table: None,
237 index: Default::default(),
238 primary: false,
239 unique: false,
240 nulls_not_distinct: false,
241 index_type: None,
242 if_not_exists: false,
243 r#where: ConditionHolder::new(),
244 include_columns: vec![],
245 }
246 }
247
248 /// Create index if index not exists
249 pub fn if_not_exists(&mut self) -> &mut Self {
250 self.if_not_exists = true;
251 self
252 }
253
254 /// Set index name
255 pub fn name<T>(&mut self, name: T) -> &mut Self
256 where
257 T: Into<String>,
258 {
259 self.index.name(name);
260 self
261 }
262
263 /// Set target table
264 pub fn table<T>(&mut self, table: T) -> &mut Self
265 where
266 T: IntoTableRef,
267 {
268 self.table = Some(table.into_table_ref());
269 self
270 }
271
272 /// Add index column
273 pub fn col<C>(&mut self, col: C) -> &mut Self
274 where
275 C: IntoIndexColumn,
276 {
277 self.index.col(col.into_index_column());
278 self
279 }
280
281 /// Set index as primary
282 pub fn primary(&mut self) -> &mut Self {
283 self.primary = true;
284 self
285 }
286
287 /// Set index as unique
288 pub fn unique(&mut self) -> &mut Self {
289 self.unique = true;
290 self
291 }
292
293 /// Set nulls to not be treated as distinct values. Only available on Postgres.
294 pub fn nulls_not_distinct(&mut self) -> &mut Self {
295 self.nulls_not_distinct = true;
296 self
297 }
298
299 /// Set index as full text.
300 /// On MySQL, this is `FULLTEXT`.
301 /// On PgSQL, this is `GIN`.
302 pub fn full_text(&mut self) -> &mut Self {
303 self.index_type(IndexType::FullText)
304 }
305
306 /// Set index type. Not available on Sqlite.
307 pub fn index_type(&mut self, index_type: IndexType) -> &mut Self {
308 self.index_type = Some(index_type);
309 self
310 }
311
312 pub fn include<C>(&mut self, col: C) -> &mut Self
313 where
314 C: IntoIden,
315 {
316 self.include_columns.push(col.into_iden());
317 self
318 }
319
320 pub fn is_primary_key(&self) -> bool {
321 self.primary
322 }
323
324 pub fn is_unique_key(&self) -> bool {
325 self.unique
326 }
327
328 pub fn is_nulls_not_distinct(&self) -> bool {
329 self.nulls_not_distinct
330 }
331
332 pub fn get_index_spec(&self) -> &TableIndex {
333 &self.index
334 }
335
336 pub fn take(&mut self) -> Self {
337 Self {
338 table: self.table.take(),
339 index: self.index.take(),
340 primary: self.primary,
341 unique: self.unique,
342 nulls_not_distinct: self.nulls_not_distinct,
343 index_type: self.index_type.take(),
344 if_not_exists: self.if_not_exists,
345 r#where: self.r#where.clone(),
346 include_columns: self.include_columns.clone(),
347 }
348 }
349}
350
351#[inherent]
352impl SchemaStatementBuilder for IndexCreateStatement {
353 pub fn build<T: SchemaBuilder>(&self, schema_builder: T) -> String {
354 let mut sql = String::with_capacity(256);
355 schema_builder.prepare_index_create_statement(self, &mut sql);
356 sql
357 }
358
359 pub fn build_any(&self, schema_builder: &dyn SchemaBuilder) -> String {
360 let mut sql = String::with_capacity(256);
361 schema_builder.prepare_index_create_statement(self, &mut sql);
362 sql
363 }
364
365 pub fn to_string<T: SchemaBuilder>(&self, schema_builder: T) -> String;
366}
367
368impl ConditionalStatement for IndexCreateStatement {
369 fn and_or_where(&mut self, condition: LogicalChainOper) -> &mut Self {
370 self.r#where.add_and_or(condition);
371 self
372 }
373
374 fn cond_where<C>(&mut self, condition: C) -> &mut Self
375 where
376 C: IntoCondition,
377 {
378 self.r#where.add_condition(condition.into_condition());
379 self
380 }
381}