Skip to main content

sea_query/table/
alter.rs

1use crate::{
2    ColumnDef, IntoColumnDef, SchemaStatementBuilder, TableForeignKey, backend::SchemaBuilder,
3    types::*,
4};
5
6/// Alter a table
7///
8/// # Examples
9///
10/// ```
11/// use sea_query::{tests_cfg::*, *};
12///
13/// let table = Table::alter()
14///     .table(Font::Table)
15///     .add_column(ColumnDef::new("new_col").integer().not_null().default(100))
16///     .to_owned();
17///
18/// assert_eq!(
19///     table.to_string(MysqlQueryBuilder),
20///     r#"ALTER TABLE `font` ADD COLUMN `new_col` int NOT NULL DEFAULT 100"#
21/// );
22/// assert_eq!(
23///     table.to_string(PostgresQueryBuilder),
24///     r#"ALTER TABLE "font" ADD COLUMN "new_col" integer NOT NULL DEFAULT 100"#
25/// );
26/// assert_eq!(
27///     table.to_string(SqliteQueryBuilder),
28///     r#"ALTER TABLE "font" ADD COLUMN "new_col" integer NOT NULL DEFAULT 100"#,
29/// );
30/// ```
31#[derive(Default, Debug, Clone)]
32pub struct TableAlterStatement {
33    pub(crate) table: Option<TableRef>,
34    pub(crate) options: Vec<TableAlterOption>,
35}
36
37/// table alter add column options
38#[derive(Debug, Clone)]
39pub struct AddColumnOption {
40    pub(crate) column: ColumnDef,
41    pub(crate) if_not_exists: bool,
42}
43
44/// table alter drop column options
45#[derive(Debug, Clone)]
46pub struct DropColumnOption {
47    pub(crate) column_name: DynIden,
48    pub(crate) if_exists: bool,
49}
50
51/// All available table alter options
52#[allow(clippy::large_enum_variant)]
53#[derive(Debug, Clone)]
54#[non_exhaustive]
55pub enum TableAlterOption {
56    AddColumn(AddColumnOption),
57    ModifyColumn(ColumnDef),
58    RenameColumn(DynIden, DynIden),
59    DropColumn(DropColumnOption),
60    AddForeignKey(TableForeignKey),
61    DropForeignKey(DynIden),
62    DropConstraint(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    /// Drop a named constraint from an existing table.
390    ///
391    /// On PostgreSQL and MySQL this generates `DROP CONSTRAINT "name"`.
392    ///
393    /// **Note:** On PostgreSQL this is the correct way to drop a unique constraint that was
394    /// created via `ADD COLUMN … UNIQUE` (which creates a named constraint, not a standalone
395    /// index). On MySQL, `DROP CONSTRAINT` is only valid for **check constraints** (MySQL
396    /// 8.0.19+); use [`crate::Index::drop`] to remove a unique index on MySQL.
397    ///
398    /// # Examples
399    ///
400    /// ```
401    /// use sea_query::{tests_cfg::*, *};
402    ///
403    /// let table = Table::alter()
404    ///     .table(Font::Table)
405    ///     .drop_constraint("font_name_key")
406    ///     .to_owned();
407    ///
408    /// assert_eq!(
409    ///     table.to_string(MysqlQueryBuilder),
410    ///     r#"ALTER TABLE `font` DROP CONSTRAINT `font_name_key`"#
411    /// );
412    /// assert_eq!(
413    ///     table.to_string(PostgresQueryBuilder),
414    ///     r#"ALTER TABLE "font" DROP CONSTRAINT "font_name_key""#
415    /// );
416    /// // Sqlite does not support dropping constraints
417    /// ```
418    pub fn drop_constraint<T>(&mut self, name: T) -> &mut Self
419    where
420        T: IntoIden,
421    {
422        self.add_alter_option(TableAlterOption::DropConstraint(name.into_iden()))
423    }
424
425    fn add_alter_option(&mut self, alter_option: TableAlterOption) -> &mut Self {
426        self.options.push(alter_option);
427        self
428    }
429
430    pub fn take(&mut self) -> Self {
431        Self {
432            table: self.table.take(),
433            options: std::mem::take(&mut self.options),
434        }
435    }
436}
437
438impl SchemaStatementBuilder for TableAlterStatement {
439    fn build<T: SchemaBuilder>(&self, schema_builder: T) -> String {
440        let mut sql = String::with_capacity(256);
441        schema_builder.prepare_table_alter_statement(self, &mut sql);
442        sql
443    }
444}
445
446impl TableAlterStatement {
447    pub fn build<T: SchemaBuilder>(&self, schema_builder: T) -> String {
448        <Self as SchemaStatementBuilder>::build(self, schema_builder)
449    }
450
451    pub fn to_string<T: SchemaBuilder>(&self, schema_builder: T) -> String {
452        <Self as SchemaStatementBuilder>::to_string(self, schema_builder)
453    }
454}