changes
All checks were successful
CI / ci (push) Successful in 19m38s

This commit is contained in:
2026-08-26 20:55:30 +02:00
parent a557c183e9
commit 23d052278a
523 changed files with 24448 additions and 2005 deletions

View File

@@ -1,16 +1,68 @@
use std::time::Duration;
use sqlx::SqlitePool;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
const MIGRATIONS: &[&str] = &[
include_str!("migrations/001_initial.sql"),
include_str!("migrations/002_push_subscriptions.sql"),
const BUSY_TIMEOUT: Duration = Duration::from_secs(10);
const MIGRATIONS: &[(&str, &str)] = &[
("001_initial", include_str!("migrations/001_initial.sql")),
(
"002_push_subscriptions",
include_str!("migrations/002_push_subscriptions.sql"),
),
(
"003_entry_content",
include_str!("migrations/003_entry_content.sql"),
),
(
"004_drop_entry_content_column",
include_str!("migrations/004_drop_entry_content_column.sql"),
),
(
"005_location_and_song",
include_str!("migrations/005_location_and_song.sql"),
),
(
"006_provider_connections",
include_str!("migrations/006_provider_connections.sql"),
),
(
"007_daily_metrics",
include_str!("migrations/007_daily_metrics.sql"),
),
(
"008_api_tokens",
include_str!("migrations/008_api_tokens.sql"),
),
(
"009_metric_rejections",
include_str!("migrations/009_metric_rejections.sql"),
),
(
"010_cycle_and_preferences",
include_str!("migrations/010_cycle_and_preferences.sql"),
),
("011_jobs", include_str!("migrations/011_jobs.sql")),
(
"012_entry_weather",
include_str!("migrations/012_entry_weather.sql"),
),
];
const TAKE_THE_WRITE_LOCK_UP_FRONT: &str = "BEGIN IMMEDIATE";
const SCHEMA_MIGRATIONS_TABLE: &str = "CREATE TABLE IF NOT EXISTS schema_migrations (
name TEXT PRIMARY KEY NOT NULL,
applied_at TEXT NOT NULL
)";
pub async fn create_pool(database_url: &str) -> Result<SqlitePool, sqlx::Error> {
let options: SqliteConnectOptions = database_url
.parse::<SqliteConnectOptions>()?
.create_if_missing(true)
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.busy_timeout(BUSY_TIMEOUT)
.foreign_keys(true);
let pool = SqlitePoolOptions::new()
@@ -22,9 +74,59 @@ pub async fn create_pool(database_url: &str) -> Result<SqlitePool, sqlx::Error>
}
pub async fn run_migrations(pool: &SqlitePool) -> Result<(), sqlx::Error> {
for migration in MIGRATIONS {
sqlx::raw_sql(*migration).execute(pool).await?;
let mut connection = pool.acquire().await?;
sqlx::raw_sql(SCHEMA_MIGRATIONS_TABLE)
.execute(&mut *connection)
.await?;
sqlx::raw_sql(TAKE_THE_WRITE_LOCK_UP_FRONT)
.execute(&mut *connection)
.await?;
match apply_pending(&mut connection).await {
Ok(()) => {
sqlx::raw_sql("COMMIT").execute(&mut *connection).await?;
Ok(())
}
Err(error) => {
let _ = sqlx::raw_sql("ROLLBACK").execute(&mut *connection).await;
Err(error)
}
}
tracing::info!("database migrations completed");
}
async fn apply_pending(connection: &mut sqlx::SqliteConnection) -> Result<(), sqlx::Error> {
for (name, sql) in MIGRATIONS {
if is_applied(connection, name).await? {
continue;
}
sqlx::raw_sql(*sql).execute(&mut *connection).await?;
sqlx::query("INSERT INTO schema_migrations (name, applied_at) VALUES (?, ?)")
.bind(*name)
.bind(chrono::Utc::now().to_rfc3339())
.execute(&mut *connection)
.await?;
tracing::info!(migration = *name, "applied migration");
}
Ok(())
}
async fn is_applied(
connection: &mut sqlx::SqliteConnection,
name: &str,
) -> Result<bool, sqlx::Error> {
let existing: Option<(String,)> =
sqlx::query_as("SELECT name FROM schema_migrations WHERE name = ?")
.bind(name)
.fetch_optional(&mut *connection)
.await?;
Ok(existing.is_some())
}