summaryrefslogtreecommitdiff
path: root/src/db.rs
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-10-05 00:15:45 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-10-05 00:27:29 -0400
commite1551113323d5a496b826d7b0265b1be6235f45c (patch)
tree08f09cac579c954c782e39d5cd02c7ae72f86374 /src/db.rs
parentb422be184e01b4cc35b9c9a6921379080c24edb3 (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.rs105
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),
- }
- }
-}