sea_query/table/
alter.rs

1use crate::{
2    ColumnDef, IntoColumnDef, SchemaStatementBuilder, TableForeignKey, backend::SchemaBuilder,
3    types::*,
4};
5use inherent::inherent;
6
7/// Alter a table
8///
9/// # Examples
10///
11/// ```
12/// use sea_query::{tests_cfg::*, *};
13///
14/// let table = Table::alter()
15///     .table(Font::Table)
16///     .add_column(ColumnDef::new("new_col").integer().not_null().default(100))
17///     .to_owned();
18///
19/// assert_eq!(
20///     table.to_string(MysqlQueryBuilder),
21///     r#"ALTER TABLE `font` ADD COLUMN `new_col` int NOT NULL DEFAULT 100"#
22/// );
23/// assert_eq!(
24///     table.to_string(PostgresQueryBuilder),
25///     r#"ALTER TABLE "font" ADD COLUMN "new_col" integer NOT NULL DEFAULT 100"#
26/// );
27/// assert_eq!(
28///     table.to_string(SqliteQueryBuilder),
29///     r#"ALTER TABLE "font" ADD COLUMN "new_col" integer NOT NULL DEFAULT 100"#,
30/// );
31/// ```
32#[derive(Default, Debug, Clone)]
33pub struct TableAlterStatement {
34    pub(crate) table: Option<TableRef>,
35    pub(crate) options: Vec<TableAlterOption>,
36}
37
38/// table alter add column options
39#[derive(Debug, Clone)]
40pub struct AddColumnOption {
41    pub(crate) column: ColumnDef,
42    pub(crate) if_not_exists: bool,
43}
44
45/// table alter drop column options
46#[derive(Debug, Clone)]
47pub struct DropColumnOption {
48    pub(crate) column_name: DynIden,
49    pub(crate) if_exists: bool,
50}
51
52/// All available table alter options
53#[allow(clippy::large_enum_variant)]
54#[derive(Debug, Clone)]
55#[non_exhaustive]
56pub enum TableAlterOption {
57    AddColumn(AddColumnOption),
58    ModifyColumn(ColumnDef),
59    RenameColumn(DynIden, DynIden),
60    DropColumn(DropColumnOption),
61    AddForeignKey(TableForeignKey),
62    DropForeignKey(DynIden),
63}
64
65impl TableAlterStatement {
66    /// Construct alter table statement
67    pub fn new() -> Self {
68        Self::default()
69    }
70
71    /// Set table name
72    pub fn table<T>(&mut self, table: T) -> &mut Self
73    where
74        T: IntoTableRef,
75    {
76        self.table = Some(table.into_table_ref());
77        self
78    }
79
80    /// Add a column to an existing table
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use sea_query::{tests_cfg::*, *};
86    ///
87    /// let table = Table::alter()
88    ///     .table(Font::Table)
89    ///     .add_column(ColumnDef::new("new_col").integer().not_null().default(100))
90    ///     .to_owned();
91    ///
92    /// assert_eq!(
93    ///     table.to_string(MysqlQueryBuilder),
94    ///     r#"ALTER TABLE `font` ADD COLUMN `new_col` int NOT NULL DEFAULT 100"#
95    /// );
96    /// assert_eq!(
97    ///     table.to_string(PostgresQueryBuilder),
98    ///     r#"ALTER TABLE "font" ADD COLUMN "new_col" integer NOT NULL DEFAULT 100"#
99    /// );
100    /// assert_eq!(
101    ///     table.to_string(SqliteQueryBuilder),
102    ///     r#"ALTER TABLE "font" ADD COLUMN "new_col" integer NOT NULL DEFAULT 100"#,
103    /// );
104    /// ```
105    pub fn add_column<C: IntoColumnDef>(&mut self, column_def: C) -> &mut Self {
106        self.options
107            .push(TableAlterOption::AddColumn(AddColumnOption {
108                column: column_def.into_column_def(),
109                if_not_exists: false,
110            }));
111        self
112    }
113
114    /// Try add a column to an existing table if it does not exists
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// use sea_query::{tests_cfg::*, *};
120    ///
121    /// let table = Table::alter()
122    ///     .table(Font::Table)
123    ///     .add_column_if_not_exists(ColumnDef::new("new_col").integer().not_null().default(100))
124    ///     .to_owned();
125    ///
126    /// assert_eq!(
127    ///     table.to_string(MysqlQueryBuilder),
128    ///     r#"ALTER TABLE `font` ADD COLUMN IF NOT EXISTS `new_col` int NOT NULL DEFAULT 100"#
129    /// );
130    /// assert_eq!(
131    ///     table.to_string(PostgresQueryBuilder),
132    ///     r#"ALTER TABLE "font" ADD COLUMN IF NOT EXISTS "new_col" integer NOT NULL DEFAULT 100"#
133    /// );
134    /// assert_eq!(
135    ///     table.to_string(SqliteQueryBuilder),
136    ///     r#"ALTER TABLE "font" ADD COLUMN "new_col" integer NOT NULL DEFAULT 100"#,
137    /// );
138    /// ```
139    pub fn add_column_if_not_exists<C: IntoColumnDef>(&mut self, column_def: C) -> &mut Self {
140        self.options
141            .push(TableAlterOption::AddColumn(AddColumnOption {
142                column: column_def.into_column_def(),
143                if_not_exists: true,
144            }));
145        self
146    }
147
148    /// Modify a column in an existing table
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use sea_query::{tests_cfg::*, *};
154    ///
155    /// let table = Table::alter()
156    ///     .table(Font::Table)
157    ///     .modify_column(ColumnDef::new("new_col").big_integer().default(999))
158    ///     .to_owned();
159    ///
160    /// assert_eq!(
161    ///     table.to_string(MysqlQueryBuilder),
162    ///     r#"ALTER TABLE `font` MODIFY COLUMN `new_col` bigint DEFAULT 999"#
163    /// );
164    /// assert_eq!(
165    ///     table.to_string(PostgresQueryBuilder),
166    ///     [
167    ///         r#"ALTER TABLE "font""#,
168    ///         r#"ALTER COLUMN "new_col" TYPE bigint,"#,
169    ///         r#"ALTER COLUMN "new_col" SET DEFAULT 999"#,
170    ///     ]
171    ///     .join(" ")
172    /// );
173    /// // Sqlite not support modifying table column
174    /// ```
175    pub fn modify_column<C: IntoColumnDef>(&mut self, column_def: C) -> &mut Self {
176        self.add_alter_option(TableAlterOption::ModifyColumn(column_def.into_column_def()))
177    }
178
179    /// Rename a column in an existing table
180    ///
181    /// # Examples
182    ///
183    /// ```
184    /// use sea_query::{tests_cfg::*, *};
185    ///
186    /// let table = Table::alter()
187    ///     .table(Font::Table)
188    ///     .rename_column("new_col", "new_column")
189    ///     .to_owned();
190    ///
191    /// assert_eq!(
192    ///     table.to_string(MysqlQueryBuilder),
193    ///     r#"ALTER TABLE `font` RENAME COLUMN `new_col` TO `new_column`"#
194    /// );
195    /// assert_eq!(
196    ///     table.to_string(PostgresQueryBuilder),
197    ///     r#"ALTER TABLE "font" RENAME COLUMN "new_col" TO "new_column""#
198    /// );
199    /// assert_eq!(
200    ///     table.to_string(SqliteQueryBuilder),
201    ///     r#"ALTER TABLE "font" RENAME COLUMN "new_col" TO "new_column""#
202    /// );
203    /// ```
204    pub fn rename_column<T, R>(&mut self, from_name: T, to_name: R) -> &mut Self
205    where
206        T: IntoIden,
207        R: IntoIden,
208    {
209        self.add_alter_option(TableAlterOption::RenameColumn(
210            from_name.into_iden(),
211            to_name.into_iden(),
212        ))
213    }
214
215    /// Drop a column from an existing table
216    ///
217    /// # Examples
218    ///
219    /// ```
220    /// use sea_query::{tests_cfg::*, *};
221    ///
222    /// let table = Table::alter()
223    ///     .table(Font::Table)
224    ///     .drop_column("new_column")
225    ///     .to_owned();
226    ///
227    /// assert_eq!(
228    ///     table.to_string(MysqlQueryBuilder),
229    ///     r#"ALTER TABLE `font` DROP COLUMN `new_column`"#
230    /// );
231    /// assert_eq!(
232    ///     table.to_string(PostgresQueryBuilder),
233    ///     r#"ALTER TABLE "font" DROP COLUMN "new_column""#
234    /// );
235    /// assert_eq!(
236    ///     table.to_string(SqliteQueryBuilder),
237    ///     r#"ALTER TABLE "font" DROP COLUMN "new_column""#
238    /// );
239    /// ```
240    pub fn drop_column<T>(&mut self, col_name: T) -> &mut Self
241    where
242        T: IntoIden,
243    {
244        self.add_alter_option(TableAlterOption::DropColumn(DropColumnOption {
245            column_name: col_name.into(),
246            if_exists: false,
247        }))
248    }
249
250    /// Drop a column from an existing table if it exists
251    ///
252    /// # Examples
253    ///
254    /// ```
255    /// use sea_query::{tests_cfg::*, *};
256    ///
257    /// let table = Table::alter()
258    ///     .table(Font::Table)
259    ///     .drop_column_if_exists("new_column")
260    ///     .to_owned();
261    ///
262    /// assert_eq!(
263    ///     table.to_string(PostgresQueryBuilder),
264    ///     r#"ALTER TABLE "font" DROP COLUMN IF EXISTS "new_column""#
265    /// );
266    /// // MySQL and Sqlite do not support DROP COLUMN IF EXISTS
267    /// ```
268    pub fn drop_column_if_exists<T>(&mut self, col_name: T) -> &mut Self
269    where
270        T: IntoIden,
271    {
272        self.add_alter_option(TableAlterOption::DropColumn(DropColumnOption {
273            column_name: col_name.into(),
274            if_exists: true,
275        }))
276    }
277
278    /// Add a foreign key to existing table
279    ///
280    /// # Examples
281    ///
282    /// ```
283    /// use sea_query::{tests_cfg::*, *};
284    ///
285    /// let foreign_key_char = TableForeignKey::new()
286    ///     .name("FK_character_glyph")
287    ///     .from_tbl(Char::Table)
288    ///     .from_col(Char::FontId)
289    ///     .from_col(Char::Id)
290    ///     .to_tbl(Glyph::Table)
291    ///     .to_col(Char::FontId)
292    ///     .to_col(Char::Id)
293    ///     .on_delete(ForeignKeyAction::Cascade)
294    ///     .on_update(ForeignKeyAction::Cascade)
295    ///     .to_owned();
296    ///
297    /// let foreign_key_font = TableForeignKey::new()
298    ///     .name("FK_character_font")
299    ///     .from_tbl(Char::Table)
300    ///     .from_col(Char::FontId)
301    ///     .to_tbl(Font::Table)
302    ///     .to_col(Font::Id)
303    ///     .on_delete(ForeignKeyAction::Cascade)
304    ///     .on_update(ForeignKeyAction::Cascade)
305    ///     .to_owned();
306    ///
307    /// let table = Table::alter()
308    ///     .table(Character::Table)
309    ///     .add_foreign_key(&foreign_key_char)
310    ///     .add_foreign_key(&foreign_key_font)
311    ///     .to_owned();
312    ///
313    /// assert_eq!(
314    ///     table.to_string(MysqlQueryBuilder),
315    ///     [
316    ///         r#"ALTER TABLE `character`"#,
317    ///         r#"ADD CONSTRAINT `FK_character_glyph`"#,
318    ///         r#"FOREIGN KEY (`font_id`, `id`) REFERENCES `glyph` (`font_id`, `id`)"#,
319    ///         r#"ON DELETE CASCADE ON UPDATE CASCADE,"#,
320    ///         r#"ADD CONSTRAINT `FK_character_font`"#,
321    ///         r#"FOREIGN KEY (`font_id`) REFERENCES `font` (`id`)"#,
322    ///         r#"ON DELETE CASCADE ON UPDATE CASCADE"#,
323    ///     ]
324    ///     .join(" ")
325    /// );
326    ///
327    /// assert_eq!(
328    ///     table.to_string(PostgresQueryBuilder),
329    ///     [
330    ///         r#"ALTER TABLE "character""#,
331    ///         r#"ADD CONSTRAINT "FK_character_glyph""#,
332    ///         r#"FOREIGN KEY ("font_id", "id") REFERENCES "glyph" ("font_id", "id")"#,
333    ///         r#"ON DELETE CASCADE ON UPDATE CASCADE,"#,
334    ///         r#"ADD CONSTRAINT "FK_character_font""#,
335    ///         r#"FOREIGN KEY ("font_id") REFERENCES "font" ("id")"#,
336    ///         r#"ON DELETE CASCADE ON UPDATE CASCADE"#,
337    ///     ]
338    ///     .join(" ")
339    /// );
340    ///
341    /// // Sqlite not support modifying table column
342    /// ```
343    pub fn add_foreign_key(&mut self, foreign_key: &TableForeignKey) -> &mut Self {
344        self.add_alter_option(TableAlterOption::AddForeignKey(foreign_key.to_owned()))
345    }
346
347    /// Drop a foreign key from existing table
348    ///
349    /// # Examples
350    ///
351    /// ```
352    /// use sea_query::{tests_cfg::*, *};
353    ///
354    /// let table = Table::alter()
355    ///     .table(Character::Table)
356    ///     .drop_foreign_key("FK_character_glyph")
357    ///     .drop_foreign_key("FK_character_font")
358    ///     .to_owned();
359    ///
360    /// assert_eq!(
361    ///     table.to_string(MysqlQueryBuilder),
362    ///     [
363    ///         r#"ALTER TABLE `character`"#,
364    ///         r#"DROP FOREIGN KEY `FK_character_glyph`,"#,
365    ///         r#"DROP FOREIGN KEY `FK_character_font`"#,
366    ///     ]
367    ///     .join(" ")
368    /// );
369    ///
370    /// assert_eq!(
371    ///     table.to_string(PostgresQueryBuilder),
372    ///     [
373    ///         r#"ALTER TABLE "character""#,
374    ///         r#"DROP CONSTRAINT "FK_character_glyph","#,
375    ///         r#"DROP CONSTRAINT "FK_character_font""#,
376    ///     ]
377    ///     .join(" ")
378    /// );
379    ///
380    /// // Sqlite not support modifying table column
381    /// ```
382    pub fn drop_foreign_key<T>(&mut self, name: T) -> &mut Self
383    where
384        T: IntoIden,
385    {
386        self.add_alter_option(TableAlterOption::DropForeignKey(name.into_iden()))
387    }
388
389    fn add_alter_option(&mut self, alter_option: TableAlterOption) -> &mut Self {
390        self.options.push(alter_option);
391        self
392    }
393
394    pub fn take(&mut self) -> Self {
395        Self {
396            table: self.table.take(),
397            options: std::mem::take(&mut self.options),
398        }
399    }
400}
401
402#[inherent]
403impl SchemaStatementBuilder for TableAlterStatement {
404    pub fn build<T>(&self, schema_builder: T) -> String
405    where
406        T: SchemaBuilder,
407    {
408        let mut sql = String::with_capacity(256);
409        schema_builder.prepare_table_alter_statement(self, &mut sql);
410        sql
411    }
412
413    pub fn to_string<T>(&self, schema_builder: T) -> String
414    where
415        T: SchemaBuilder;
416}