Skip to content

Commit fda4159

Browse files
authored
Sqlx Cli: Added force flag to drop database for postgres (#2873)
* Updated ahash so it can compile on mac * Updated MigrateDatabase Trait + related functions * Postgres force drop database flag impl * Update migrate.rs * Reverted MigrateDatabase Trait * Update migrate.rs * Update migrate.rs * Added force drop database fn impl * Add Migrate Error * Fixed changed function name
1 parent ed1b030 commit fda4159

File tree

10 files changed

+78
-15
lines changed

10 files changed

+78
-15
lines changed

Cargo.lock

+9-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sqlx-cli/src/database.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub async fn create(connect_opts: &ConnectOpts) -> anyhow::Result<()> {
2323
Ok(())
2424
}
2525

26-
pub async fn drop(connect_opts: &ConnectOpts, confirm: bool) -> anyhow::Result<()> {
26+
pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> anyhow::Result<()> {
2727
if confirm && !ask_to_continue_drop(connect_opts.required_db_url()?) {
2828
return Ok(());
2929
}
@@ -33,7 +33,11 @@ pub async fn drop(connect_opts: &ConnectOpts, confirm: bool) -> anyhow::Result<(
3333
let exists = crate::retry_connect_errors(connect_opts, Any::database_exists).await?;
3434

3535
if exists {
36-
Any::drop_database(connect_opts.required_db_url()?).await?;
36+
if force {
37+
Any::force_drop_database(connect_opts.required_db_url()?).await?;
38+
} else {
39+
Any::drop_database(connect_opts.required_db_url()?).await?;
40+
}
3741
}
3842

3943
Ok(())
@@ -43,8 +47,9 @@ pub async fn reset(
4347
migration_source: &str,
4448
connect_opts: &ConnectOpts,
4549
confirm: bool,
50+
force: bool,
4651
) -> anyhow::Result<()> {
47-
drop(connect_opts, confirm).await?;
52+
drop(connect_opts, confirm, force).await?;
4853
setup(migration_source, connect_opts).await
4954
}
5055

sqlx-cli/src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,14 @@ pub async fn run(opt: Opt) -> Result<()> {
7474
DatabaseCommand::Drop {
7575
confirmation,
7676
connect_opts,
77-
} => database::drop(&connect_opts, !confirmation.yes).await?,
77+
force,
78+
} => database::drop(&connect_opts, !confirmation.yes, force).await?,
7879
DatabaseCommand::Reset {
7980
confirmation,
8081
source,
8182
connect_opts,
82-
} => database::reset(&source, &connect_opts, !confirmation.yes).await?,
83+
force,
84+
} => database::reset(&source, &connect_opts, !confirmation.yes, force).await?,
8385
DatabaseCommand::Setup {
8486
source,
8587
connect_opts,

sqlx-cli/src/opt.rs

+8
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ pub enum DatabaseCommand {
7676

7777
#[clap(flatten)]
7878
connect_opts: ConnectOpts,
79+
80+
/// PostgreSQL only: force drops the database.
81+
#[clap(long, short, default_value = "false")]
82+
force: bool,
7983
},
8084

8185
/// Drops the database specified in your DATABASE_URL, re-creates it, and runs any pending migrations.
@@ -88,6 +92,10 @@ pub enum DatabaseCommand {
8892

8993
#[clap(flatten)]
9094
connect_opts: ConnectOpts,
95+
96+
/// PostgreSQL only: force drops the database.
97+
#[clap(long, short, default_value = "false")]
98+
force: bool,
9199
},
92100

93101
/// Creates the database specified in your DATABASE_URL and runs any pending migrations.

sqlx-core/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ uuid = { workspace = true, optional = true }
5151

5252
async-io = { version = "1.9.0", optional = true }
5353
paste = "1.0.6"
54-
ahash = "0.8"
54+
ahash = "0.8.6"
5555
atoi = "2.0"
5656

5757
bytes = "1.1.0"

sqlx-core/src/any/driver.rs

+6
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ impl AnyDriver {
7070
create_database: DebugFn(DB::create_database),
7171
database_exists: DebugFn(DB::database_exists),
7272
drop_database: DebugFn(DB::drop_database),
73+
force_drop_database: DebugFn(DB::force_drop_database),
7374
}),
7475
..Self::without_migrate::<DB>()
7576
}
@@ -94,6 +95,7 @@ pub struct AnyMigrateDatabase {
9495
create_database: DebugFn<fn(&str) -> BoxFuture<'_, crate::Result<()>>>,
9596
database_exists: DebugFn<fn(&str) -> BoxFuture<'_, crate::Result<bool>>>,
9697
drop_database: DebugFn<fn(&str) -> BoxFuture<'_, crate::Result<()>>>,
98+
force_drop_database: DebugFn<fn(&str) -> BoxFuture<'_, crate::Result<()>>>,
9799
}
98100

99101
impl AnyMigrateDatabase {
@@ -108,6 +110,10 @@ impl AnyMigrateDatabase {
108110
pub fn drop_database<'a>(&self, url: &'a str) -> BoxFuture<'a, crate::Result<()>> {
109111
(self.drop_database)(url)
110112
}
113+
114+
pub fn force_drop_database<'a>(&self, url: &'a str) -> BoxFuture<'a, crate::Result<()>> {
115+
(self.force_drop_database)(url)
116+
}
111117
}
112118

