Skip to content

Commit 5451653

Browse files
committed
Properly fix overwriting dump file
1. Fix to always overwrite targets in /dev/* 2. Add a warning for the overwrite behavior change 3. Add a test for piping through stdin/out
1 parent d84331b commit 5451653

3 files changed

Lines changed: 115 additions & 33 deletions

File tree

src/commands/dump.rs

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,39 +25,60 @@ pub struct Guard {
2525
}
2626

2727
impl Guard {
28-
async fn open(filename: &Path, overwrite_existing: bool) -> anyhow::Result<(Output, Guard)> {
28+
async fn open(
29+
filename: &Path,
30+
overwrite_existing: Option<bool>,
31+
) -> anyhow::Result<(Output, Guard)> {
2932
if filename.to_str() == Some("-") {
3033
Ok((Box::new(io::stdout()), Guard { filenames: None }))
31-
} else if cfg!(windows) || filename.starts_with("/dev/") || filename.file_name().is_none() {
32-
let file = OpenOptions::new()
33-
.write(true)
34-
.create(overwrite_existing)
35-
.create_new(!overwrite_existing)
36-
.truncate(overwrite_existing)
37-
.open(&filename)
34+
} else if filename.starts_with("/dev/") {
35+
let file = fs::File::create(&filename)
3836
.await
3937
.context(filename.display().to_string())?;
4038
Ok((Box::new(file), Guard { filenames: None }))
4139
} else {
42-
if !overwrite_existing && fs::metadata(&filename).await.is_ok() {
43-
anyhow::bail!(
44-
"failed: target file already exists. Specify --overwrite-existing to replace."
45-
)
46-
}
47-
// Create .~.tmp file path, first remove if already existing
48-
let tmp_path = filename.with_file_name(tmp_file_name(filename));
49-
if fs::metadata(&tmp_path).await.is_ok() {
50-
fs::remove_file(&tmp_path).await.ok();
40+
let overwrite_existing = overwrite_existing.unwrap_or_else(|| {
41+
log::warn!(
42+
"In the next EdgeDB CLI release, the dump behavior will \
43+
change to not overwrite the target file by default. For \
44+
compatibility, please specify --overwrite-existing to \
45+
preserve the current behavior, or --overwrite-existing=false \
46+
to adopt the new behavior."
47+
);
48+
true
49+
});
50+
if cfg!(windows) || filename.file_name().is_none() {
51+
let file = OpenOptions::new()
52+
.write(true)
53+
.create(overwrite_existing)
54+
.create_new(!overwrite_existing)
55+
.truncate(overwrite_existing)
56+
.open(&filename)
57+
.await
58+
.context(filename.display().to_string())?;
59+
Ok((Box::new(file), Guard { filenames: None }))
60+
} else {
61+
if !overwrite_existing && fs::metadata(&filename).await.is_ok() {
62+
anyhow::bail!(
63+
"failed: target file already exists. \
64+
Specify --overwrite-existing to replace."
65+
)
66+
}
67+
// Create .~.tmp file path, first remove if already existing
68+
let tmp_path = filename.with_file_name(tmp_file_name(filename));
69+
if fs::metadata(&tmp_path).await.is_ok() {
70+
fs::remove_file(&tmp_path).await.ok();
71+
}
72+
let tmp_file = fs::File::create(&tmp_path)
73+
.await
74+
.context(tmp_path.display().to_string())?;
75+
Ok((
76+
Box::new(tmp_file),
77+
Guard {
78+
filenames: Some((tmp_path, filename.to_owned(), overwrite_existing)),
79+
},
80+
))
5181
}
52-
let tmp_file = fs::File::create(&tmp_path)
53-
.await
54-
.context(tmp_path.display().to_string())?;
55-
Ok((
56-
Box::new(tmp_file),
57-
Guard {
58-
filenames: Some((tmp_path, filename.to_owned(), overwrite_existing)),
59-
},
60-
))
6182
}
6283
}
6384

@@ -114,7 +135,7 @@ async fn dump_db(
114135
_options: &Options,
115136
filename: &Path,
116137
mut include_secrets: bool,
117-
overwrite_existing: bool,
138+
overwrite_existing: Option<bool>,
118139
) -> Result<(), anyhow::Error> {
119140
if cli.get_version().await?.specific() < "4.0-alpha.2".parse().unwrap() {
120141
include_secrets = false;
@@ -189,7 +210,7 @@ pub async fn dump_all(
189210

190211
fs::create_dir_all(dir).await?;
191212

192-
let (mut init, guard) = Guard::open(&dir.join("init.edgeql"), true).await?;
213+
let (mut init, guard) = Guard::open(&dir.join("init.edgeql"), Some(true)).await?;
193214
if !config.trim().is_empty() {
194215
init.write_all(b"# DESCRIBE SYSTEM CONFIG\n").await?;
195216
init.write_all(config.as_bytes()).await?;
@@ -207,7 +228,14 @@ pub async fn dump_all(
207228
match conn_params.branch(database)?.connect().await {
208229
Ok(mut db_conn) => {
209230
let filename = dir.join(&(urlencoding::encode(database) + ".dump")[..]);
210-
dump_db(&mut db_conn, options, &filename, include_secrets, true).await?;
231+
dump_db(
232+
&mut db_conn,
233+
options,
234+
&filename,
235+
include_secrets,
236+
Some(true),
237+
)
238+
.await?;
211239
}
212240
Err(err) => {
213241
if let Some(e) = err.downcast_ref::<edgedb_errors::Error>() {

src/commands/parser.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::path::PathBuf;
22

3-
use clap::ValueHint;
3+
use clap::{ArgAction, ValueHint};
44

55
use crate::migrations::options::{Migrate, Migration};
66
use crate::options::ConnectionOptions;
@@ -411,9 +411,9 @@ pub struct Dump {
411411
pub format: Option<DumpFormat>,
412412

413413
/// Used to automatically overwrite existing files of the same name. Defaults
414-
/// to `true`.
415-
#[arg(long, default_value = "true")]
416-
pub overwrite_existing: bool,
414+
/// to `true` for now but will default to `false` in the next release.
415+
#[arg(long, default_missing_value = "true", num_args = 0..=1, action = ArgAction::Set)]
416+
pub overwrite_existing: Option<bool>,
417417
}
418418

419419
#[derive(clap::Args, Clone, Debug)]

tests/func/dump_restore.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,57 @@ fn dump_restore_all() {
115115
new_instance.0.stop();
116116
println!("query");
117117
}
118+
119+
#[!cfg(target_os = "windows")]
120+
#[test]
121+
fn dump_restore_dev() {
122+
println!("before");
123+
SERVER
124+
.admin_cmd()
125+
.arg("database")
126+
.arg("create")
127+
.arg("dump_03")
128+
.assert()
129+
.success();
130+
println!("dbcreated");
131+
SERVER
132+
.database_cmd("dump_03")
133+
.arg("query")
134+
.arg("CREATE TYPE Hello { CREATE REQUIRED PROPERTY name -> str; }")
135+
.arg("INSERT Hello { name := 'world' }")
136+
.assert()
137+
.success();
138+
println!("Created");
139+
let dumped_data = SERVER
140+
.admin_cmd()
141+
.arg("dump")
142+
.arg("/dev/stdout")
143+
.assert()
144+
.success()
145+
.get_output();
146+
println!("dumped");
147+
SERVER
148+
.admin_cmd()
149+
.arg("database")
150+
.arg("create")
151+
.arg("restore_03")
152+
.assert()
153+
.success();
154+
println!("created2");
155+
SERVER
156+
.database_cmd("restore_03")
157+
.arg("restore")
158+
.arg("/dev/stdin")
159+
.write_stdin(&dumped_data.stdout)
160+
.assert()
161+
.success();
162+
println!("restored");
163+
SERVER
164+
.database_cmd("restore_03")
165+
.arg("query")
166+
.arg("SELECT Hello.name")
167+
.assert()
168+
.success()
169+
.stdout("\"world\"\n");
170+
println!("query");
171+
}

0 commit comments

Comments
 (0)