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}