convert_db: prevent conversion between same input/output engine
All checks were successful
continuous-integration/drone/pr Build is passing

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.
This commit is contained in:
Zdenek Crha 2024-01-18 17:57:53 +01:00
parent 8527dd87cc
commit 4b54e053df
2 changed files with 60 additions and 12 deletions

View file

@ -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]

View file

@ -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(),
)),
}
}