mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
7dc7096ae1
## Summary - Revert #28655, restoring the thread `recencyAt` behavior introduced by #27910. - Move `threads_recency_at` to migration 0039 so it no longer collides with `external_agent_config_imports` at version 0038. - Repair databases that already applied the recency migration as version 38 by moving the matching migration-history row to version 39 before SQLx validation. The current version-38 migration can then apply normally. ## Validation - `just test -p codex-state migrations::tests::repairs_recency_migration_that_was_applied_as_version_38` - `just test -p codex-state -p codex-rollout -p codex-thread-store -p codex-app-server-protocol -p codex-tui`: 3,439 passed; six TUI tests could not open the machine's existing read-only incident database at `~/.codex/sqlite/state_5.sqlite`. - `just fix -p codex-state` - `just fmt` - Verified that state migration versions are unique.
89 lines
2.5 KiB
Rust
89 lines
2.5 KiB
Rust
use std::borrow::Cow;
|
|
|
|
use sqlx::SqlitePool;
|
|
use sqlx::migrate::Migrator;
|
|
|
|
pub(crate) static STATE_MIGRATOR: Migrator = sqlx::migrate!("./migrations");
|
|
pub(crate) static LOGS_MIGRATOR: Migrator = sqlx::migrate!("./logs_migrations");
|
|
pub(crate) static GOALS_MIGRATOR: Migrator = sqlx::migrate!("./goals_migrations");
|
|
pub(crate) static MEMORIES_MIGRATOR: Migrator = sqlx::migrate!("./memory_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) 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(());
|
|
}
|
|
|
|
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;
|