pub(crate) fn has_column(
conn: &rusqlite::Connection,
table: &str,
column: &str,
) -> rusqlite::Result<bool> {
let cols: Vec<String> = conn
.prepare(&format!("PRAGMA table_info({table})"))?
.query_map([], |r| r.get::<_, String>(1))?
.filter_map(Result::ok)
.collect();
Ok(cols.iter().any(|c| c == column))
}
fn has_table(conn: &rusqlite::Connection, table: &str) -> rusqlite::Result<bool> {
conn.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = ?1",
[table],
|r| r.get::<_, i64>(0),
)
.map(|n| n > 0)
}
pub(crate) fn heal_notify_queue(conn: &rusqlite::Connection) -> rusqlite::Result<bool> {
if has_table(conn, "notify_queue")? {
return Ok(false);
}
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS notify_queue (
id TEXT PRIMARY KEY NOT NULL,
session_id TEXT NOT NULL,
context_text TEXT NOT NULL,
display_text TEXT NOT NULL,
origin TEXT NOT NULL,
bg_meta TEXT,
created_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_notify_queue_session ON notify_queue(session_id);",
)?;
tracing::warn!(
"Healed notify_queue: the #111 table was missing although the schema was stamped past \
its migration index (#1401). Parked pushes could not survive a restart until now."
);
Ok(true)
}
const THREAD_ID_MIGRATION_INDEX: i64 = 41;
pub(crate) fn skip_applied_thread_id_migration(
conn: &rusqlite::Connection,
user_version: i64,
) -> rusqlite::Result<bool> {
if user_version != THREAD_ID_MIGRATION_INDEX - 1 {
return Ok(false);
}
if !has_column(conn, "pending_requests", "channel_thread_id")? {
return Ok(false);
}
conn.pragma_update(None, "user_version", THREAD_ID_MIGRATION_INDEX)?;
tracing::warn!(
"Stamped past the pending_requests.channel_thread_id migration: the column was already \
present at version {user_version}, so replaying it would have failed startup on a \
duplicate column (#1401)."
);
Ok(true)
}
pub(crate) fn heal_pending_requests_origin(conn: &rusqlite::Connection) -> rusqlite::Result<bool> {
if !has_table(conn, "pending_requests")? || has_column(conn, "pending_requests", "origin")? {
return Ok(false);
}
conn.execute_batch(
"ALTER TABLE pending_requests ADD COLUMN origin TEXT NOT NULL DEFAULT 'user';",
)?;
tracing::warn!(
"Healed pending_requests: the origin column of migration 37 was missing although the \
schema was stamped past it (#1401). Restart recovery could not record turns until now."
);
Ok(true)
}