about summary refs log tree commit diff stats
path: root/yt/src/storage/migrate
diff options
context:
space:
mode:
Diffstat (limited to 'yt/src/storage/migrate')
-rw-r--r--yt/src/storage/migrate/mod.rs123
1 files changed, 92 insertions, 31 deletions
diff --git a/yt/src/storage/migrate/mod.rs b/yt/src/storage/migrate/mod.rs
index ee43008..52f8c2b 100644
--- a/yt/src/storage/migrate/mod.rs
+++ b/yt/src/storage/migrate/mod.rs
@@ -25,6 +25,15 @@ pub enum DbVersion {
 }
 const CURRENT_VERSION: DbVersion = DbVersion::One;
 
+async fn add_error_context(
+    function: impl Future<Output = Result<()>>,
+    level: DbVersion,
+) -> Result<()> {
+    function
+        .await
+        .with_context(|| format!("Format failed to migrate database to version: {level}"))
+}
+
 async fn set_db_version(
     tx: &mut Transaction<'_, Sqlite>,
     old_version: Option<DbVersion>,
@@ -83,40 +92,85 @@ impl DbVersion {
     ///
     /// Each update is atomic, so if this function fails you are still guaranteed to have a
     /// database at version `get_version`.
+    #[allow(clippy::too_many_lines)]
     async fn update(self, app: &App) -> Result<()> {
         match self {
             DbVersion::Empty => {
-                let mut tx = app.database.begin().await?;
-                debug!("Migrate: Empty -> Zero");
-
-                sqlx::raw_sql(include_str!("./sql/00_empty_to_zero.sql"))
-                    .execute(&mut *tx)
-                    .await?;
-
-                set_db_version(&mut tx, None, DbVersion::Zero).await?;
-
-                tx.commit().await?;
+                add_error_context(
+                    async {
+                        let mut tx = app
+                            .database
+                            .begin()
+                            .await
+                            .context("Failed to start transaction")?;
+                        debug!("Migrate: Empty -> Zero");
+
+                        sqlx::raw_sql(include_str!("./sql/00_empty_to_zero.sql"))
+                            .execute(&mut *tx)
+                            .await
+                            .context("Failed to execute sql update script")?;
+
+                        set_db_version(&mut tx, None, DbVersion::Zero)
+                            .await
+                            .context("Failed to set new version")?;
+
+                        tx.commit().await.context("Failed to commit changes")?;
+
+                        // NOTE: This is needed, so that sqlite "sees" our changes to the table
+                        // without having to reconnect. <2025-02-18>
+                        query!("VACUUM")
+                            .execute(&app.database)
+                            .await
+                            .context("Failed to vacuum database")?;
+
+                        Ok(())
+                    },
+                    DbVersion::One,
+                )
+                .await?;
                 Box::pin(Self::Zero.update(app)).await
             }
 
             DbVersion::Zero => {
-                let mut tx = app.database.begin().await?;
-                debug!("Migrate: Zero -> One");
-
-                sqlx::raw_sql(include_str!("./sql/01_zero_to_one.sql"))
-                    .execute(&mut *tx)
-                    .await?;
+                add_error_context(
+                    async {
+                        let mut tx = app
+                            .database
+                            .begin()
+                            .await
+                            .context("Failed to start transaction")?;
+                        debug!("Migrate: Zero -> One");
+
+                        sqlx::raw_sql(include_str!("./sql/01_zero_to_one.sql"))
+                            .execute(&mut *tx)
+                            .await
+                            .context("Failed to execute the update sql script")?;
+
+                        set_db_version(&mut tx, Some(DbVersion::Zero), DbVersion::One)
+                            .await
+                            .context("Failed to set the new version")?;
+
+                        tx.commit()
+                            .await
+                            .context("Failed to commit the update transaction")?;
+
+                        // NOTE: This is needed, so that sqlite "sees" our changes to the table
+                        // without having to reconnect. <2025-02-18>
+                        query!("VACUUM")
+                            .execute(&app.database)
+                            .await
+                            .context("Failed to vacuum database")?;
+
+                        Ok(())
+                    },
+                    DbVersion::Zero,
+                )
+                .await?;
 
-                set_db_version(&mut tx, Some(DbVersion::Zero), DbVersion::One).await?;
-
-                tx.commit().await?;
                 Box::pin(Self::One.update(app)).await
             }
-
             // This is the current_version
             DbVersion::One => {
-                debug!("Migrate: One -> Two");
-
                 assert_eq!(self, CURRENT_VERSION);
                 assert_eq!(self, get_version(app).await?);
                 Ok(())
@@ -141,13 +195,8 @@ fn get_current_date() -> i64 {
     )
     .expect("Time does not go backwards");
 
-    debug!(
-        "Adding a date with timestamp: {}",
-        seconds_since_epoch.num_seconds()
-    );
-
     // All database dates should be after the UNIX_EPOCH (and thus positiv)
-    seconds_since_epoch.num_seconds()
+    seconds_since_epoch.num_milliseconds()
 }
 
 /// Return the current database version.
@@ -155,11 +204,21 @@ fn get_current_date() -> i64 {
 /// # Panics
 /// Only if internal assertions fail.
 pub async fn get_version(app: &App) -> Result<DbVersion> {
+    get_version_db(&app.database).await
+}
+/// Return the current database version.
+///
+/// In contrast to the [`get_version`] function, this function does not
+/// a fully instantiated [`App`], a database connection suffices.
+///
+/// # Panics
+/// Only if internal assertions fail.
+pub async fn get_version_db(pool: &SqlitePool) -> Result<DbVersion> {
     let version_table_exists = {
         let query = query!(
             "SELECT 1 as result FROM sqlite_master WHERE type = 'table' AND name = 'version'"
         )
-        .fetch_optional(&app.database)
+        .fetch_optional(pool)
         .await?;
         if let Some(output) = query {
             assert_eq!(output.result, 1);
@@ -177,7 +236,7 @@ pub async fn get_version(app: &App) -> Result<DbVersion> {
         SELECT namespace, number FROM version WHERE valid_to IS NULL;
         "
     )
-    .fetch_one(&app.database)
+    .fetch_one(pool)
     .await
     .context("Failed to fetch version number")?;
 
@@ -185,7 +244,9 @@ pub async fn get_version(app: &App) -> Result<DbVersion> {
 }
 
 pub async fn migrate_db(app: &App) -> Result<()> {
-    let current_version = get_version(app).await?;
+    let current_version = get_version(app)
+        .await
+        .context("Failed to determine initial version")?;
 
     if current_version == CURRENT_VERSION {
         return Ok(());