Files
codex/codex-rs/state/src/migrations.rs
T
Ahmed Ibrahim 3627bb91b2 Avoid building a host SQLite driver for SQLx macros (#47084)
## Why

Row derives and migration embedding do not need query macros or a host SQLite driver.

## What changed

- Remove the `macros` feature from `sqlx` and depend directly on `sqlx-macros` with only `derive` and `migrate` enabled.
- Update state and thread-store derives and migration embedding to use `sqlx_macros`.
- Enable `sqlx-sqlite`'s `offline` feature directly to preserve query-description support in the target driver.
- Pin `sqlx`, `sqlx-macros`, and `sqlx-sqlite` to `=0.9.0` to keep their versions aligned.

GitOrigin-RevId: 8a8ef691ff2862a7dad978a578c5206b52dfc0b6
2026-09-21 18:30:31 +00:00

123 lines
3.5 KiB
Rust

use std::borrow::Cow;
use sqlx::migrate::Migrator;
use sqlx_sqlite::SqlitePool;
pub(crate) static STATE_MIGRATOR: Migrator = sqlx_macros::migrate!("./migrations");
pub(crate) static LOGS_MIGRATOR: Migrator = sqlx_macros::migrate!("./logs_migrations");
pub(crate) static GOALS_MIGRATOR: Migrator = sqlx_macros::migrate!("./goals_migrations");
pub(crate) static MEMORIES_MIGRATOR: Migrator = sqlx_macros::migrate!("./memory_migrations");
pub(crate) static QUEUE_MIGRATOR: Migrator = sqlx_macros::migrate!("./queue_migrations");
pub(crate) static THREAD_HISTORY_MIGRATOR: Migrator =
sqlx_macros::migrate!("./thread_history_migrations");
/// Allow an older Codex binary to open a database that has already been
/// migrated by a newer binary running in parallel.
///
/// We intentionally ignore applied migration versions that are newer than the
/// embedded migration set. Known migration versions are still validated by
/// checksum, so this only relaxes the "database is ahead of me" case.
fn runtime_migrator(base: &'static Migrator) -> Migrator {
Migrator {
migrations: Cow::Borrowed(base.migrations.as_ref()),
ignore_missing: true,
locking: base.locking,
no_tx: base.no_tx,
table_name: base.table_name.clone(),
create_schemas: base.create_schemas.clone(),
}
}
pub(crate) fn runtime_state_migrator() -> Migrator {
runtime_migrator(&STATE_MIGRATOR)
}
pub(crate) fn runtime_logs_migrator() -> Migrator {
runtime_migrator(&LOGS_MIGRATOR)
}
pub(crate) fn runtime_goals_migrator() -> Migrator {
runtime_migrator(&GOALS_MIGRATOR)
}
pub(crate) fn runtime_memories_migrator() -> Migrator {
runtime_migrator(&MEMORIES_MIGRATOR)
}
pub(crate) fn runtime_queue_migrator() -> Migrator {
runtime_migrator(&QUEUE_MIGRATOR)
}
// The paginated history projector will call this when it takes ownership of opening the database.
#[allow(dead_code)]
pub(crate) fn runtime_thread_history_migrator() -> Migrator {
runtime_migrator(&THREAD_HISTORY_MIGRATOR)
}
pub(crate) async fn repair_legacy_recency_migration_version(
pool: &SqlitePool,
migrator: &Migrator,
) -> anyhow::Result<()> {
let Some(recency_migration) = migrator
.migrations
.iter()
.find(|migration| migration.version == 39)
else {
return Ok(());
};
let migrations_table_exists = sqlx::query_scalar::<_, i64>(
"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_sqlx_migrations'",
)
.fetch_optional(pool)
.await?
.is_some();
if !migrations_table_exists {
return Ok(());
}
let legacy_recency_needs_repair = sqlx::query_scalar::<_, i64>(
r#"
SELECT 1
FROM _sqlx_migrations
WHERE version = ?
AND checksum = ?
AND NOT EXISTS (
SELECT 1 FROM _sqlx_migrations WHERE version = ?
)
"#,
)
.bind(38_i64)
.bind(recency_migration.checksum.as_ref())
.bind(recency_migration.version)
.fetch_optional(pool)
.await?
.is_some();
if !legacy_recency_needs_repair {
return Ok(());
}
sqlx::query(
r#"
UPDATE _sqlx_migrations
SET version = ?, description = ?
WHERE version = ?
AND checksum = ?
AND NOT EXISTS (
SELECT 1 FROM _sqlx_migrations WHERE version = ?
)
"#,
)
.bind(recency_migration.version)
.bind(recency_migration.description.as_ref())
.bind(38_i64)
.bind(recency_migration.checksum.as_ref())
.bind(recency_migration.version)
.execute(pool)
.await?;
Ok(())
}
#[cfg(test)]
#[path = "migrations_tests.rs"]
mod tests;