Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,10 @@ pub struct MyUserId(pub UserId);
+ Support timestamp with different timezone / resolution
+ Added parquet example

### Bug Fixes

* [sea-orm-migration] PostgreSQL `drop_everything` now drops custom types with `CASCADE`

### Breaking Changes

Please read [SeaQuery's breaking changes](https://github.com/SeaQL/sea-query/blob/master/CHANGELOG.md#breaking-changes) as well. But for most compile errors, you can simply add `use sea_orm::ExprTrait;` in scope.
Expand Down
2 changes: 1 addition & 1 deletion sea-orm-migration/src/migrator/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ pub async fn drop_everything<C: ConnectionTrait>(db: &C) -> Result<(), DbErr> {
let type_name: String = row.try_get("", "typname")?;
info!("Dropping type '{}'", type_name);
let mut stmt = Type::drop();
stmt.name(Alias::new(&type_name));
stmt.name(Alias::new(&type_name)).if_exists().cascade();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restrict cascading type drops to the target schema

query_pg_types() intentionally limits this cleanup to get_current_schema(db) (sea-orm-migration/src/migrator/queries.rs:117-142), and the migration tests explicitly support custom schema_search_path values (sea-orm-migration/tests/main.rs:58-63). Adding CASCADE here changes the failure mode for PostgreSQL: if another schema has a table/function/operator that references one of these types, DROP TYPE ... CASCADE will recursively delete that external object instead of stopping with a dependency error. In shared-database or multi-schema deployments, Migrator::fresh() can now destroy objects outside the schema it is supposed to be refreshing.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that’s the problem. Users might not want objects in other schemas to get dropped. I think we can add a flag to control this behavior.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts, @tyt2y3?

db.execute(&stmt).await?;
info!("Type '{}' has been dropped", type_name);
}
Expand Down
38 changes: 36 additions & 2 deletions sea-orm-migration/tests/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,23 @@ mod inner {
let url = format!("{url}/{db_name}");
let db = db_connect(url).await?;

// Create the extension and a custom type
// Create the extension, a custom type, and dependent objects.
db.execute_unprepared("CREATE EXTENSION IF NOT EXISTS citext")
.await?;
db.execute_unprepared("CREATE TYPE \"UserFruit\" AS ENUM ('Apple', 'Banana')")
.await?;
db.execute_unprepared(r#"CREATE DOMAIN "UserFruitDomain" AS "UserFruit""#)
.await?;
db.execute_unprepared(
r#"CREATE FUNCTION format_user_fruit("UserFruit") RETURNS text LANGUAGE SQL AS $$ SELECT $1::text $$"#,
)
.await?;

// Run the fresh migration
Migrator::fresh(&db).await?;

// Check that the custom type was dropped and the extension's type was not
// Check that the custom type and its dependent objects were dropped,
// and the extension's type was not.
let citext_exists: Option<i32> = db
.query_one_raw(Statement::from_string(
DbBackend::Postgres,
Expand All @@ -63,6 +70,33 @@ mod inner {
"the UserFruit type should have been dropped"
);

let user_fruit_domain_exists: Option<i32> = db
.query_one_raw(Statement::from_string(
DbBackend::Postgres,
r#"SELECT 1 as "value" FROM pg_type WHERE typname = 'UserFruitDomain'"#.to_owned(),
))
.await?
.map(|row| row.try_get("", "value").unwrap());

assert_eq!(
user_fruit_domain_exists, None,
"the dependent UserFruitDomain type should have been dropped"
);

let format_user_fruit_exists: Option<i32> = db
.query_one_raw(Statement::from_string(
DbBackend::Postgres,
r#"SELECT 1 as "value" FROM pg_proc WHERE proname = 'format_user_fruit'"#
.to_owned(),
))
.await?
.map(|row| row.try_get("", "value").unwrap());

assert_eq!(
format_user_fruit_exists, None,
"the dependent format_user_fruit function should have been dropped"
);

Ok(())
}
}
Loading