Skip to content

Commit 1431408

Browse files
committed
Fix schema sync drop unique constraint #2994
1 parent 50ef02c commit 1431408

3 files changed

Lines changed: 96 additions & 3 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ sea-orm-macros = { version = "~2.0.0-rc.37", path = "sea-orm-macros", default-fe
6262
"async",
6363
"strum",
6464
] }
65-
sea-query = { version = "=1.0.0-rc.31", default-features = false, features = [
65+
sea-query = { version = "=1.0.0-rc.32", default-features = false, features = [
6666
"thread-safe",
6767
"hashable-value",
6868
"backend-mysql",

src/schema/builder.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,9 +459,31 @@ impl EntitySchemaInfo {
459459
}
460460
}
461461
if !has_index {
462-
if let Some(drop_existing) = existing_index.get_index_spec().get_name() {
463-
db.execute(sea_query::Index::drop().name(drop_existing))
462+
if let Some(drop_existing) = existing_index
463+
.get_index_spec()
464+
.get_name()
465+
.map(|s| s.to_owned())
466+
{
467+
if db_backend == DbBackend::Postgres {
468+
// On PostgreSQL, unique indexes created via column-level UNIQUE
469+
// (e.g. ADD COLUMN ... UNIQUE) are backed by a named constraint.
470+
// DROP INDEX fails on constraint-owned indexes; use
471+
// ALTER TABLE ... DROP CONSTRAINT instead.
472+
db.execute(
473+
TableAlterStatement::new()
474+
.table(
475+
self.table
476+
.get_table_name()
477+
.expect("Checked above")
478+
.clone(),
479+
)
480+
.drop_constraint(drop_existing),
481+
)
464482
.await?;
483+
} else {
484+
db.execute(sea_query::Index::drop().name(drop_existing))
485+
.await?;
486+
}
465487
}
466488
}
467489
}

tests/schema_sync_tests.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,39 @@ mod product_v2 {
6060
impl ActiveModelBehavior for ActiveModel {}
6161
}
6262

63+
// Scenario 4a: initial version — column has UNIQUE.
64+
mod order_v1 {
65+
use sea_orm::entity::prelude::*;
66+
67+
#[sea_orm::model]
68+
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
69+
#[sea_orm(table_name = "sync_order")]
70+
pub struct Model {
71+
#[sea_orm(primary_key)]
72+
pub id: i32,
73+
#[sea_orm(unique)]
74+
pub ref_no: String,
75+
}
76+
77+
impl ActiveModelBehavior for ActiveModel {}
78+
}
79+
80+
// Scenario 4b: UNIQUE removed from the column.
81+
mod order_v2 {
82+
use sea_orm::entity::prelude::*;
83+
84+
#[sea_orm::model]
85+
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
86+
#[sea_orm(table_name = "sync_order")]
87+
pub struct Model {
88+
#[sea_orm(primary_key)]
89+
pub id: i32,
90+
pub ref_no: String,
91+
}
92+
93+
impl ActiveModelBehavior for ActiveModel {}
94+
}
95+
6396
// Scenario 3a: initial version — column exists without UNIQUE.
6497
mod user_v1 {
6598
use sea_orm::entity::prelude::*;
@@ -206,6 +239,44 @@ async fn test_sync_make_existing_column_unique() -> Result<(), DbErr> {
206239
Ok(())
207240
}
208241

242+
/// Regression test for <https://github.com/SeaQL/sea-orm/issues/2994>.
243+
///
244+
/// A column marked `#[sea_orm(unique)]` is synced, then the unique attribute is
245+
/// removed. The second sync must drop the PostgreSQL constraint without error.
246+
#[sea_orm_macros::test]
247+
#[cfg(feature = "sqlx-postgres")]
248+
async fn test_sync_drop_unique_constraint() -> Result<(), DbErr> {
249+
let ctx = TestContext::new("test_sync_drop_unique_constraint").await;
250+
let db = &ctx.db;
251+
252+
#[cfg(feature = "schema-sync")]
253+
{
254+
// First sync: creates the table with the unique constraint
255+
db.get_schema_builder()
256+
.register(order_v1::Entity)
257+
.sync(db)
258+
.await?;
259+
260+
assert!(
261+
pg_index_exists(db, "sync_order", "sync_order_ref_no_key").await?,
262+
"unique constraint should exist after first sync"
263+
);
264+
265+
// Second sync: unique is removed — must not error on PostgreSQL
266+
db.get_schema_builder()
267+
.register(order_v2::Entity)
268+
.sync(db)
269+
.await?;
270+
271+
assert!(
272+
!pg_index_exists(db, "sync_order", "sync_order_ref_no_key").await?,
273+
"unique constraint should be gone after second sync"
274+
);
275+
}
276+
277+
Ok(())
278+
}
279+
209280
#[cfg(feature = "sqlx-postgres")]
210281
async fn pg_index_exists(db: &DatabaseConnection, table: &str, index: &str) -> Result<bool, DbErr> {
211282
db.query_one(

0 commit comments

Comments
 (0)