From dbcc871ce3f6cadf965d05c8c6ad9eab5f62c15d Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Fri, 31 Dec 2021 15:05:21 +0200 Subject: feat: compare schema versions for compatibility When opening a local generation, check that it's compatible with the current version of Obnam. Sponsored-by: author --- src/generation.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 5560eaf..454bb50 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -168,6 +168,11 @@ pub enum LocalGenerationError { #[error("Generation 'meta' row {0} has badly formed integer: {1}")] BadMetaInteger(String, std::num::ParseIntError), + /// Local generation uses a schema version that this version of + /// Obnam isn't compatible with. + #[error("Backup is not compatible with this version of Obnam: {0}.{1}")] + Incompatible(u32, u32), + /// Error from SQL. #[error(transparent)] RusqliteError(#[from] rusqlite::Error), @@ -216,13 +221,26 @@ impl BackedUpFile { } impl LocalGeneration { + fn new(conn: Connection) -> Self { + Self { conn } + } + /// Open a local file as a local generation. pub fn open

(filename: P) -> Result where P: AsRef, { let conn = sql::open_db(filename.as_ref())?; - Ok(Self { conn }) + let gen = Self::new(conn); + let schema = gen.meta()?.schema_version(); + let our_schema = SchemaVersion::new(SCHEMA_MAJOR, SCHEMA_MINOR); + if !our_schema.is_compatible_with(&schema) { + return Err(LocalGenerationError::Incompatible( + schema.major, + schema.minor, + )); + } + Ok(gen) } /// Return generation metadata for local generation. @@ -329,6 +347,50 @@ impl SchemaVersion { fn new(major: u32, minor: u32) -> Self { Self { major, minor } } + + /// Is this schema version compatible with another schema version? + pub fn is_compatible_with(&self, other: &Self) -> bool { + self.major == other.major && self.minor >= other.minor + } +} + +#[cfg(test)] +mod test_schema { + use super::*; + + #[test] + fn compatible_with_self() { + let v = SchemaVersion::new(1, 2); + assert!(v.is_compatible_with(&v)); + } + + #[test] + fn compatible_with_older_minor_version() { + let old = SchemaVersion::new(1, 2); + let new = SchemaVersion::new(1, 3); + assert!(new.is_compatible_with(&old)); + } + + #[test] + fn not_compatible_with_newer_minor_version() { + let old = SchemaVersion::new(1, 2); + let new = SchemaVersion::new(1, 3); + assert!(!old.is_compatible_with(&new)); + } + + #[test] + fn not_compatible_with_older_major_version() { + let old = SchemaVersion::new(1, 2); + let new = SchemaVersion::new(2, 0); + assert!(!new.is_compatible_with(&old)); + } + + #[test] + fn not_compatible_with_newer_major_version() { + let old = SchemaVersion::new(1, 2); + let new = SchemaVersion::new(2, 0); + assert!(!old.is_compatible_with(&new)); + } } mod sql { -- cgit v1.2.1