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}