fix: correct schema upgrade logic (and refactor)

Schema upgrades were buggy from 4->5 (the v5 would be skipped).  This
change also refactors the logic slightly so that future additions can
be clearer (no need to have if and else-if combinations).
This commit is contained in:
Greg Heartsfield 2022-10-09 08:24:01 -05:00
parent 2739e49362
commit 2af5f9fbe8
3 changed files with 192 additions and 142 deletions

27
Cargo.lock generated
View File

@ -286,6 +286,26 @@ dependencies = [
"tracing-subscriber 0.3.15", "tracing-subscriber 0.3.15",
] ]
[[package]]
name = "const_format"
version = "0.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79f926bc2341a80e6bb5a16e18057c8c90ca3edbdeb9fa497bd0f82b1f4df4e6"
dependencies = [
"const_format_proc_macros",
]
[[package]]
name = "const_format_proc_macros"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.3" version = "0.9.3"
@ -1008,6 +1028,7 @@ dependencies = [
"bitcoin_hashes", "bitcoin_hashes",
"config", "config",
"console-subscriber", "console-subscriber",
"const_format",
"futures", "futures",
"futures-util", "futures-util",
"governor", "governor",
@ -2216,6 +2237,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]] [[package]]
name = "url" name = "url"
version = "2.3.1" version = "2.3.1"

View File

@ -31,6 +31,7 @@ hyper-tls = "0.5"
http = { version = "0.2" } http = { version = "0.2" }
parse_duration = "2" parse_duration = "2"
rand = "0.8" rand = "0.8"
const_format = "0.2.28"
[dev-dependencies] [dev-dependencies]
anyhow = "1" anyhow = "1"

View File

@ -3,9 +3,11 @@ use crate::db::PooledConnection;
use crate::error::Result; use crate::error::Result;
use crate::event::{single_char_tagname, Event}; use crate::event::{single_char_tagname, Event};
use crate::utils::is_lower_hex; use crate::utils::is_lower_hex;
use const_format::formatcp;
use rusqlite::limits::Limit; use rusqlite::limits::Limit;
use rusqlite::params; use rusqlite::params;
use rusqlite::Connection; use rusqlite::Connection;
use std::cmp::Ordering;
use std::time::Instant; use std::time::Instant;
use tracing::{debug, error, info}; use tracing::{debug, error, info};
@ -17,15 +19,19 @@ PRAGMA journal_size_limit=32768;
pragma mmap_size = 536870912; -- 512MB of mmap pragma mmap_size = 536870912; -- 512MB of mmap
"##; "##;
/// Latest database version
pub const DB_VERSION: usize = 6;
/// Schema definition /// Schema definition
const INIT_SQL: &str = r##" const INIT_SQL: &str = formatcp!(
r##"
-- Database settings -- Database settings
PRAGMA encoding = "UTF-8"; PRAGMA encoding = "UTF-8";
PRAGMA journal_mode=WAL; PRAGMA journal_mode=WAL;
PRAGMA main.synchronous=NORMAL; PRAGMA main.synchronous=NORMAL;
PRAGMA foreign_keys = ON; PRAGMA foreign_keys = ON;
PRAGMA application_id = 1654008667; PRAGMA application_id = 1654008667;
PRAGMA user_version = 6; PRAGMA user_version = {};
-- Event Table -- Event Table
CREATE TABLE IF NOT EXISTS event ( CREATE TABLE IF NOT EXISTS event (
@ -72,7 +78,9 @@ FOREIGN KEY(metadata_event) REFERENCES event(id) ON UPDATE CASCADE ON DELETE CAS
); );
CREATE INDEX IF NOT EXISTS user_verification_name_index ON user_verification(name); CREATE INDEX IF NOT EXISTS user_verification_name_index ON user_verification(name);
CREATE INDEX IF NOT EXISTS user_verification_event_index ON user_verification(metadata_event); CREATE INDEX IF NOT EXISTS user_verification_event_index ON user_verification(metadata_event);
"##; "##,
DB_VERSION
);
/// Determine the current application database schema version. /// Determine the current application database schema version.
pub fn db_version(conn: &mut Connection) -> Result<usize> { pub fn db_version(conn: &mut Connection) -> Result<usize> {
@ -100,6 +108,9 @@ pub fn upgrade_db(conn: &mut PooledConnection) -> Result<()> {
(conn.limit(Limit::SQLITE_LIMIT_SQL_LENGTH) as f64 / (1024 * 1024) as f64).floor() (conn.limit(Limit::SQLITE_LIMIT_SQL_LENGTH) as f64 / (1024 * 1024) as f64).floor()
); );
match curr_version.cmp(&DB_VERSION) {
// Database is new or not current
Ordering::Less => {
// initialize from scratch // initialize from scratch
if curr_version == 0 { if curr_version == 0 {
match conn.execute_batch(INIT_SQL) { match conn.execute_batch(INIT_SQL) {
@ -112,6 +123,7 @@ pub fn upgrade_db(conn: &mut PooledConnection) -> Result<()> {
} }
} }
} }
if curr_version == 1 { if curr_version == 1 {
// only change is adding a hidden column to events. // only change is adding a hidden column to events.
let upgrade_sql = r##" let upgrade_sql = r##"
@ -130,6 +142,7 @@ PRAGMA user_version = 2;
} }
} }
} }
if curr_version == 2 { if curr_version == 2 {
// this version lacks the tag column // this version lacks the tag column
info!("database schema needs update from 2->3"); info!("database schema needs update from 2->3");
@ -177,6 +190,7 @@ PRAGMA user_version = 3;
tx.commit()?; tx.commit()?;
info!("Upgrade complete"); info!("Upgrade complete");
} }
if curr_version == 3 { if curr_version == 3 {
info!("database schema needs update from 3->4"); info!("database schema needs update from 3->4");
let upgrade_sql = r##" let upgrade_sql = r##"
@ -224,7 +238,9 @@ PRAGMA user_version=5;
panic!("database could not be upgraded"); panic!("database could not be upgraded");
} }
} }
} else if curr_version == 5 { }
if curr_version == 5 {
info!("database schema needs update from 5->6"); info!("database schema needs update from 5->6");
// We need to rebuild the tags table. iterate through the // We need to rebuild the tags table. iterate through the
// event table. build event from json, insert tags into a // event table. build event from json, insert tags into a
@ -275,14 +291,20 @@ PRAGMA user_version=5;
let start = Instant::now(); let start = Instant::now();
conn.execute("VACUUM;", [])?; conn.execute("VACUUM;", [])?;
info!("vacuumed DB after tags rebuild in {:?}", start.elapsed()); info!("vacuumed DB after tags rebuild in {:?}", start.elapsed());
} else if curr_version == 6 { }
debug!("Database version was already current (v6)"); }
} else if curr_version > 6 { // Database is current, all is good
Ordering::Equal => {
debug!("Database version was already current (v{})", DB_VERSION);
}
// Database is newer than what this code understands, abort
Ordering::Greater => {
panic!( panic!(
"Database version is newer than supported by this executable (v{})", "Database version is newer than supported by this executable (v{} > v{})",
curr_version curr_version, DB_VERSION
); );
} }
}
// Setup PRAGMA // Setup PRAGMA
conn.execute_batch(STARTUP_SQL)?; conn.execute_batch(STARTUP_SQL)?;