sea_query/index/
create.rs

1use inherent::inherent;
2
3use crate::{ConditionHolder, ConditionalStatement, IntoCondition};
4use crate::{SchemaStatementBuilder, backend::SchemaBuilder, types::*};
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) concurrently: bool,
217    pub(crate) nulls_not_distinct: bool,
218    pub(crate) index_type: Option<IndexType>,
219    pub(crate) if_not_exists: bool,
220    pub(crate) r#where: ConditionHolder,
221    pub(crate) include_columns: Vec<DynIden>,
222}
223
224/// Specification of a table index
225#[derive(Debug, Clone)]
226#[non_exhaustive]
227pub enum IndexType {
228    BTree,
229    FullText,
230    Hash,
231    Custom(DynIden),
232}
233
234impl IndexCreateStatement {
235    /// Construct a new [`IndexCreateStatement`]
236    pub fn new() -> Self {
237        Self {
238            table: None,
239            index: Default::default(),
240            primary: false,
241            unique: false,
242            concurrently: false,
243            nulls_not_distinct: false,
244            index_type: None,
245            if_not_exists: false,
246            r#where: ConditionHolder::new(),
247            include_columns: vec![],
248        }
249    }
250
251    /// Create index if index not exists
252    pub fn if_not_exists(&mut self) -> &mut Self {
253        self.if_not_exists = true;
254        self
255    }
256
257    /// Set index name
258    pub fn name<T>(&mut self, name: T) -> &mut Self
259    where
260        T: Into<String>,
261    {
262        self.index.name(name);
263        self
264    }
265
266    /// Set target table
267    pub fn table<T>(&mut self, table: T) -> &mut Self
268    where
269        T: IntoTableRef,
270    {
271        self.table = Some(table.into_table_ref());
272        self
273    }
274
275    /// Add index column
276    pub fn col<C>(&mut self, col: C) -> &mut Self
277    where
278        C: IntoIndexColumn,
279    {
280        self.index.col(col.into_index_column());
281        self
282    }
283
284    /// Set index as primary
285    pub fn primary(&mut self) -> &mut Self {
286        self.primary = true;
287        self
288    }
289
290    /// Set index as unique
291    pub fn unique(&mut self) -> &mut Self {
292        self.unique = true;
293        self
294    }
295
296    /// Set index to be created concurrently. Only available on Postgres.
297    pub fn concurrently(&mut self) -> &mut Self {
298        self.concurrently = true;
299        self
300    }
301
302    /// Set nulls to not be treated as distinct values. Only available on Postgres.
303    pub fn nulls_not_distinct(&mut self) -> &mut Self {
304        self.nulls_not_distinct = true;
305        self
306    }
307
308    /// Set index as full text.
309    /// On MySQL, this is `FULLTEXT`.
310    /// On PgSQL, this is `GIN`.
311    pub fn full_text(&mut self) -> &mut Self {
312        self.index_type(IndexType::FullText)
313    }
314
315    /// Set index type. Not available on Sqlite.
316    pub fn index_type(&mut self, index_type: IndexType) -> &mut Self {
317        self.index_type = Some(index_type);
318        self
319    }
320
321    pub fn include<C>(&mut self, col: C) -> &mut Self
322    where
323        C: IntoIden,
324    {
325        self.include_columns.push(col.into_iden());
326        self
327    }
328
329    pub fn is_primary_key(&self) -> bool {
330        self.primary
331    }
332
333    pub fn is_unique_key(&self) -> bool {
334        self.unique
335    }
336
337    pub fn is_nulls_not_distinct(&self) -> bool {
338        self.nulls_not_distinct
339    }
340
341    pub fn get_index_spec(&self) -> &TableIndex {
342        &self.index
343    }
344
345    pub fn take(&mut self) -> Self {
346        Self {
347            table: self.table.take(),
348            index: self.index.take(),
349            primary: self.primary,
350            unique: self.unique,
351            concurrently: self.concurrently,
352            nulls_not_distinct: self.nulls_not_distinct,
353            index_type: self.index_type.take(),
354            if_not_exists: self.if_not_exists,
355            r#where: self.r#where.clone(),
356            include_columns: self.include_columns.clone(),
357        }
358    }
359}
360
361#[inherent]
362impl SchemaStatementBuilder for IndexCreateStatement {
363    pub fn build<T>(&self, schema_builder: T) -> String
364    where
365        T: SchemaBuilder,
366    {
367        let mut sql = String::with_capacity(256);
368        schema_builder.prepare_index_create_statement(self, &mut sql);
369        sql
370    }
371
372    pub fn to_string<T>(&self, schema_builder: T) -> String
373    where
374        T: SchemaBuilder;
375}
376
377impl ConditionalStatement for IndexCreateStatement {
378    fn and_or_where(&mut self, condition: LogicalChainOper) -> &mut Self {
379        self.r#where.add_and_or(condition);
380        self
381    }
382
383    fn cond_where<C>(&mut self, condition: C) -> &mut Self
384    where
385        C: IntoCondition,
386    {
387        self.r#where.add_condition(condition.into_condition());
388        self
389    }
390}