1use sea_query::{Alias, ForeignKey, Query, Table};
2
3use vespertide_core::{TableConstraint, TableDef};
4
5use super::create_table::build_create_table_for_backend;
6use super::rename_table::build_rename_table;
7use super::types::{BuiltQuery, DatabaseBackend};
8use crate::error::QueryError;
9use crate::sql::RawSql;
10
11pub fn build_remove_constraint(
12 backend: &DatabaseBackend,
13 table: &str,
14 constraint: &TableConstraint,
15 current_schema: &[TableDef],
16) -> Result<Vec<BuiltQuery>, QueryError> {
17 match constraint {
18 TableConstraint::PrimaryKey { .. } => {
19 if *backend == DatabaseBackend::Sqlite {
20 let table_def = current_schema.iter().find(|t| t.name == table).ok_or_else(|| QueryError::Other(format!("Table '{}' not found in current schema. SQLite requires current schema information to remove constraints.", table)))?;
22
23 let mut new_constraints = table_def.constraints.clone();
25 new_constraints.retain(|c| !matches!(c, TableConstraint::PrimaryKey { .. }));
26
27 let temp_table = format!("{}_temp", table);
29
30 let create_temp_table = build_create_table_for_backend(
32 backend,
33 &temp_table,
34 &table_def.columns,
35 &new_constraints,
36 );
37 let create_query = BuiltQuery::CreateTable(Box::new(create_temp_table));
38
39 let column_aliases: Vec<Alias> = table_def
41 .columns
42 .iter()
43 .map(|c| Alias::new(&c.name))
44 .collect();
45 let mut select_query = Query::select();
46 for col_alias in &column_aliases {
47 select_query = select_query.column(col_alias.clone()).to_owned();
48 }
49 select_query = select_query.from(Alias::new(table)).to_owned();
50
51 let insert_stmt = Query::insert()
52 .into_table(Alias::new(&temp_table))
53 .columns(column_aliases.clone())
54 .select_from(select_query)
55 .unwrap()
56 .to_owned();
57 let insert_query = BuiltQuery::Insert(Box::new(insert_stmt));
58
59 let drop_table = Table::drop().table(Alias::new(table)).to_owned();
61 let drop_query = BuiltQuery::DropTable(Box::new(drop_table));
62
63 let rename_query = build_rename_table(&temp_table, table);
65
66 let mut index_queries = Vec::new();
68 for constraint in &table_def.constraints {
69 if let TableConstraint::Index {
70 name: idx_name,
71 columns: idx_cols,
72 } = constraint
73 {
74 let index_name = vespertide_naming::build_index_name(
75 table,
76 idx_cols,
77 idx_name.as_deref(),
78 );
79 let mut idx_stmt = sea_query::Index::create();
80 idx_stmt = idx_stmt.name(&index_name).to_owned();
81 for col_name in idx_cols {
82 idx_stmt = idx_stmt.col(Alias::new(col_name)).to_owned();
83 }
84 idx_stmt = idx_stmt.table(Alias::new(table)).to_owned();
85 index_queries.push(BuiltQuery::CreateIndex(Box::new(idx_stmt)));
86 }
87 }
88
89 let mut queries = vec![create_query, insert_query, drop_query, rename_query];
90 queries.extend(index_queries);
91 Ok(queries)
92 } else {
93 let pg_sql = format!(
95 "ALTER TABLE \"{}\" DROP CONSTRAINT \"{}_pkey\"",
96 table, table
97 );
98 let mysql_sql = format!("ALTER TABLE `{}` DROP PRIMARY KEY", table);
99 Ok(vec![BuiltQuery::Raw(RawSql::per_backend(
100 pg_sql.clone(),
101 mysql_sql,
102 pg_sql,
103 ))])
104 }
105 }
106 TableConstraint::Unique { name, columns } => {
107 if *backend == DatabaseBackend::Sqlite {
109 let table_def = current_schema.iter().find(|t| t.name == table).ok_or_else(|| QueryError::Other(format!("Table '{}' not found in current schema. SQLite requires current schema information to remove constraints.", table)))?;
111
112 let mut new_constraints = table_def.constraints.clone();
114 new_constraints.retain(|c| {
115 match (c, constraint) {
116 (
117 TableConstraint::Unique {
118 name: c_name,
119 columns: c_cols,
120 },
121 TableConstraint::Unique {
122 name: r_name,
123 columns: r_cols,
124 },
125 ) => {
126 if let (Some(cn), Some(rn)) = (c_name, r_name) {
128 cn != rn
129 } else {
130 c_cols != r_cols
131 }
132 }
133 _ => true,
134 }
135 });
136
137 let temp_table = format!("{}_temp", table);
139
140 let create_temp_table = build_create_table_for_backend(
142 backend,
143 &temp_table,
144 &table_def.columns,
145 &new_constraints,
146 );
147 let create_query = BuiltQuery::CreateTable(Box::new(create_temp_table));
148
149 let column_aliases: Vec<Alias> = table_def
151 .columns
152 .iter()
153 .map(|c| Alias::new(&c.name))
154 .collect();
155 let mut select_query = Query::select();
156 for col_alias in &column_aliases {
157 select_query = select_query.column(col_alias.clone()).to_owned();
158 }
159 select_query = select_query.from(Alias::new(table)).to_owned();
160
161 let insert_stmt = Query::insert()
162 .into_table(Alias::new(&temp_table))
163 .columns(column_aliases.clone())
164 .select_from(select_query)
165 .unwrap()
166 .to_owned();
167 let insert_query = BuiltQuery::Insert(Box::new(insert_stmt));
168
169 let drop_table = Table::drop().table(Alias::new(table)).to_owned();
171 let drop_query = BuiltQuery::DropTable(Box::new(drop_table));
172
173 let rename_query = build_rename_table(&temp_table, table);
175
176 let mut index_queries = Vec::new();
178 for c in &table_def.constraints {
179 if let TableConstraint::Index {
180 name: idx_name,
181 columns: idx_cols,
182 } = c
183 {
184 let index_name = vespertide_naming::build_index_name(
185 table,
186 idx_cols,
187 idx_name.as_deref(),
188 );
189 let mut idx_stmt = sea_query::Index::create();
190 idx_stmt = idx_stmt.name(&index_name).to_owned();
191 for col_name in idx_cols {
192 idx_stmt = idx_stmt.col(Alias::new(col_name)).to_owned();
193 }
194 idx_stmt = idx_stmt.table(Alias::new(table)).to_owned();
195 index_queries.push(BuiltQuery::CreateIndex(Box::new(idx_stmt)));
196 }
197 }
198
199 let mut queries = vec![create_query, insert_query, drop_query, rename_query];
200 queries.extend(index_queries);
201 Ok(queries)
202 } else {
203 let constraint_name = vespertide_naming::build_unique_constraint_name(
210 table,
211 columns,
212 name.as_deref(),
213 );
214 let pg_sql = format!(
219 "ALTER TABLE \"{}\" DROP CONSTRAINT \"{}\"",
220 table, constraint_name
221 );
222 let mysql_sql = format!("ALTER TABLE `{}` DROP INDEX `{}`", table, constraint_name);
223 Ok(vec![BuiltQuery::Raw(RawSql::per_backend(
224 pg_sql.clone(),
225 mysql_sql,
226 pg_sql,
227 ))])
228 }
229 }
230 TableConstraint::ForeignKey { name, columns, .. } => {
231 if *backend == DatabaseBackend::Sqlite {
233 let table_def = current_schema.iter().find(|t| t.name == table).ok_or_else(|| QueryError::Other(format!("Table '{}' not found in current schema. SQLite requires current schema information to remove constraints.", table)))?;
235
236 let mut new_constraints = table_def.constraints.clone();
238 new_constraints.retain(|c| {
239 match (c, constraint) {
240 (
241 TableConstraint::ForeignKey {
242 name: c_name,
243 columns: c_cols,
244 ..
245 },
246 TableConstraint::ForeignKey {
247 name: r_name,
248 columns: r_cols,
249 ..
250 },
251 ) => {
252 if let (Some(cn), Some(rn)) = (c_name, r_name) {
254 cn != rn
255 } else {
256 c_cols != r_cols
257 }
258 }
259 _ => true,
260 }
261 });
262
263 let temp_table = format!("{}_temp", table);
265
266 let create_temp_table = build_create_table_for_backend(
268 backend,
269 &temp_table,
270 &table_def.columns,
271 &new_constraints,
272 );
273 let create_query = BuiltQuery::CreateTable(Box::new(create_temp_table));
274
275 let column_aliases: Vec<Alias> = table_def
277 .columns
278 .iter()
279 .map(|c| Alias::new(&c.name))
280 .collect();
281 let mut select_query = Query::select();
282 for col_alias in &column_aliases {
283 select_query = select_query.column(col_alias.clone()).to_owned();
284 }
285 select_query = select_query.from(Alias::new(table)).to_owned();
286
287 let insert_stmt = Query::insert()
288 .into_table(Alias::new(&temp_table))
289 .columns(column_aliases.clone())
290 .select_from(select_query)
291 .unwrap()
292 .to_owned();
293 let insert_query = BuiltQuery::Insert(Box::new(insert_stmt));
294
295 let drop_table = Table::drop().table(Alias::new(table)).to_owned();
297 let drop_query = BuiltQuery::DropTable(Box::new(drop_table));
298
299 let rename_query = build_rename_table(&temp_table, table);
301
302 let mut index_queries = Vec::new();
304 for c in &table_def.constraints {
305 if let TableConstraint::Index {
306 name: idx_name,
307 columns: idx_cols,
308 } = c
309 {
310 let index_name = vespertide_naming::build_index_name(
311 table,
312 idx_cols,
313 idx_name.as_deref(),
314 );
315 let mut idx_stmt = sea_query::Index::create();
316 idx_stmt = idx_stmt.name(&index_name).to_owned();
317 for col_name in idx_cols {
318 idx_stmt = idx_stmt.col(Alias::new(col_name)).to_owned();
319 }
320 idx_stmt = idx_stmt.table(Alias::new(table)).to_owned();
321 index_queries.push(BuiltQuery::CreateIndex(Box::new(idx_stmt)));
322 }
323 }
324
325 let mut queries = vec![create_query, insert_query, drop_query, rename_query];
326 queries.extend(index_queries);
327 Ok(queries)
328 } else {
329 let constraint_name =
331 vespertide_naming::build_foreign_key_name(table, columns, name.as_deref());
332 let fk_drop = ForeignKey::drop()
333 .name(&constraint_name)
334 .table(Alias::new(table))
335 .to_owned();
336 Ok(vec![BuiltQuery::DropForeignKey(Box::new(fk_drop))])
337 }
338 }
339 TableConstraint::Index { name, columns } => {
340 let index_name = if let Some(n) = name {
342 vespertide_naming::build_index_name(table, columns, Some(n))
344 } else {
345 vespertide_naming::build_index_name(table, columns, None)
347 };
348 let idx_drop = sea_query::Index::drop()
349 .table(Alias::new(table))
350 .name(&index_name)
351 .to_owned();
352 Ok(vec![BuiltQuery::DropIndex(Box::new(idx_drop))])
353 }
354 TableConstraint::Check { name, .. } => {
355 if *backend == DatabaseBackend::Sqlite {
357 let table_def = current_schema.iter().find(|t| t.name == table).ok_or_else(|| QueryError::Other(format!("Table '{}' not found in current schema. SQLite requires current schema information to remove constraints.", table)))?;
359
360 let mut new_constraints = table_def.constraints.clone();
362 new_constraints.retain(|c| match (c, constraint) {
363 (
364 TableConstraint::Check { name: c_name, .. },
365 TableConstraint::Check { name: r_name, .. },
366 ) => c_name != r_name,
367 _ => true,
368 });
369
370 let temp_table = format!("{}_temp", table);
372
373 let create_temp_table = build_create_table_for_backend(
375 backend,
376 &temp_table,
377 &table_def.columns,
378 &new_constraints,
379 );
380 let create_query = BuiltQuery::CreateTable(Box::new(create_temp_table));
381
382 let column_aliases: Vec<Alias> = table_def
384 .columns
385 .iter()
386 .map(|c| Alias::new(&c.name))
387 .collect();
388 let mut select_query = Query::select();
389 for col_alias in &column_aliases {
390 select_query = select_query.column(col_alias.clone()).to_owned();
391 }
392 select_query = select_query.from(Alias::new(table)).to_owned();
393
394 let insert_stmt = Query::insert()
395 .into_table(Alias::new(&temp_table))
396 .columns(column_aliases.clone())
397 .select_from(select_query)
398 .unwrap()
399 .to_owned();
400 let insert_query = BuiltQuery::Insert(Box::new(insert_stmt));
401
402 let drop_table = Table::drop().table(Alias::new(table)).to_owned();
404 let drop_query = BuiltQuery::DropTable(Box::new(drop_table));
405
406 let rename_query = build_rename_table(&temp_table, table);
408
409 let mut index_queries = Vec::new();
411 for c in &table_def.constraints {
412 if let TableConstraint::Index {
413 name: idx_name,
414 columns: idx_cols,
415 } = c
416 {
417 let index_name = vespertide_naming::build_index_name(
418 table,
419 idx_cols,
420 idx_name.as_deref(),
421 );
422 let mut idx_stmt = sea_query::Index::create();
423 idx_stmt = idx_stmt.name(&index_name).to_owned();
424 for col_name in idx_cols {
425 idx_stmt = idx_stmt.col(Alias::new(col_name)).to_owned();
426 }
427 idx_stmt = idx_stmt.table(Alias::new(table)).to_owned();
428 index_queries.push(BuiltQuery::CreateIndex(Box::new(idx_stmt)));
429 }
430 }
431
432 let mut queries = vec![create_query, insert_query, drop_query, rename_query];
433 queries.extend(index_queries);
434 Ok(queries)
435 } else {
436 let pg_sql = format!("ALTER TABLE \"{}\" DROP CONSTRAINT \"{}\"", table, name);
437 let mysql_sql = format!("ALTER TABLE `{}` DROP CHECK `{}`", table, name);
438 Ok(vec![BuiltQuery::Raw(RawSql::per_backend(
439 pg_sql.clone(),
440 mysql_sql,
441 pg_sql,
442 ))])
443 }
444 }
445 }
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451 use crate::sql::types::DatabaseBackend;
452 use insta::{assert_snapshot, with_settings};
453 use rstest::rstest;
454 use vespertide_core::{ColumnDef, ColumnType, SimpleColumnType, TableConstraint, TableDef};
455
456 #[rstest]
457 #[case::remove_constraint_primary_key_postgres(
458 "remove_constraint_primary_key_postgres",
459 DatabaseBackend::Postgres,
460 &["DROP CONSTRAINT \"users_pkey\""]
461 )]
462 #[case::remove_constraint_primary_key_mysql(
463 "remove_constraint_primary_key_mysql",
464 DatabaseBackend::MySql,
465 &["DROP PRIMARY KEY"]
466 )]
467 #[case::remove_constraint_primary_key_sqlite(
468 "remove_constraint_primary_key_sqlite",
469 DatabaseBackend::Sqlite,
470 &["CREATE TABLE \"users_temp\""]
471 )]
472 #[case::remove_constraint_unique_named_postgres(
473 "remove_constraint_unique_named_postgres",
474 DatabaseBackend::Postgres,
475 &["DROP CONSTRAINT \"uq_users__uq_email\""]
476 )]
477 #[case::remove_constraint_unique_named_mysql(
478 "remove_constraint_unique_named_mysql",
479 DatabaseBackend::MySql,
480 &["DROP INDEX `uq_users__uq_email`"]
481 )]
482 #[case::remove_constraint_unique_named_sqlite(
483 "remove_constraint_unique_named_sqlite",
484 DatabaseBackend::Sqlite,
485 &["CREATE TABLE \"users_temp\""]
486 )]
487 #[case::remove_constraint_foreign_key_named_postgres(
488 "remove_constraint_foreign_key_named_postgres",
489 DatabaseBackend::Postgres,
490 &["DROP CONSTRAINT \"fk_users__fk_user\""]
491 )]
492 #[case::remove_constraint_foreign_key_named_mysql(
493 "remove_constraint_foreign_key_named_mysql",
494 DatabaseBackend::MySql,
495 &["DROP FOREIGN KEY `fk_users__fk_user`"]
496 )]
497 #[case::remove_constraint_foreign_key_named_sqlite(
498 "remove_constraint_foreign_key_named_sqlite",
499 DatabaseBackend::Sqlite,
500 &["CREATE TABLE \"users_temp\""]
501 )]
502 #[case::remove_constraint_check_named_postgres(
503 "remove_constraint_check_named_postgres",
504 DatabaseBackend::Postgres,
505 &["DROP CONSTRAINT \"chk_age\""]
506 )]
507 #[case::remove_constraint_check_named_mysql(
508 "remove_constraint_check_named_mysql",
509 DatabaseBackend::MySql,
510 &["DROP CHECK `chk_age`"]
511 )]
512 #[case::remove_constraint_check_named_sqlite(
513 "remove_constraint_check_named_sqlite",
514 DatabaseBackend::Sqlite,
515 &["CREATE TABLE \"users_temp\""]
516 )]
517 fn test_remove_constraint(
518 #[case] title: &str,
519 #[case] backend: DatabaseBackend,
520 #[case] expected: &[&str],
521 ) {
522 let constraint = if title.contains("primary_key") {
523 TableConstraint::PrimaryKey {
524 columns: vec!["id".into()],
525 auto_increment: false,
526 }
527 } else if title.contains("unique") {
528 TableConstraint::Unique {
529 name: Some("uq_email".into()),
530 columns: vec!["email".into()],
531 }
532 } else if title.contains("foreign_key") {
533 TableConstraint::ForeignKey {
534 name: Some("fk_user".into()),
535 columns: vec!["user_id".into()],
536 ref_table: "users".into(),
537 ref_columns: vec!["id".into()],
538 on_delete: None,
539 on_update: None,
540 }
541 } else {
542 TableConstraint::Check {
543 name: "chk_age".into(),
544 expr: "age > 0".into(),
545 }
546 };
547
548 let current_schema = vec![TableDef {
550 name: "users".into(),
551 description: None,
552 columns: if title.contains("check") {
553 vec![
554 ColumnDef {
555 name: "id".into(),
556 r#type: ColumnType::Simple(SimpleColumnType::Integer),
557 nullable: false,
558 default: None,
559 comment: None,
560 primary_key: None,
561 unique: None,
562 index: None,
563 foreign_key: None,
564 },
565 ColumnDef {
566 name: "age".into(),
567 r#type: ColumnType::Simple(SimpleColumnType::Integer),
568 nullable: true,
569 default: None,
570 comment: None,
571 primary_key: None,
572 unique: None,
573 index: None,
574 foreign_key: None,
575 },
576 ]
577 } else if title.contains("foreign_key") {
578 vec![
579 ColumnDef {
580 name: "id".into(),
581 r#type: ColumnType::Simple(SimpleColumnType::Integer),
582 nullable: false,
583 default: None,
584 comment: None,
585 primary_key: None,
586 unique: None,
587 index: None,
588 foreign_key: None,
589 },
590 ColumnDef {
591 name: "user_id".into(),
592 r#type: ColumnType::Simple(SimpleColumnType::Integer),
593 nullable: true,
594 default: None,
595 comment: None,
596 primary_key: None,
597 unique: None,
598 index: None,
599 foreign_key: None,
600 },
601 ]
602 } else {
603 vec![ColumnDef {
605 name: "id".into(),
606 r#type: ColumnType::Simple(SimpleColumnType::Integer),
607 nullable: false,
608 default: None,
609 comment: None,
610 primary_key: None,
611 unique: None,
612 index: None,
613 foreign_key: None,
614 }]
615 },
616 constraints: vec![constraint.clone()],
617 }];
618
619 let result =
620 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
621 let sql = result[0].build(backend);
622 for exp in expected {
623 assert!(
624 sql.contains(exp),
625 "Expected SQL to contain '{}', got: {}",
626 exp,
627 sql
628 );
629 }
630
631 with_settings!({ snapshot_suffix => format!("remove_constraint_{}", title) }, {
632 assert_snapshot!(result.iter().map(|q| q.build(backend)).collect::<Vec<String>>().join("\n"));
633 });
634 }
635
636 #[test]
637 fn test_remove_constraint_primary_key_sqlite_table_not_found() {
638 let constraint = TableConstraint::PrimaryKey {
640 columns: vec!["id".into()],
641 auto_increment: false,
642 };
643 let result = build_remove_constraint(
644 &DatabaseBackend::Sqlite,
645 "nonexistent_table",
646 &constraint,
647 &[], );
649 assert!(result.is_err());
650 let err_msg = result.unwrap_err().to_string();
651 assert!(err_msg.contains("Table 'nonexistent_table' not found in current schema"));
652 }
653
654 #[rstest]
655 #[case::remove_primary_key_with_index_postgres(DatabaseBackend::Postgres)]
656 #[case::remove_primary_key_with_index_mysql(DatabaseBackend::MySql)]
657 #[case::remove_primary_key_with_index_sqlite(DatabaseBackend::Sqlite)]
658 fn test_remove_constraint_primary_key_with_index(#[case] backend: DatabaseBackend) {
659 let constraint = TableConstraint::PrimaryKey {
661 columns: vec!["id".into()],
662 auto_increment: false,
663 };
664 let current_schema = vec![TableDef {
665 name: "users".into(),
666 description: None,
667 columns: vec![ColumnDef {
668 name: "id".into(),
669 r#type: ColumnType::Simple(SimpleColumnType::Integer),
670 nullable: false,
671 default: None,
672 comment: None,
673 primary_key: None,
674 unique: None,
675 index: None,
676 foreign_key: None,
677 }],
678 constraints: vec![
679 constraint.clone(),
680 TableConstraint::Index {
681 name: Some("idx_id".into()),
682 columns: vec!["id".into()],
683 },
684 ],
685 }];
686
687 let result =
688 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
689 let sql = result
690 .iter()
691 .map(|q| q.build(backend))
692 .collect::<Vec<String>>()
693 .join("\n");
694
695 if matches!(backend, DatabaseBackend::Sqlite) {
696 assert!(sql.contains("CREATE INDEX"));
697 assert!(sql.contains("ix_users__idx_id"));
698 }
699
700 with_settings!({ snapshot_suffix => format!("remove_primary_key_with_index_{:?}", backend) }, {
701 assert_snapshot!(sql);
702 });
703 }
704
705 #[rstest]
706 #[case::remove_primary_key_with_unique_constraint_postgres(DatabaseBackend::Postgres)]
707 #[case::remove_primary_key_with_unique_constraint_mysql(DatabaseBackend::MySql)]
708 #[case::remove_primary_key_with_unique_constraint_sqlite(DatabaseBackend::Sqlite)]
709 fn test_remove_constraint_primary_key_with_unique_constraint(#[case] backend: DatabaseBackend) {
710 let constraint = TableConstraint::PrimaryKey {
712 columns: vec!["id".into()],
713 auto_increment: false,
714 };
715 let current_schema = vec![TableDef {
716 name: "users".into(),
717 description: None,
718 columns: vec![ColumnDef {
719 name: "id".into(),
720 r#type: ColumnType::Simple(SimpleColumnType::Integer),
721 nullable: false,
722 default: None,
723 comment: None,
724 primary_key: None,
725 unique: None,
726 index: None,
727 foreign_key: None,
728 }],
729 constraints: vec![
730 constraint.clone(),
731 TableConstraint::Unique {
732 name: Some("uq_email".into()),
733 columns: vec!["email".into()],
734 },
735 ],
736 }];
737
738 let result =
739 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
740 let sql = result
741 .iter()
742 .map(|q| q.build(backend))
743 .collect::<Vec<String>>()
744 .join("\n");
745
746 if matches!(backend, DatabaseBackend::Sqlite) {
747 assert!(sql.contains("CREATE TABLE"));
749 }
750
751 with_settings!({ snapshot_suffix => format!("remove_primary_key_with_unique_constraint_{:?}", backend) }, {
752 assert_snapshot!(sql);
753 });
754 }
755
756 #[test]
757 fn test_remove_constraint_unique_sqlite_table_not_found() {
758 let constraint = TableConstraint::Unique {
760 name: Some("uq_email".into()),
761 columns: vec!["email".into()],
762 };
763 let result = build_remove_constraint(
764 &DatabaseBackend::Sqlite,
765 "nonexistent_table",
766 &constraint,
767 &[], );
769 assert!(result.is_err());
770 let err_msg = result.unwrap_err().to_string();
771 assert!(err_msg.contains("Table 'nonexistent_table' not found in current schema"));
772 }
773
774 #[rstest]
775 #[case::remove_unique_without_name_postgres(DatabaseBackend::Postgres)]
776 #[case::remove_unique_without_name_mysql(DatabaseBackend::MySql)]
777 #[case::remove_unique_without_name_sqlite(DatabaseBackend::Sqlite)]
778 fn test_remove_constraint_unique_without_name(#[case] backend: DatabaseBackend) {
779 let constraint = TableConstraint::Unique {
781 name: None,
782 columns: vec!["email".into()],
783 };
784 let current_schema = vec![TableDef {
785 name: "users".into(),
786 description: None,
787 columns: vec![
788 ColumnDef {
789 name: "id".into(),
790 r#type: ColumnType::Simple(SimpleColumnType::Integer),
791 nullable: false,
792 default: None,
793 comment: None,
794 primary_key: None,
795 unique: None,
796 index: None,
797 foreign_key: None,
798 },
799 ColumnDef {
800 name: "email".into(),
801 r#type: ColumnType::Simple(SimpleColumnType::Text),
802 nullable: true,
803 default: None,
804 comment: None,
805 primary_key: None,
806 unique: None,
807 index: None,
808 foreign_key: None,
809 },
810 ],
811 constraints: vec![constraint.clone()],
812 }];
813
814 let result =
815 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
816 let sql = result
817 .iter()
818 .map(|q| q.build(backend))
819 .collect::<Vec<String>>()
820 .join("\n");
821
822 if !matches!(backend, DatabaseBackend::Sqlite) {
824 assert!(sql.contains("users_email_key") || sql.contains("email"));
825 }
826
827 with_settings!({ snapshot_suffix => format!("remove_unique_without_name_{:?}", backend) }, {
828 assert_snapshot!(sql);
829 });
830 }
831
832 #[rstest]
833 #[case::remove_unique_with_index_postgres(DatabaseBackend::Postgres)]
834 #[case::remove_unique_with_index_mysql(DatabaseBackend::MySql)]
835 #[case::remove_unique_with_index_sqlite(DatabaseBackend::Sqlite)]
836 fn test_remove_constraint_unique_with_index(#[case] backend: DatabaseBackend) {
837 let constraint = TableConstraint::Unique {
839 name: Some("uq_email".into()),
840 columns: vec!["email".into()],
841 };
842 let current_schema = vec![TableDef {
843 name: "users".into(),
844 description: None,
845 columns: vec![
846 ColumnDef {
847 name: "id".into(),
848 r#type: ColumnType::Simple(SimpleColumnType::Integer),
849 nullable: false,
850 default: None,
851 comment: None,
852 primary_key: None,
853 unique: None,
854 index: None,
855 foreign_key: None,
856 },
857 ColumnDef {
858 name: "email".into(),
859 r#type: ColumnType::Simple(SimpleColumnType::Text),
860 nullable: true,
861 default: None,
862 comment: None,
863 primary_key: None,
864 unique: None,
865 index: None,
866 foreign_key: None,
867 },
868 ],
869 constraints: vec![
870 constraint.clone(),
871 TableConstraint::Index {
872 name: Some("idx_id".into()),
873 columns: vec!["id".into()],
874 },
875 ],
876 }];
877
878 let result =
879 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
880 let sql = result
881 .iter()
882 .map(|q| q.build(backend))
883 .collect::<Vec<String>>()
884 .join("\n");
885
886 if matches!(backend, DatabaseBackend::Sqlite) {
887 assert!(sql.contains("CREATE INDEX"));
888 assert!(sql.contains("ix_users__idx_id"));
889 }
890
891 with_settings!({ snapshot_suffix => format!("remove_unique_with_index_{:?}", backend) }, {
892 assert_snapshot!(sql);
893 });
894 }
895
896 #[rstest]
897 #[case::remove_unique_with_other_unique_constraint_postgres(DatabaseBackend::Postgres)]
898 #[case::remove_unique_with_other_unique_constraint_mysql(DatabaseBackend::MySql)]
899 #[case::remove_unique_with_other_unique_constraint_sqlite(DatabaseBackend::Sqlite)]
900 fn test_remove_constraint_unique_with_other_unique_constraint(
901 #[case] backend: DatabaseBackend,
902 ) {
903 let constraint = TableConstraint::Unique {
905 name: Some("uq_email".into()),
906 columns: vec!["email".into()],
907 };
908 let current_schema = vec![TableDef {
909 name: "users".into(),
910 description: None,
911 columns: vec![
912 ColumnDef {
913 name: "id".into(),
914 r#type: ColumnType::Simple(SimpleColumnType::Integer),
915 nullable: false,
916 default: None,
917 comment: None,
918 primary_key: None,
919 unique: None,
920 index: None,
921 foreign_key: None,
922 },
923 ColumnDef {
924 name: "email".into(),
925 r#type: ColumnType::Simple(SimpleColumnType::Text),
926 nullable: true,
927 default: None,
928 comment: None,
929 primary_key: None,
930 unique: None,
931 index: None,
932 foreign_key: None,
933 },
934 ],
935 constraints: vec![
936 constraint.clone(),
937 TableConstraint::Unique {
938 name: Some("uq_name".into()),
939 columns: vec!["name".into()],
940 },
941 ],
942 }];
943
944 let result =
945 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
946 let sql = result
947 .iter()
948 .map(|q| q.build(backend))
949 .collect::<Vec<String>>()
950 .join("\n");
951
952 if matches!(backend, DatabaseBackend::Sqlite) {
953 assert!(sql.contains("CREATE TABLE"));
955 }
956
957 with_settings!({ snapshot_suffix => format!("remove_unique_with_other_unique_constraint_{:?}", backend) }, {
958 assert_snapshot!(sql);
959 });
960 }
961
962 #[test]
963 fn test_remove_constraint_foreign_key_sqlite_table_not_found() {
964 let constraint = TableConstraint::ForeignKey {
966 name: Some("fk_user".into()),
967 columns: vec!["user_id".into()],
968 ref_table: "users".into(),
969 ref_columns: vec!["id".into()],
970 on_delete: None,
971 on_update: None,
972 };
973 let result = build_remove_constraint(
974 &DatabaseBackend::Sqlite,
975 "nonexistent_table",
976 &constraint,
977 &[], );
979 assert!(result.is_err());
980 let err_msg = result.unwrap_err().to_string();
981 assert!(err_msg.contains("Table 'nonexistent_table' not found in current schema"));
982 }
983
984 #[rstest]
985 #[case::remove_foreign_key_without_name_postgres(DatabaseBackend::Postgres)]
986 #[case::remove_foreign_key_without_name_mysql(DatabaseBackend::MySql)]
987 #[case::remove_foreign_key_without_name_sqlite(DatabaseBackend::Sqlite)]
988 fn test_remove_constraint_foreign_key_without_name(#[case] backend: DatabaseBackend) {
989 let constraint = TableConstraint::ForeignKey {
991 name: None,
992 columns: vec!["user_id".into()],
993 ref_table: "users".into(),
994 ref_columns: vec!["id".into()],
995 on_delete: None,
996 on_update: None,
997 };
998 let current_schema = vec![TableDef {
999 name: "posts".into(),
1000 description: None,
1001 columns: vec![
1002 ColumnDef {
1003 name: "id".into(),
1004 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1005 nullable: false,
1006 default: None,
1007 comment: None,
1008 primary_key: None,
1009 unique: None,
1010 index: None,
1011 foreign_key: None,
1012 },
1013 ColumnDef {
1014 name: "user_id".into(),
1015 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1016 nullable: true,
1017 default: None,
1018 comment: None,
1019 primary_key: None,
1020 unique: None,
1021 index: None,
1022 foreign_key: None,
1023 },
1024 ],
1025 constraints: vec![constraint.clone()],
1026 }];
1027
1028 let result =
1029 build_remove_constraint(&backend, "posts", &constraint, ¤t_schema).unwrap();
1030 let sql = result
1031 .iter()
1032 .map(|q| q.build(backend))
1033 .collect::<Vec<String>>()
1034 .join("\n");
1035
1036 if !matches!(backend, DatabaseBackend::Sqlite) {
1038 assert!(sql.contains("posts_user_id_fkey") || sql.contains("user_id"));
1039 }
1040
1041 with_settings!({ snapshot_suffix => format!("remove_foreign_key_without_name_{:?}", backend) }, {
1042 assert_snapshot!(sql);
1043 });
1044 }
1045
1046 #[rstest]
1047 #[case::remove_foreign_key_with_index_postgres(DatabaseBackend::Postgres)]
1048 #[case::remove_foreign_key_with_index_mysql(DatabaseBackend::MySql)]
1049 #[case::remove_foreign_key_with_index_sqlite(DatabaseBackend::Sqlite)]
1050 fn test_remove_constraint_foreign_key_with_index(#[case] backend: DatabaseBackend) {
1051 let constraint = TableConstraint::ForeignKey {
1053 name: Some("fk_user".into()),
1054 columns: vec!["user_id".into()],
1055 ref_table: "users".into(),
1056 ref_columns: vec!["id".into()],
1057 on_delete: None,
1058 on_update: None,
1059 };
1060 let current_schema = vec![TableDef {
1061 name: "posts".into(),
1062 description: None,
1063 columns: vec![
1064 ColumnDef {
1065 name: "id".into(),
1066 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1067 nullable: false,
1068 default: None,
1069 comment: None,
1070 primary_key: None,
1071 unique: None,
1072 index: None,
1073 foreign_key: None,
1074 },
1075 ColumnDef {
1076 name: "user_id".into(),
1077 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1078 nullable: true,
1079 default: None,
1080 comment: None,
1081 primary_key: None,
1082 unique: None,
1083 index: None,
1084 foreign_key: None,
1085 },
1086 ],
1087 constraints: vec![
1088 constraint.clone(),
1089 TableConstraint::Index {
1090 name: Some("idx_user_id".into()),
1091 columns: vec!["user_id".into()],
1092 },
1093 ],
1094 }];
1095
1096 let result =
1097 build_remove_constraint(&backend, "posts", &constraint, ¤t_schema).unwrap();
1098 let sql = result
1099 .iter()
1100 .map(|q| q.build(backend))
1101 .collect::<Vec<String>>()
1102 .join("\n");
1103
1104 if matches!(backend, DatabaseBackend::Sqlite) {
1105 assert!(sql.contains("CREATE INDEX"));
1106 assert!(sql.contains("idx_user_id"));
1107 }
1108
1109 with_settings!({ snapshot_suffix => format!("remove_foreign_key_with_index_{:?}", backend) }, {
1110 assert_snapshot!(sql);
1111 });
1112 }
1113
1114 #[rstest]
1115 #[case::remove_foreign_key_with_unique_constraint_postgres(DatabaseBackend::Postgres)]
1116 #[case::remove_foreign_key_with_unique_constraint_mysql(DatabaseBackend::MySql)]
1117 #[case::remove_foreign_key_with_unique_constraint_sqlite(DatabaseBackend::Sqlite)]
1118 fn test_remove_constraint_foreign_key_with_unique_constraint(#[case] backend: DatabaseBackend) {
1119 let constraint = TableConstraint::ForeignKey {
1121 name: Some("fk_user".into()),
1122 columns: vec!["user_id".into()],
1123 ref_table: "users".into(),
1124 ref_columns: vec!["id".into()],
1125 on_delete: None,
1126 on_update: None,
1127 };
1128 let current_schema = vec![TableDef {
1129 name: "posts".into(),
1130 description: None,
1131 columns: vec![
1132 ColumnDef {
1133 name: "id".into(),
1134 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1135 nullable: false,
1136 default: None,
1137 comment: None,
1138 primary_key: None,
1139 unique: None,
1140 index: None,
1141 foreign_key: None,
1142 },
1143 ColumnDef {
1144 name: "user_id".into(),
1145 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1146 nullable: true,
1147 default: None,
1148 comment: None,
1149 primary_key: None,
1150 unique: None,
1151 index: None,
1152 foreign_key: None,
1153 },
1154 ],
1155 constraints: vec![
1156 constraint.clone(),
1157 TableConstraint::Unique {
1158 name: Some("uq_user_id".into()),
1159 columns: vec!["user_id".into()],
1160 },
1161 ],
1162 }];
1163
1164 let result =
1165 build_remove_constraint(&backend, "posts", &constraint, ¤t_schema).unwrap();
1166 let sql = result
1167 .iter()
1168 .map(|q| q.build(backend))
1169 .collect::<Vec<String>>()
1170 .join("\n");
1171
1172 if matches!(backend, DatabaseBackend::Sqlite) {
1173 assert!(sql.contains("CREATE TABLE"));
1175 }
1176
1177 with_settings!({ snapshot_suffix => format!("remove_foreign_key_with_unique_constraint_{:?}", backend) }, {
1178 assert_snapshot!(sql);
1179 });
1180 }
1181
1182 #[test]
1183 fn test_remove_constraint_check_sqlite_table_not_found() {
1184 let constraint = TableConstraint::Check {
1186 name: "chk_age".into(),
1187 expr: "age > 0".into(),
1188 };
1189 let result = build_remove_constraint(
1190 &DatabaseBackend::Sqlite,
1191 "nonexistent_table",
1192 &constraint,
1193 &[], );
1195 assert!(result.is_err());
1196 let err_msg = result.unwrap_err().to_string();
1197 assert!(err_msg.contains("Table 'nonexistent_table' not found in current schema"));
1198 }
1199
1200 #[rstest]
1201 #[case::remove_check_with_index_postgres(DatabaseBackend::Postgres)]
1202 #[case::remove_check_with_index_mysql(DatabaseBackend::MySql)]
1203 #[case::remove_check_with_index_sqlite(DatabaseBackend::Sqlite)]
1204 fn test_remove_constraint_check_with_index(#[case] backend: DatabaseBackend) {
1205 let constraint = TableConstraint::Check {
1207 name: "chk_age".into(),
1208 expr: "age > 0".into(),
1209 };
1210 let current_schema = vec![TableDef {
1211 name: "users".into(),
1212 description: None,
1213 columns: vec![
1214 ColumnDef {
1215 name: "id".into(),
1216 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1217 nullable: false,
1218 default: None,
1219 comment: None,
1220 primary_key: None,
1221 unique: None,
1222 index: None,
1223 foreign_key: None,
1224 },
1225 ColumnDef {
1226 name: "age".into(),
1227 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1228 nullable: true,
1229 default: None,
1230 comment: None,
1231 primary_key: None,
1232 unique: None,
1233 index: None,
1234 foreign_key: None,
1235 },
1236 ],
1237 constraints: vec![
1238 constraint.clone(),
1239 TableConstraint::Index {
1240 name: Some("idx_age".into()),
1241 columns: vec!["age".into()],
1242 },
1243 ],
1244 }];
1245
1246 let result =
1247 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
1248 let sql = result
1249 .iter()
1250 .map(|q| q.build(backend))
1251 .collect::<Vec<String>>()
1252 .join("\n");
1253
1254 if matches!(backend, DatabaseBackend::Sqlite) {
1255 assert!(sql.contains("CREATE INDEX"));
1256 assert!(sql.contains("idx_age"));
1257 }
1258
1259 with_settings!({ snapshot_suffix => format!("remove_check_with_index_{:?}", backend) }, {
1260 assert_snapshot!(sql);
1261 });
1262 }
1263
1264 #[rstest]
1265 #[case::remove_check_with_unique_constraint_postgres(DatabaseBackend::Postgres)]
1266 #[case::remove_check_with_unique_constraint_mysql(DatabaseBackend::MySql)]
1267 #[case::remove_check_with_unique_constraint_sqlite(DatabaseBackend::Sqlite)]
1268 fn test_remove_constraint_check_with_unique_constraint(#[case] backend: DatabaseBackend) {
1269 let constraint = TableConstraint::Check {
1271 name: "chk_age".into(),
1272 expr: "age > 0".into(),
1273 };
1274 let current_schema = vec![TableDef {
1275 name: "users".into(),
1276 description: None,
1277 columns: vec![
1278 ColumnDef {
1279 name: "id".into(),
1280 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1281 nullable: false,
1282 default: None,
1283 comment: None,
1284 primary_key: None,
1285 unique: None,
1286 index: None,
1287 foreign_key: None,
1288 },
1289 ColumnDef {
1290 name: "age".into(),
1291 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1292 nullable: true,
1293 default: None,
1294 comment: None,
1295 primary_key: None,
1296 unique: None,
1297 index: None,
1298 foreign_key: None,
1299 },
1300 ],
1301 constraints: vec![
1302 constraint.clone(),
1303 TableConstraint::Unique {
1304 name: Some("uq_age".into()),
1305 columns: vec!["age".into()],
1306 },
1307 ],
1308 }];
1309
1310 let result =
1311 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
1312 let sql = result
1313 .iter()
1314 .map(|q| q.build(backend))
1315 .collect::<Vec<String>>()
1316 .join("\n");
1317
1318 if matches!(backend, DatabaseBackend::Sqlite) {
1319 assert!(sql.contains("CREATE TABLE"));
1321 }
1322
1323 with_settings!({ snapshot_suffix => format!("remove_check_with_unique_constraint_{:?}", backend) }, {
1324 assert_snapshot!(sql);
1325 });
1326 }
1327
1328 #[rstest]
1329 #[case::remove_unique_with_other_constraints_postgres(DatabaseBackend::Postgres)]
1330 #[case::remove_unique_with_other_constraints_mysql(DatabaseBackend::MySql)]
1331 #[case::remove_unique_with_other_constraints_sqlite(DatabaseBackend::Sqlite)]
1332 fn test_remove_constraint_unique_with_other_constraints(#[case] backend: DatabaseBackend) {
1333 let constraint = TableConstraint::Unique {
1335 name: Some("uq_email".into()),
1336 columns: vec!["email".into()],
1337 };
1338 let current_schema = vec![TableDef {
1339 name: "users".into(),
1340 description: None,
1341 columns: vec![
1342 ColumnDef {
1343 name: "id".into(),
1344 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1345 nullable: false,
1346 default: None,
1347 comment: None,
1348 primary_key: None,
1349 unique: None,
1350 index: None,
1351 foreign_key: None,
1352 },
1353 ColumnDef {
1354 name: "email".into(),
1355 r#type: ColumnType::Simple(SimpleColumnType::Text),
1356 nullable: true,
1357 default: None,
1358 comment: None,
1359 primary_key: None,
1360 unique: None,
1361 index: None,
1362 foreign_key: None,
1363 },
1364 ],
1365 constraints: vec![
1366 TableConstraint::PrimaryKey {
1367 columns: vec!["id".into()],
1368 auto_increment: false,
1369 },
1370 constraint.clone(),
1371 TableConstraint::Check {
1372 name: "chk_email".into(),
1373 expr: "email IS NOT NULL".into(),
1374 },
1375 ],
1376 }];
1377
1378 let result =
1379 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
1380 let sql = result
1381 .iter()
1382 .map(|q| q.build(backend))
1383 .collect::<Vec<String>>()
1384 .join("\n");
1385
1386 assert!(sql.contains("DROP") || sql.contains("CREATE TABLE"));
1388
1389 with_settings!({ snapshot_suffix => format!("remove_unique_with_other_constraints_{:?}", backend) }, {
1390 assert_snapshot!(sql);
1391 });
1392 }
1393
1394 #[rstest]
1395 #[case::remove_foreign_key_with_other_constraints_postgres(DatabaseBackend::Postgres)]
1396 #[case::remove_foreign_key_with_other_constraints_mysql(DatabaseBackend::MySql)]
1397 #[case::remove_foreign_key_with_other_constraints_sqlite(DatabaseBackend::Sqlite)]
1398 fn test_remove_constraint_foreign_key_with_other_constraints(#[case] backend: DatabaseBackend) {
1399 let constraint = TableConstraint::ForeignKey {
1401 name: Some("fk_user".into()),
1402 columns: vec!["user_id".into()],
1403 ref_table: "users".into(),
1404 ref_columns: vec!["id".into()],
1405 on_delete: None,
1406 on_update: None,
1407 };
1408 let current_schema = vec![TableDef {
1409 name: "posts".into(),
1410 description: None,
1411 columns: vec![
1412 ColumnDef {
1413 name: "id".into(),
1414 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1415 nullable: false,
1416 default: None,
1417 comment: None,
1418 primary_key: None,
1419 unique: None,
1420 index: None,
1421 foreign_key: None,
1422 },
1423 ColumnDef {
1424 name: "user_id".into(),
1425 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1426 nullable: true,
1427 default: None,
1428 comment: None,
1429 primary_key: None,
1430 unique: None,
1431 index: None,
1432 foreign_key: None,
1433 },
1434 ],
1435 constraints: vec![
1436 TableConstraint::PrimaryKey {
1437 columns: vec!["id".into()],
1438 auto_increment: false,
1439 },
1440 constraint.clone(),
1441 TableConstraint::Unique {
1442 name: Some("uq_user_id".into()),
1443 columns: vec!["user_id".into()],
1444 },
1445 TableConstraint::Check {
1446 name: "chk_user_id".into(),
1447 expr: "user_id > 0".into(),
1448 },
1449 ],
1450 }];
1451
1452 let result =
1453 build_remove_constraint(&backend, "posts", &constraint, ¤t_schema).unwrap();
1454 let sql = result
1455 .iter()
1456 .map(|q| q.build(backend))
1457 .collect::<Vec<String>>()
1458 .join("\n");
1459
1460 assert!(sql.contains("DROP") || sql.contains("CREATE TABLE"));
1462
1463 with_settings!({ snapshot_suffix => format!("remove_foreign_key_with_other_constraints_{:?}", backend) }, {
1464 assert_snapshot!(sql);
1465 });
1466 }
1467
1468 #[rstest]
1469 #[case::remove_check_with_other_constraints_postgres(DatabaseBackend::Postgres)]
1470 #[case::remove_check_with_other_constraints_mysql(DatabaseBackend::MySql)]
1471 #[case::remove_check_with_other_constraints_sqlite(DatabaseBackend::Sqlite)]
1472 fn test_remove_constraint_check_with_other_constraints(#[case] backend: DatabaseBackend) {
1473 let constraint = TableConstraint::Check {
1475 name: "chk_age".into(),
1476 expr: "age > 0".into(),
1477 };
1478 let current_schema = vec![TableDef {
1479 name: "users".into(),
1480 description: None,
1481 columns: vec![
1482 ColumnDef {
1483 name: "id".into(),
1484 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1485 nullable: false,
1486 default: None,
1487 comment: None,
1488 primary_key: None,
1489 unique: None,
1490 index: None,
1491 foreign_key: None,
1492 },
1493 ColumnDef {
1494 name: "age".into(),
1495 r#type: ColumnType::Simple(SimpleColumnType::Integer),
1496 nullable: true,
1497 default: None,
1498 comment: None,
1499 primary_key: None,
1500 unique: None,
1501 index: None,
1502 foreign_key: None,
1503 },
1504 ],
1505 constraints: vec![
1506 TableConstraint::PrimaryKey {
1507 columns: vec!["id".into()],
1508 auto_increment: false,
1509 },
1510 TableConstraint::Unique {
1511 name: Some("uq_age".into()),
1512 columns: vec!["age".into()],
1513 },
1514 constraint.clone(),
1515 ],
1516 }];
1517
1518 let result =
1519 build_remove_constraint(&backend, "users", &constraint, ¤t_schema).unwrap();
1520 let sql = result
1521 .iter()
1522 .map(|q| q.build(backend))
1523 .collect::<Vec<String>>()
1524 .join("\n");
1525
1526 assert!(sql.contains("DROP") || sql.contains("CREATE TABLE"));
1528
1529 with_settings!({ snapshot_suffix => format!("remove_check_with_other_constraints_{:?}", backend) }, {
1530 assert_snapshot!(sql);
1531 });
1532 }
1533
1534 #[rstest]
1535 #[case::remove_index_with_custom_inline_name_postgres(DatabaseBackend::Postgres)]
1536 #[case::remove_index_with_custom_inline_name_mysql(DatabaseBackend::MySql)]
1537 #[case::remove_index_with_custom_inline_name_sqlite(DatabaseBackend::Sqlite)]
1538 fn test_remove_constraint_index_with_custom_inline_name(#[case] backend: DatabaseBackend) {
1539 let constraint = TableConstraint::Index {
1542 name: Some("custom_idx_email".into()),
1543 columns: vec!["email".into()],
1544 };
1545
1546 let schema = vec![TableDef {
1547 name: "users".to_string(),
1548 description: None,
1549 columns: vec![ColumnDef {
1550 name: "email".to_string(),
1551 r#type: ColumnType::Simple(SimpleColumnType::Text),
1552 nullable: true,
1553 default: None,
1554 comment: None,
1555 primary_key: None,
1556 unique: None,
1557 index: Some(vespertide_core::StrOrBoolOrArray::Str(
1558 "custom_idx_email".into(),
1559 )),
1560 foreign_key: None,
1561 }],
1562 constraints: vec![],
1563 }];
1564
1565 let result = build_remove_constraint(&backend, "users", &constraint, &schema);
1566 assert!(result.is_ok());
1567 let sql = result
1568 .unwrap()
1569 .iter()
1570 .map(|q| q.build(backend))
1571 .collect::<Vec<String>>()
1572 .join("\n");
1573
1574 assert!(sql.contains("custom_idx_email"));
1576
1577 with_settings!({ snapshot_suffix => format!("remove_index_custom_name_{:?}", backend) }, {
1578 assert_snapshot!(sql);
1579 });
1580 }
1581}