fix: idempotent migrations (ADD COLUMN IF NOT EXISTS), stale doc, safety note
This commit is contained in:
parent
7d36ec5e1f
commit
c47faf0af9
5 changed files with 80 additions and 26 deletions
|
|
@ -80,6 +80,11 @@ pub fn apply_versioned_migrations(
|
|||
// old try-and-ignore path — skip them. If it doesn't exist, start
|
||||
// at 0 (fresh install or partial state).
|
||||
let (legacy_table, legacy_col) = legacy_column;
|
||||
// SAFETY: `legacy_table` is always a static table-name literal
|
||||
// supplied by each store's `open` (e.g. `"messages"`). Dynamic
|
||||
// or untrusted table names must not be passed here — if that
|
||||
// ever changes, switch to `pragma_table_info(?1)` with two
|
||||
// bound parameters.
|
||||
let is_legacy: bool = conn
|
||||
.prepare(&format!(
|
||||
"SELECT 1 FROM pragma_table_info('{legacy_table}') WHERE name = ?1"
|
||||
|
|
@ -143,9 +148,9 @@ mod tests {
|
|||
|
||||
const TEST_SCHEMA: &str = "CREATE TABLE IF NOT EXISTS things (id INTEGER PRIMARY KEY)";
|
||||
const TEST_MIGRATIONS: &[&str] = &[
|
||||
"ALTER TABLE things ADD COLUMN alpha TEXT",
|
||||
"ALTER TABLE things ADD COLUMN beta INTEGER NOT NULL DEFAULT 0",
|
||||
"ALTER TABLE things ADD COLUMN gamma TEXT",
|
||||
"ALTER TABLE things ADD COLUMN IF NOT EXISTS alpha TEXT",
|
||||
"ALTER TABLE things ADD COLUMN IF NOT EXISTS beta INTEGER NOT NULL DEFAULT 0",
|
||||
"ALTER TABLE things ADD COLUMN IF NOT EXISTS gamma TEXT",
|
||||
];
|
||||
|
||||
#[test]
|
||||
|
|
@ -289,4 +294,44 @@ mod tests {
|
|||
assert_eq!(va, 1);
|
||||
assert_eq!(vb, 1);
|
||||
}
|
||||
|
||||
/// A partially-migrated legacy database: some columns from the old
|
||||
/// try-and-ignore path exist, but the legacy marker column (used to
|
||||
/// fast-forward fresh schema_versions entries to `len`) is absent.
|
||||
/// With idempotent `ADD COLUMN IF NOT EXISTS` migrations this must
|
||||
/// succeed — the already-present columns are skipped and only the
|
||||
/// missing ones are added.
|
||||
#[test]
|
||||
fn partial_legacy_completes_without_error() {
|
||||
let conn = open_tmp();
|
||||
// Simulate a DB that has `alpha` (v1) but not `beta` (v2) or
|
||||
// `gamma` (v3). No schema_versions row exists yet, and `gamma`
|
||||
// (the legacy marker) is absent → the function cannot fast-forward
|
||||
// and must start from v0 with idempotent migrations.
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS things (id INTEGER PRIMARY KEY, alpha TEXT)",
|
||||
)
|
||||
.unwrap();
|
||||
apply_versioned_migrations(&conn, "things", ("things", "gamma"), TEST_MIGRATIONS).unwrap();
|
||||
// All three columns must now exist.
|
||||
for col in ["alpha", "beta", "gamma"] {
|
||||
let exists: bool = conn
|
||||
.prepare(&format!(
|
||||
"SELECT 1 FROM pragma_table_info('things') WHERE name = '{col}'"
|
||||
))
|
||||
.unwrap()
|
||||
.exists([])
|
||||
.unwrap();
|
||||
assert!(exists, "column {col} missing after partial-legacy migration");
|
||||
}
|
||||
// Version must be at the latest.
|
||||
let v: i64 = conn
|
||||
.query_row(
|
||||
"SELECT version FROM schema_versions WHERE store = 'things'",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(v, 3, "must reach latest version after completing partial legacy");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue