aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZdenek Crha <zdenek.crha@gmail.com>2024-01-18 17:57:53 +0100
committerZdenek Crha <zdenek.crha@gmail.com>2024-01-18 17:57:56 +0100
commit4b54e053dfd1bd950e86ffad9b4155a47806d8f1 (patch)
treea13ece1f8aac75560503a672afe6b0ca417f46d3
parent8527dd87cc7a685b3ffd2054fca1a88574b93ea4 (diff)
downloadgarage-4b54e053dfd1bd950e86ffad9b4155a47806d8f1.tar.gz
garage-4b54e053dfd1bd950e86ffad9b4155a47806d8f1.zip
convert_db: prevent conversion between same input/output engine
Use optional DB open overrides for both input and output database. Duplicating the same override flag for input/output would result in too many, too long flags. It would be too costly for very rare edge-case where converting between same DB engine, just with different flags. Because overrides flags for different engines are disjoint and we are preventing conversion between same input/ouput DB engine, we can have only one set. The override flag will be passed either to input or output, based on engine type it belongs to. It will never be passed to both of them and cause unwelcome surprise to user.
-rw-r--r--src/db/lib.rs47
-rw-r--r--src/garage/cli/convert_db.rs25
2 files changed, 60 insertions, 12 deletions
diff --git a/src/db/lib.rs b/src/db/lib.rs
index fe44b01e..427140f9 100644
--- a/src/db/lib.rs
+++ b/src/db/lib.rs
@@ -171,6 +171,53 @@ impl Db {
}
}
+/// List of supported database engine types
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum Engine {
+ #[cfg(feature = "lmdb")]
+ Lmdb,
+
+ #[cfg(feature = "sqlite")]
+ Sqlite,
+
+ #[cfg(feature = "sled")]
+ Sled,
+}
+
+impl std::fmt::Display for Engine {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ #[cfg(feature = "lmdb")]
+ Self::Lmdb => fmt.write_str("lmdb"),
+
+ #[cfg(feature = "sqlite")]
+ Self::Sqlite => fmt.write_str("sqlite"),
+
+ #[cfg(feature = "sled")]
+ Self::Sled => fmt.write_str("sled"),
+ }
+ }
+}
+
+impl std::str::FromStr for Engine {
+ type Err = Error;
+
+ fn from_str(text: &str) -> Result<Engine> {
+ match text {
+ #[cfg(feature = "lmdb")]
+ "lmdb" | "heed" => Ok(Self::Lmdb),
+
+ #[cfg(feature = "sqlite")]
+ "sqlite" | "sqlite3" | "rusqlite" => Ok(Self::Sqlite),
+
+ #[cfg(feature = "sled")]
+ "sled" => Ok(Self::Sled),
+
+ kind => Err(Error(format!("Invalid DB engine: {}", kind).into())),
+ }
+ }
+}
+
#[allow(clippy::len_without_is_empty)]
impl Tree {
#[inline]
diff --git a/src/garage/cli/convert_db.rs b/src/garage/cli/convert_db.rs
index 3b396c1b..d33010f4 100644
--- a/src/garage/cli/convert_db.rs
+++ b/src/garage/cli/convert_db.rs
@@ -14,14 +14,14 @@ pub struct ConvertDbOpt {
/// Input database engine (sled, lmdb or sqlite; limited by db engines
/// enabled in this build)
#[structopt(short = "a")]
- input_engine: String,
+ input_engine: Engine,
/// Output database path
#[structopt(short = "o")]
output_path: PathBuf,
/// Output database engine
#[structopt(short = "b")]
- output_engine: String,
+ output_engine: Engine,
#[structopt(flatten)]
output_open: OpenDbOpt,
@@ -47,28 +47,32 @@ pub struct OpenLmdbOpt {
}
pub(crate) fn do_conversion(args: ConvertDbOpt) -> Result<()> {
- let input = open_db(args.input_path, args.input_engine, OpenDbOpt::default())?;
- let output = open_db(args.output_path, args.output_engine, args.output_open)?;
+ if args.input_engine == args.output_engine {
+ return Err(Error("input and output database engine must differ".into()));
+ }
+
+ let input = open_db(args.input_path, args.input_engine, &args.output_open)?;
+ let output = open_db(args.output_path, args.output_engine, &args.output_open)?;
output.import(&input)?;
Ok(())
}
-fn open_db(path: PathBuf, engine: String, open: OpenDbOpt) -> Result<Db> {
- match engine.as_str() {
+fn open_db(path: PathBuf, engine: Engine, open: &OpenDbOpt) -> Result<Db> {
+ match engine {
#[cfg(feature = "sled")]
- "sled" => {
+ Engine::Sled => {
let db = sled_adapter::sled::Config::default().path(&path).open()?;
Ok(sled_adapter::SledDb::init(db))
}
#[cfg(feature = "sqlite")]
- "sqlite" | "sqlite3" | "rusqlite" => {
+ Engine::Sqlite => {
let db = sqlite_adapter::rusqlite::Connection::open(&path)?;
db.pragma_update(None, "journal_mode", &"WAL")?;
db.pragma_update(None, "synchronous", &"NORMAL")?;
Ok(sqlite_adapter::SqliteDb::init(db))
}
#[cfg(feature = "lmdb")]
- "lmdb" | "heed" => {
+ Engine::Lmdb => {
std::fs::create_dir_all(&path).map_err(|e| {
Error(format!("Unable to create LMDB data directory: {}", e).into())
})?;
@@ -87,8 +91,5 @@ fn open_db(path: PathBuf, engine: String, open: OpenDbOpt) -> Result<Db> {
let db = env_builder.open(&path)?;
Ok(lmdb_adapter::LmdbDb::init(db))
}
- e => Err(Error(
- format!("Invalid or unsupported DB engine: {}", e).into(),
- )),
}
}