@@ -352,15 +352,12 @@ impl Database {
352352 let mut conn = self . pool . get ( ) ?;
353353 let transaction = conn. transaction ( ) ?;
354354
355- // select the max migration id
356- let max_id = max_migration ( & transaction) ;
355+ let max_version = max_migration_version ( & transaction) ;
357356
358357 for ( version, migration) in MIGRATIONS . iter ( ) . enumerate ( ) {
359- // skip migrations that already exist
360- match max_id {
361- Some ( max_id) if max_id >= version as i64 => continue ,
362- _ => ( ) ,
363- } ;
358+ if has_migration ( & transaction, version, max_version) ? {
359+ continue ;
360+ }
364361
365362 // execute the migration
366363 transaction. execute_batch ( migration. sql ) ?;
@@ -443,11 +440,51 @@ impl Database {
443440 }
444441}
445442
446- fn max_migration < C : Deref < Target = Connection > > ( conn : & C ) -> Option < i64 > {
447- let mut stmt = conn. prepare ( "SELECT MAX(id ) FROM migrations" ) . ok ( ) ?;
443+ fn max_migration_version < C : Deref < Target = Connection > > ( conn : & C ) -> Option < i64 > {
444+ let mut stmt = conn. prepare ( "SELECT MAX(version ) FROM migrations" ) . ok ( ) ?;
448445 stmt. query_row ( [ ] , |row| row. get ( 0 ) ) . ok ( )
449446}
450447
448+ fn has_migration < C : Deref < Target = Connection > > (
449+ conn : & C ,
450+ version : usize ,
451+ max_version : Option < i64 > ,
452+ ) -> Result < bool , DatabaseError > {
453+ // IMPORTANT: Due to a bug with the first 7 migrations, we have to check manually
454+ //
455+ // Background: the migrations table stores two identifying keys: the sqlite auto-generated
456+ // auto-incrementing key `id`, and the `version` which is the index of the `MIGRATIONS`
457+ // constant.
458+ //
459+ // Checking whether a migration exists would compare id with version, but since id is 1-indexed
460+ // and version is 0-indexed, we would actually skip the last migration! Therefore, it's
461+ // possible users are missing a critical migration (namely, auth_kv table creation) when
462+ // upgrading to the qchat build (which includes two new migrations). Hence, we have to check
463+ // all migrations until version 7 to make sure that nothing is missed.
464+ if version <= 7 {
465+ let mut stmt = match conn. prepare ( "SELECT COUNT(*) FROM migrations WHERE version = ?1" ) {
466+ Ok ( stmt) => stmt,
467+ // If the migrations table does not exist, then we can reasonably say no migrations
468+ // will exist.
469+ Err ( Error :: SqliteFailure ( _, Some ( msg) ) ) if msg. contains ( "no such table" ) => {
470+ return Ok ( false ) ;
471+ } ,
472+ Err ( err) => return Err ( err. into ( ) ) ,
473+ } ;
474+ let count: i32 = stmt. query_row ( [ version] , |row| row. get ( 0 ) ) ?;
475+ return Ok ( count >= 1 ) ;
476+ }
477+
478+ // Continuing from the previously implemented logic - any migrations after the 7th can have a simple
479+ // maximum version check, since we can reasonably assume if any version >=7 will have all
480+ // migrations prior to it.
481+ #[ allow( clippy:: match_like_matches_macro) ]
482+ Ok ( match max_version {
483+ Some ( max_version) if max_version >= version as i64 => true ,
484+ _ => false ,
485+ } )
486+ }
487+
451488#[ cfg( test) ]
452489mod tests {
453490 use super :: * ;
@@ -477,7 +514,7 @@ mod tests {
477514 let db = Database :: new ( ) . await . unwrap ( ) ;
478515
479516 // assert migration count is correct
480- let max_migration = max_migration ( & & * db. pool . get ( ) . unwrap ( ) ) ;
517+ let max_migration = max_migration_version ( & & * db. pool . get ( ) . unwrap ( ) ) ;
481518 assert_eq ! ( max_migration, Some ( MIGRATIONS . len( ) as i64 ) ) ;
482519 }
483520
0 commit comments