@@ -3,8 +3,7 @@ use chrono::Utc;
3
3
use slog:: { debug, error, info, Logger } ;
4
4
use std:: { cmp:: Ordering , collections:: BTreeSet } ;
5
5
6
- use mithril_common:: logging:: LoggerExtensions ;
7
- use mithril_common:: StdResult ;
6
+ use mithril_common:: { logging:: LoggerExtensions , StdError , StdResult } ;
8
7
9
8
use super :: {
10
9
ApplicationNodeType , DatabaseVersion , DbVersion , GetDatabaseVersionQuery ,
@@ -155,6 +154,10 @@ insert into db_version (application_type, version, updated_at) values ('{applica
155
154
Ok ( ( ) )
156
155
}
157
156
157
+ /// Checks if the database version meets the minimum required version to apply a squashed migration.
158
+ /// If the database version 0 or if the migration doesn't specify a fallback distribution version, the check passes.
159
+ /// For migrations with a fallback distribution version, the check passes if the database version is exactly
160
+ /// one less than the migration version (i.e., there's no gap between them).
158
161
fn check_minimum_required_version (
159
162
& self ,
160
163
db_version : DbVersion ,
@@ -167,29 +170,40 @@ insert into db_version (application_type, version, updated_at) values ('{applica
167
170
if let Some ( fallback_distribution_version) = & migration. fallback_distribution_version {
168
171
let min_required_version = migration. version - 1 ;
169
172
if db_version < min_required_version {
170
- return Err ( anyhow ! (
171
- r#"
172
- Minimum required database version is not met to apply migration '{}'.
173
- Please migrate your {} node database with the minimum node version compatible available in the distribution: '{}'.
174
-
175
- First, download the required node version in your current directory by running the following command:
176
- curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/input-output-hk/mithril/refs/heads/main/mithril-install.sh | sh -s -- -c mithril-{} -d {} -p $(pwd)
177
-
178
- Then run the database migrate command:
179
- mithril-{} database migrate --stores-directory /path/to/stores-directory
180
- "# ,
173
+ return Err ( self . generate_fallback_migration_error (
181
174
migration. version ,
182
- self . application_type. to_string( ) ,
183
- fallback_distribution_version,
184
- self . application_type. to_string( ) ,
185
175
fallback_distribution_version,
186
- self . application_type. to_string( )
187
176
) ) ;
188
177
}
189
178
}
190
179
191
180
Ok ( ( ) )
192
181
}
182
+
183
+ fn generate_fallback_migration_error (
184
+ & self ,
185
+ migration_version : i64 ,
186
+ fallback_distribution_version : & str ,
187
+ ) -> StdError {
188
+ anyhow ! (
189
+ r#"
190
+ Minimum required database version is not met to apply migration '{}'.
191
+ Please migrate your {} node database with the minimum node version compatible available in the distribution: '{}'.
192
+
193
+ First, download the required node version in your current directory by running the following command:
194
+ curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/input-output-hk/mithril/refs/heads/main/mithril-install.sh | sh -s -- -c mithril-{} -d {} -p $(pwd)
195
+
196
+ Then run the database migrate command:
197
+ mithril-{} database migrate --stores-directory /path/to/stores-directory
198
+ "# ,
199
+ migration_version,
200
+ self . application_type. to_string( ) ,
201
+ fallback_distribution_version,
202
+ self . application_type. to_string( ) ,
203
+ fallback_distribution_version,
204
+ self . application_type. to_string( )
205
+ )
206
+ }
193
207
}
194
208
195
209
/// Represent a file containing SQL structure or data alterations.
@@ -253,12 +267,15 @@ impl Eq for SqlMigration {}
253
267
mod tests {
254
268
use anyhow:: Context ;
255
269
use mithril_common:: test_utils:: TempDir ;
256
- use mithril_common:: StdResult ;
257
- use sqlite:: Connection ;
270
+ use mithril_common:: { current_function , StdResult } ;
271
+ use sqlite:: { Connection , ConnectionThreadSafe } ;
258
272
use std:: path:: PathBuf ;
259
273
260
274
use super :: * ;
261
275
276
+ const CREATE_TABLE_SQL_REQUEST : & str = "create table whatever (thing_id integer); insert into whatever (thing_id) values (1), (2), (3), (4);" ;
277
+ const ALTER_TABLE_SQL_REQUEST : & str = "alter table whatever add column thing_content text; update whatever set thing_content = 'some content'" ;
278
+
262
279
fn discard_logger ( ) -> Logger {
263
280
Logger :: root ( slog:: Discard , slog:: o!( ) )
264
281
}
@@ -298,15 +315,18 @@ mod tests {
298
315
column_count
299
316
}
300
317
301
- #[ test]
302
- fn test_upgrade_with_migration ( ) {
303
- let ( _filepath, connection) =
304
- create_sqlite_file ( "test_upgrade_with_migration.sqlite3" ) . unwrap ( ) ;
305
- let mut db_checker = DatabaseVersionChecker :: new (
318
+ fn create_db_checker ( connection : & ConnectionThreadSafe ) -> DatabaseVersionChecker {
319
+ DatabaseVersionChecker :: new (
306
320
discard_logger ( ) ,
307
321
ApplicationNodeType :: Aggregator ,
308
- & connection,
309
- ) ;
322
+ connection,
323
+ )
324
+ }
325
+
326
+ #[ test]
327
+ fn test_upgrade_with_migration ( ) {
328
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
329
+ let mut db_checker = create_db_checker ( & connection) ;
310
330
311
331
db_checker. apply ( ) . unwrap ( ) ;
312
332
assert_eq ! ( 0 , get_table_whatever_column_count( & connection) ) ;
@@ -364,13 +384,8 @@ mod tests {
364
384
365
385
#[ test]
366
386
fn test_upgrade_with_migration_with_a_version_gap ( ) {
367
- let ( _filepath, connection) =
368
- create_sqlite_file ( "test_upgrade_with_migration_with_a_version_gap.sqlite3" ) . unwrap ( ) ;
369
- let mut db_checker = DatabaseVersionChecker :: new (
370
- discard_logger ( ) ,
371
- ApplicationNodeType :: Aggregator ,
372
- & connection,
373
- ) ;
387
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
388
+ let mut db_checker = create_db_checker ( & connection) ;
374
389
375
390
let alterations = "create table whatever (thing_id integer); insert into whatever (thing_id) values (1), (2), (3), (4);" ;
376
391
let migration = SqlMigration {
@@ -397,12 +412,8 @@ mod tests {
397
412
398
413
#[ test]
399
414
fn starting_with_migration ( ) {
400
- let ( _filepath, connection) = create_sqlite_file ( "starting_with_migration" ) . unwrap ( ) ;
401
- let mut db_checker = DatabaseVersionChecker :: new (
402
- discard_logger ( ) ,
403
- ApplicationNodeType :: Aggregator ,
404
- & connection,
405
- ) ;
415
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
416
+ let mut db_checker = create_db_checker ( & connection) ;
406
417
407
418
let alterations = "create table whatever (thing_id integer); insert into whatever (thing_id) values (1), (2), (3), (4);" ;
408
419
let migration = SqlMigration {
@@ -421,12 +432,8 @@ mod tests {
421
432
/// * previous migrations are ok and the database version is updated
422
433
/// * further migrations are not played.
423
434
fn test_failing_migration ( ) {
424
- let ( _filepath, connection) = create_sqlite_file ( "test_failing_migration" ) . unwrap ( ) ;
425
- let mut db_checker = DatabaseVersionChecker :: new (
426
- discard_logger ( ) ,
427
- ApplicationNodeType :: Aggregator ,
428
- & connection,
429
- ) ;
435
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
436
+ let mut db_checker = create_db_checker ( & connection) ;
430
437
// Table whatever does not exist, this should fail with error.
431
438
let alterations = "create table whatever (thing_id integer); insert into whatever (thing_id) values (1), (2), (3), (4);" ;
432
439
let migration = SqlMigration {
@@ -455,28 +462,19 @@ mod tests {
455
462
456
463
#[ test]
457
464
fn test_fail_downgrading ( ) {
458
- let ( _filepath, connection) = create_sqlite_file ( "test_fail_downgrading" ) . unwrap ( ) ;
459
- let mut db_checker = DatabaseVersionChecker :: new (
460
- discard_logger ( ) ,
461
- ApplicationNodeType :: Aggregator ,
462
- & connection,
463
- ) ;
464
- let alterations = "create table whatever (thing_id integer); insert into whatever (thing_id) values (1), (2), (3), (4);" ;
465
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
466
+ let mut db_checker = create_db_checker ( & connection) ;
465
467
let migration = SqlMigration {
466
468
version : 1 ,
467
- alterations : alterations . to_string ( ) ,
469
+ alterations : CREATE_TABLE_SQL_REQUEST . to_string ( ) ,
468
470
fallback_distribution_version : None ,
469
471
} ;
470
472
db_checker. add_migration ( migration) ;
471
473
db_checker. apply ( ) . unwrap ( ) ;
472
474
check_database_version ( & connection, 1 ) ;
473
475
474
476
// re instantiate a new checker with no migration registered (version 0).
475
- let db_checker = DatabaseVersionChecker :: new (
476
- discard_logger ( ) ,
477
- ApplicationNodeType :: Aggregator ,
478
- & connection,
479
- ) ;
477
+ let db_checker = create_db_checker ( & connection) ;
480
478
assert ! (
481
479
db_checker. apply( ) . is_err( ) ,
482
480
"using an old version with an up to date database should fail"
@@ -486,17 +484,10 @@ mod tests {
486
484
487
485
#[ test]
488
486
fn check_minimum_required_version_does_not_fail_when_no_fallback_distribution_version ( ) {
489
- let ( _filepath, connection) = create_sqlite_file (
490
- "check_minimum_required_version_does_not_fail_when_no_fallback_distribution_version" ,
491
- )
492
- . unwrap ( ) ;
493
- let db_checker = DatabaseVersionChecker :: new (
494
- discard_logger ( ) ,
495
- ApplicationNodeType :: Aggregator ,
496
- & connection,
497
- ) ;
487
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
488
+ let db_checker = create_db_checker ( & connection) ;
498
489
499
- let alterations = "create table whatever (thing_id integer); insert into whatever (thing_id) values (1), (2), (3), (4);" ;
490
+ let alterations = CREATE_TABLE_SQL_REQUEST ;
500
491
let migration = SqlMigration {
501
492
version : 3 ,
502
493
alterations : alterations. to_string ( ) ,
@@ -513,17 +504,10 @@ mod tests {
513
504
#[ test]
514
505
fn check_minimum_required_version_does_not_fail_when_fallback_distribution_version_with_fresh_database (
515
506
) {
516
- let ( _filepath, connection) = create_sqlite_file (
517
- "check_minimum_required_version_does_not_fail_when_fallback_distribution_version_with_fresh_database" ,
518
- )
519
- . unwrap ( ) ;
520
- let db_checker = DatabaseVersionChecker :: new (
521
- discard_logger ( ) ,
522
- ApplicationNodeType :: Aggregator ,
523
- & connection,
524
- ) ;
507
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
508
+ let db_checker = create_db_checker ( & connection) ;
525
509
526
- let alterations = "create table whatever (thing_id integer); insert into whatever (thing_id) values (1), (2), (3), (4);" ;
510
+ let alterations = CREATE_TABLE_SQL_REQUEST ;
527
511
let migration = SqlMigration {
528
512
version : 2 ,
529
513
alterations : alterations. to_string ( ) ,
@@ -538,20 +522,12 @@ mod tests {
538
522
#[ test]
539
523
fn check_minimum_required_version_does_not_fail_when_no_gap_between_db_version_and_migration_version (
540
524
) {
541
- let ( _filepath, connection) = create_sqlite_file (
542
- "check_minimum_required_version_does_not_fail_when_no_gap_between_db_version_and_migration_version" ,
543
- )
544
- . unwrap ( ) ;
545
- let db_checker = DatabaseVersionChecker :: new (
546
- discard_logger ( ) ,
547
- ApplicationNodeType :: Aggregator ,
548
- & connection,
549
- ) ;
525
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
526
+ let db_checker = create_db_checker ( & connection) ;
550
527
551
- let alterations = "alter table whatever add column thing_content text; update whatever set thing_content = 'some content'" ;
552
528
let migration = SqlMigration {
553
529
version : 2 ,
554
- alterations : alterations . to_string ( ) ,
530
+ alterations : CREATE_TABLE_SQL_REQUEST . to_string ( ) ,
555
531
fallback_distribution_version : Some ( "2511.0" . to_string ( ) ) ,
556
532
} ;
557
533
@@ -562,20 +538,16 @@ mod tests {
562
538
563
539
#[ test]
564
540
fn check_minimum_required_version_fails_when_gap_between_db_version_and_migration_version ( ) {
565
- let ( _filepath, connection) = create_sqlite_file (
566
- "check_minimum_required_version_fails_when_gap_between_db_version_and_migration_version" ,
567
- )
568
- . unwrap ( ) ;
541
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
569
542
let db_checker = DatabaseVersionChecker :: new (
570
543
discard_logger ( ) ,
571
544
ApplicationNodeType :: Aggregator ,
572
545
& connection,
573
546
) ;
574
547
575
- let alterations = "alter table whatever add column thing_content text; update whatever set thing_content = 'some content'" ;
576
548
let migration = SqlMigration {
577
549
version : 3 ,
578
- alterations : alterations . to_string ( ) ,
550
+ alterations : CREATE_TABLE_SQL_REQUEST . to_string ( ) ,
579
551
fallback_distribution_version : Some ( "2511.0" . to_string ( ) ) ,
580
552
} ;
581
553
@@ -588,28 +560,25 @@ mod tests {
588
560
589
561
#[ test]
590
562
fn apply_fails_when_trying_to_apply_squashed_migration_on_old_database ( ) {
591
- let ( _filepath, connection) =
592
- create_sqlite_file ( "apply_fails_with_squashed_migration" ) . unwrap ( ) ;
563
+ let ( _filepath, connection) = create_sqlite_file ( current_function ! ( ) ) . unwrap ( ) ;
593
564
let mut db_checker = DatabaseVersionChecker :: new (
594
565
discard_logger ( ) ,
595
566
ApplicationNodeType :: Aggregator ,
596
567
& connection,
597
568
) ;
598
569
599
- let alterations = "create table whatever (thing_id integer); insert into whatever (thing_id) values (1), (2), (3), (4);" ;
600
570
let migration = SqlMigration {
601
571
version : 1 ,
602
- alterations : alterations . to_string ( ) ,
572
+ alterations : CREATE_TABLE_SQL_REQUEST . to_string ( ) ,
603
573
fallback_distribution_version : None ,
604
574
} ;
605
575
db_checker. add_migration ( migration) ;
606
576
db_checker. apply ( ) . unwrap ( ) ;
607
577
check_database_version ( & connection, 1 ) ;
608
578
609
- let alterations = "alter table whatever add column thing_content text; update whatever set thing_content = 'some content'" ;
610
579
let squashed_migration = SqlMigration {
611
580
version : 3 ,
612
- alterations : alterations . to_string ( ) ,
581
+ alterations : ALTER_TABLE_SQL_REQUEST . to_string ( ) ,
613
582
fallback_distribution_version : Some ( "2511.0" . to_string ( ) ) ,
614
583
} ;
615
584
db_checker. add_migration ( squashed_migration) ;
0 commit comments