113119
/// Install the list of drivers for [`AnyConnection`] to use.

sqlx-core/src/any/migrate.rs

+9
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ impl MigrateDatabase for Any {
3232
.await
3333
})
3434
}
35+
36+
fn force_drop_database(url: &str) -> BoxFuture<'_, Result<(), Error>> {
37+
Box::pin(async {
38+
driver::from_url_str(url)?
39+
.get_migrate_database()?
40+
.force_drop_database(url)
41+
.await
42+
})
43+
}
3544
}
3645

3746
impl Migrate for AnyConnection {

sqlx-core/src/migrate/error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ pub enum MigrateError {
2424
#[error("migration {0} is newer than the latest applied migration {1}")]
2525
VersionTooNew(i64, i64),
2626

27+
#[error("database driver does not support force-dropping a database (Only PostgreSQL)")]
28+
ForceNotSupported,
29+
2730
#[deprecated = "migration types are now inferred"]
2831
#[error("cannot mix reversible migrations with simple migrations. All migrations should be reversible or simple migrations")]
2932
InvalidMixReversibleAndSimple,

sqlx-core/src/migrate/migrate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ pub trait MigrateDatabase {
1515
// drop database in url
1616
// uses a maintenance database depending on driver
1717
fn drop_database(url: &str) -> BoxFuture<'_, Result<(), Error>>;
18+
19+
// force drop database in url
20+
// uses a maintenance database depending on driver
21+
fn force_drop_database(_url: &str) -> BoxFuture<'_, Result<(), Error>> {
22+
Box::pin(async { Err(MigrateError::ForceNotSupported)? })
23+
}
1824
}
1925

2026
// 'e = Executor

sqlx-postgres/src/migrate.rs

+24
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,30 @@ impl MigrateDatabase for Postgres {
8585
Ok(())
8686
})
8787
}
88+
89+
fn force_drop_database(url: &str) -> BoxFuture<'_, Result<(), Error>> {
90+
Box::pin(async move {
91+
let (options, database) = parse_for_maintenance(url)?;
92+
let mut conn = options.connect().await?;
93+
94+
let row: (String,) = query_as("SELECT current_setting('server_version_num')")
95+
.fetch_one(&mut conn)
96+
.await?;
97+
98+
let version = row.0.parse::<i32>().unwrap();
99+
100+
let pid_type = if version >= 90200 { "pid" } else { "procpid" };
101+
102+
conn.execute(&*format!(
103+
"SELECT pg_terminate_backend(pg_stat_activity.{pid_type}) FROM pg_stat_activity \
104+
WHERE pg_stat_activity.datname = {} AND {pid_type} <> pg_backend_pid()",
105+
database.replace('"', "\"\""),
106+
))
107+
.await?;
108+
109+
Self::drop_database(url).await
110+
})
111+
}
88112
}
89113

90114
impl Migrate for PgConnection {

0 commit comments

Comments
 (0)