mod v1_12; mod v1_13; mod v1_14; mod v1_15; mod v1_16; use heed::RwTxn; use v1_12::{V1_12_3_To_V1_13_0, V1_12_To_V1_12_3}; use v1_13::{V1_13_0_To_V1_13_1, V1_13_1_To_Latest_V1_13}; use v1_14::Latest_V1_13_To_Latest_V1_14; use v1_15::Latest_V1_14_To_Latest_V1_15; use v1_16::Latest_V1_15_To_V1_16_0; use crate::constants::{VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH}; use crate::progress::{Progress, VariableNameStep}; use crate::{Index, InternalError, Result}; trait UpgradeIndex { /// Returns `true` if the index scheduler must regenerate its cached stats. fn upgrade( &self, wtxn: &mut RwTxn, index: &Index, original: (u32, u32, u32), progress: Progress, ) -> Result; fn target_version(&self) -> (u32, u32, u32); } const UPGRADE_FUNCTIONS: &[&dyn UpgradeIndex] = &[ &V1_12_To_V1_12_3 {}, &V1_12_3_To_V1_13_0 {}, &V1_13_0_To_V1_13_1 {}, &V1_13_1_To_Latest_V1_13 {}, &Latest_V1_13_To_Latest_V1_14 {}, &Latest_V1_14_To_Latest_V1_15 {}, &Latest_V1_15_To_V1_16_0 {}, &ToTargetNoOp { target: (1, 18, 0) }, &ToTargetNoOp { target: (1, 19, 0) }, &ToTargetNoOp { target: (1, 20, 0) }, &ToTargetNoOp { target: (1, 21, 0) }, &ToTargetNoOp { target: (1, 22, 0) }, // This is the last upgrade function, it will be called when the index is up to date. // any other upgrade function should be added before this one. &ToCurrentNoOp {}, ]; /// Causes a compile-time error if the argument is not in range of `0..UPGRADE_FUNCTIONS.len()` macro_rules! function_index { ($start:expr) => {{ const _CHECK_INDEX: () = { if $start >= $crate::update::upgrade::UPGRADE_FUNCTIONS.len() { panic!("upgrade functions out of range") } }; $start }}; } const fn start(from: (u32, u32, u32)) -> Option { let start = match from { (1, 12, 0..=2) => function_index!(0), (1, 12, 3..) => function_index!(1), (1, 13, 0) => function_index!(2), (1, 13, _) => function_index!(4), (1, 14, _) => function_index!(5), // We must handle the current version in the match because in case of a failure some index may have been upgraded but not other. (1, 15, _) => function_index!(6), (1, 16, _) | (1, 17, _) => function_index!(7), (1, 18, _) => function_index!(8), (1, 19, _) => function_index!(9), (1, 20, _) => function_index!(10), (1, 21, _) => function_index!(11), (1, 22, _) => function_index!(12), // We deliberately don't add a placeholder with (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) here to force manually // considering dumpless upgrade. (_major, _minor, _patch) => return None, }; Some(start) } /// Causes a compile-time error if the latest package cannot be upgraded. /// /// This serves as a reminder to consider the proper dumpless upgrade implementation when changing the package version. const _CHECK_PACKAGE_CAN_UPGRADE: () = { if start((VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)).is_none() { panic!("cannot upgrade from latest package version") } }; /// Return true if the cached stats of the index must be regenerated pub fn upgrade( wtxn: &mut RwTxn, index: &Index, db_version: (u32, u32, u32), must_stop_processing: MSP, progress: Progress, ) -> Result where MSP: Fn() -> bool + Sync, { let from = index.get_version(wtxn)?.unwrap_or(db_version); let start = start(from).ok_or_else(|| InternalError::CannotUpgradeToVersion(from.0, from.1, from.2))?; enum UpgradeVersion {} let upgrade_path = &UPGRADE_FUNCTIONS[start..]; let mut current_version = from; let mut regenerate_stats = false; for (i, upgrade) in upgrade_path.iter().enumerate() { if (must_stop_processing)() { return Err(crate::Error::InternalError(InternalError::AbortedIndexation)); } let target = upgrade.target_version(); progress.update_progress(VariableNameStep::::new( format!( "Upgrading from v{}.{}.{} to v{}.{}.{}", current_version.0, current_version.1, current_version.2, target.0, target.1, target.2 ), i as u32, upgrade_path.len() as u32, )); regenerate_stats |= upgrade.upgrade(wtxn, index, from, progress.clone())?; index.put_version(wtxn, target)?; current_version = target; } Ok(regenerate_stats) } #[allow(non_camel_case_types)] struct ToCurrentNoOp {} impl UpgradeIndex for ToCurrentNoOp { fn upgrade( &self, _wtxn: &mut RwTxn, _index: &Index, _original: (u32, u32, u32), _progress: Progress, ) -> Result { Ok(false) } fn target_version(&self) -> (u32, u32, u32) { (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) } } /// Perform no operation during the upgrade except changing to the specified target version. #[allow(non_camel_case_types)] struct ToTargetNoOp { pub target: (u32, u32, u32), } impl UpgradeIndex for ToTargetNoOp { fn upgrade( &self, _wtxn: &mut RwTxn, _index: &Index, _original: (u32, u32, u32), _progress: Progress, ) -> Result { Ok(false) } fn target_version(&self) -> (u32, u32, u32) { self.target } }