@@ -7,7 +7,8 @@ use crate::runner::Type;
77use crate :: { error:: Kind , Error , Migration } ;
88
99// Verifies applied and to be applied migrations returning Error if:
10- // - `abort_divergent` is true and there are applied migrations with a different name and checksum but same version as a migration to be applied.
10+ // - `abort_divergent` is true and there are applied migrations with a different name and checksum
11+ // but same version as a migration to be applied.
1112// - `abort_missing` is true and there are applied migrations that are missing on the file system
1213// - there are repeated migrations with the same version to be applied
1314pub ( crate ) fn verify_migrations (
@@ -18,83 +19,80 @@ pub(crate) fn verify_migrations(
1819) -> Result < Vec < Migration > , Error > {
1920 migrations. sort ( ) ;
2021
21- for app in applied. iter ( ) {
22+ for app in & applied {
2223 // iterate applied migrations on database and assert all migrations
2324 // applied on database exist on the file system and have the same checksum
24- match migrations. iter ( ) . find ( |m| m. version ( ) == app. version ( ) ) {
25- None => {
26- if abort_missing {
27- return Err ( Error :: new ( Kind :: MissingVersion ( app. clone ( ) ) , None ) ) ;
28- } else {
29- log:: error!( target: "refinery_core::traits::missing" , "migration {} is missing from the filesystem" , app) ;
30- }
31- }
32- Some ( migration) => {
33- if migration != app {
34- if abort_divergent {
35- return Err ( Error :: new (
36- Kind :: DivergentVersion ( app. clone ( ) , migration. clone ( ) ) ,
37- None ,
38- ) ) ;
39- } else {
40- log:: error!(
41- target: "refinery_core::traits::divergent" ,
42- "applied migration {} is different than filesystem one {}" ,
43- app,
44- migration
45- ) ;
46- }
47- }
25+ if let Err ( _) = migrations. binary_search_by ( |m| m. version ( ) . cmp ( & app. version ( ) ) ) {
26+ if abort_missing {
27+ return Err ( Error :: new ( Kind :: MissingVersion ( app. clone ( ) ) , None ) ) ;
28+ } else {
29+ log:: warn!( target: "refinery_core::traits::missing" , "migration {} is missing from the filesystem" , app) ;
4830 }
4931 }
5032 }
5133
52- let current: i32 = match applied. last ( ) {
53- Some ( last) => {
54- log:: info!( "current version: {}" , last. version( ) ) ;
55- last. version ( ) as i32
56- }
57- None => {
58- log:: info!( "schema history table is empty, going to apply all migrations" ) ;
59- // use -1 as versions might start with 0
60- -1
61- }
34+ let stale = |migration : & Migration | {
35+ applied
36+ . last ( )
37+ . map ( |latest| latest. version ( ) >= migration. version ( ) )
38+ . unwrap_or ( false )
6239 } ;
6340
6441 let mut to_be_applied = Vec :: new ( ) ;
65- // iterate all migration files found on file system and assert that there are not migrations missing:
66- // migrations which its version is inferior to the current version on the database, yet were not applied.
67- // select to be applied all migrations with version greater than current
68- for migration in migrations. into_iter ( ) {
69- if ! applied
42+ // iterate all migration files found on file system and assert that there are not migrations
43+ // missing: migrations which its version is inferior to the current version on the database, yet
44+ // were not applied. select to be applied all migrations with version greater than current
45+ for migration in migrations {
46+ if let Some ( app ) = applied
7047 . iter ( )
71- . any ( |app| app. version ( ) == migration. version ( ) )
48+ . find ( |app| app. version ( ) == migration. version ( ) )
7249 {
73- if to_be_applied. contains ( & migration) {
74- return Err ( Error :: new ( Kind :: RepeatedVersion ( migration) , None ) ) ;
75- } else if migration. prefix ( ) == & Type :: Versioned
76- && current >= migration. version ( ) as i32
77- {
78- if abort_missing {
79- return Err ( Error :: new ( Kind :: MissingVersion ( migration) , None ) ) ;
80- } else {
81- log:: error!( target: "refinery_core::traits::missing" , "found migration on file system {} not applied" , migration) ;
82- }
83- } else {
84- to_be_applied. push ( migration) ;
50+ if * app == migration {
51+ continue ;
52+ }
53+
54+ if abort_divergent {
55+ return Err ( Error :: new (
56+ Kind :: DivergentVersion ( app. clone ( ) , migration. clone ( ) ) ,
57+ None ,
58+ ) ) ;
59+ }
60+
61+ log:: warn!(
62+ target: "refinery_core::traits::divergent" ,
63+ "applied migration {app} is different than filesystem one {migration} => skipping {migration}" ,
64+ ) ;
65+ continue ;
66+ }
67+
68+ if to_be_applied. contains ( & migration) {
69+ return Err ( Error :: new ( Kind :: RepeatedVersion ( migration) , None ) ) ;
70+ }
71+
72+ if migration. prefix ( ) == & Type :: Versioned && stale ( & migration) {
73+ if abort_missing {
74+ return Err ( Error :: new ( Kind :: MissingVersion ( migration) , None ) ) ;
8575 }
76+
77+ log:: error!( target: "refinery_core::traits::missing" , "found strictly versioned, not applied migration on file system => skipping: {migration}" ) ;
78+ continue ;
8679 }
80+
81+ to_be_applied. push ( migration) ;
8782 }
83+
8884 // with these two iterations we both assert that all migrations found on the database
8985 // exist on the file system and have the same checksum, and all migrations found
90- // on the file system are either on the database, or greater than the current, and therefore going to be applied
86+ // on the file system are either on the database, or greater than the current, and therefore going
87+ // to be applied
9188 Ok ( to_be_applied)
9289}
9390
9491pub ( crate ) fn insert_migration_query ( migration : & Migration , migration_table_name : & str ) -> String {
9592 format ! (
9693 "INSERT INTO {} (version, name, applied_on, checksum) VALUES ({}, '{}', '{}', '{}')" ,
97- // safe to call unwrap as we just converted it to applied, and we are sure it can be formatted according to RFC 33339
94+ // safe to call unwrap as we just converted it to applied, and we are sure it can be formatted
95+ // according to RFC 33339
9896 migration_table_name,
9997 migration. version( ) ,
10098 migration. name( ) ,
0 commit comments