diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-10-05 00:15:45 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-10-05 00:27:29 -0400 |
| commit | e1551113323d5a496b826d7b0265b1be6235f45c (patch) | |
| tree | 08f09cac579c954c782e39d5cd02c7ae72f86374 /src/db.rs | |
| parent | b422be184e01b4cc35b9c9a6921379080c24edb3 (diff) | |
Make a backup of the `.hi` database before applying migrations.
This was motivated by Kit and I both independently discovering that sqlite3 will happily partially apply migrations, leaving the DB in a broken state.
Diffstat (limited to 'src/db.rs')
| -rw-r--r-- | src/db.rs | 105 |
1 files changed, 0 insertions, 105 deletions
diff --git a/src/db.rs b/src/db.rs deleted file mode 100644 index e09b0ba..0000000 --- a/src/db.rs +++ /dev/null @@ -1,105 +0,0 @@ -use std::str::FromStr; - -use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions}; - -pub async fn prepare(url: &str) -> Result<SqlitePool, Error> { - let pool = create(url).await?; - - // First migration of original migration series, from commit - // 9bd6d9862b1c243def02200bca2cfbf578ad2a2f or earlier. - reject_migration(&pool, "20240831024047", "login", "9949D238C4099295EC4BEE734BFDA8D87513B2973DFB895352A11AB01DD46CB95314B7F1B3431B77E3444A165FE3DC28").await?; - - sqlx::migrate!().run(&pool).await?; - Ok(pool) -} - -async fn create(database_url: &str) -> sqlx::Result<SqlitePool> { - let options = SqliteConnectOptions::from_str(database_url)? - .create_if_missing(true) - .optimize_on_close(true, /* analysis_limit */ None); - - let pool = SqlitePoolOptions::new().connect_with(options).await?; - Ok(pool) -} - -async fn reject_migration( - pool: &SqlitePool, - version: &str, - description: &str, - checksum_hex: &str, -) -> Result<(), Error> { - if !sqlx::query_scalar!( - r#" - select count(*) as "exists: bool" - from sqlite_master - where name = '_sqlx_migrations' - "# - ) - .fetch_one(pool) - .await? - { - // No migrations table; this is a fresh DB. - return Ok(()); - } - - if !sqlx::query_scalar!( - r#" - select count(*) as "exists: bool" - from _sqlx_migrations - where version = $1 - and description = $2 - and hex(checksum) = $3 - "#, - version, - description, - checksum_hex, - ) - .fetch_one(pool) - .await? - { - // Rejected migration does not exist; this DB never ran it. - return Ok(()); - } - - Err(Error::Rejected(version.into(), description.into())) -} - -/// Errors occurring during database setup. -#[derive(Debug, thiserror::Error)] -pub enum Error { - /// Failure due to a database error. See [`sqlx::Error`]. - #[error(transparent)] - Database(#[from] sqlx::Error), - /// Failure due to a database migration error. See - /// [`sqlx::migrate::MigrateError`]. - #[error(transparent)] - Migration(#[from] sqlx::migrate::MigrateError), - /// Failure because the database contains a migration from an unsupported - /// schema version. - #[error("database contains rejected migration {0}:{1}, move it aside")] - Rejected(String, String), -} - -pub trait NotFound { - type Ok; - fn not_found<E, F>(self, map: F) -> Result<Self::Ok, E> - where - E: From<sqlx::Error>, - F: FnOnce() -> E; -} - -impl<T> NotFound for Result<T, sqlx::Error> { - type Ok = T; - - fn not_found<E, F>(self, map: F) -> Result<T, E> - where - E: From<sqlx::Error>, - F: FnOnce() -> E, - { - match self { - Err(sqlx::Error::RowNotFound) => Err(map()), - Err(other) => Err(other.into()), - Ok(value) => Ok(value), - } - } -} |
