Skip to content

Commit ed26b2a

Browse files
committed
fix: Drop existing tables in add-tables mode before restore
When using Add mode (init --include-tables on existing database), the specified tables are now dropped before restore_schema runs. This fixes 'relation already exists' errors when the tables already exist on the target database. Taariq Lewis, SerenAI, Paloma, and Volume at https://serendb.com
1 parent 7012d91 commit ed26b2a

1 file changed

Lines changed: 28 additions & 2 deletions

File tree

src/commands/init.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,10 @@ pub async fn init(
422422
let source_db_url = replace_database_in_url(source_url, &db_info.name)?;
423423
let target_db_url = replace_database_in_url(target_url, &db_info.name)?;
424424

425+
// Track if we're in add-tables mode (adding to existing database without dropping)
426+
let mut is_add_tables_mode = false;
427+
let mut tables_to_drop_in_add_mode: Vec<String> = Vec::new();
428+
425429
// Handle database creation atomically to avoid TOCTOU race condition
426430
// Scope the connection so it's dropped before dump/restore subprocess operations
427431
{
@@ -533,8 +537,11 @@ pub async fn init(
533537
" Adding tables to existing database '{}'",
534538
db_info.name
535539
);
536-
// Database exists, we're in add mode - proceed to dump/restore
537-
// The filter will ensure only specified tables are processed
540+
// Set the flag and store tables to drop before schema restore
541+
is_add_tables_mode = true;
542+
if let Some(tables) = tables_to_add.as_ref() {
543+
tables_to_drop_in_add_mode = tables.clone();
544+
}
538545
} else {
539546
bail!("Aborted: Database '{}' already exists", db_info.name);
540547
}
@@ -566,6 +573,25 @@ pub async fn init(
566573
)
567574
.await?;
568575

576+
// In add-tables mode, drop the specific tables first so restore_schema can recreate them
577+
if is_add_tables_mode && !tables_to_drop_in_add_mode.is_empty() {
578+
tracing::info!(
579+
" Dropping {} existing table(s) before restore...",
580+
tables_to_drop_in_add_mode.len()
581+
);
582+
let db_client = postgres::connect_with_retry(&target_db_url).await?;
583+
for table_name in &tables_to_drop_in_add_mode {
584+
// Table name format is "schema.table" or just "table" (assumes public)
585+
let drop_query = format!("DROP TABLE IF EXISTS {} CASCADE", table_name);
586+
if let Err(e) = db_client.execute(&drop_query, &[]).await {
587+
tracing::warn!(" Warning: Failed to drop table {}: {}", table_name, e);
588+
// Continue anyway - the table might not exist
589+
} else {
590+
tracing::info!(" Dropped table {}", table_name);
591+
}
592+
}
593+
}
594+
569595
tracing::info!(" Restoring schema for '{}'...", db_info.name);
570596
migration::restore_schema(&target_db_url, schema_file.to_str().unwrap()).await?;
571597

0 commit comments

Comments
 (0)