1#![cfg(feature = "full-node")]
17
18use super::{columns, other_io_error, DatabaseKind, LOG_TARGET};
19use std::{
20 fs, io,
21 path::{Path, PathBuf},
22 str::FromStr,
23 sync::Arc,
24};
25
26use polkadot_node_core_approval_voting::approval_db::{
27 common::{Config as ApprovalDbConfig, Result as ApprovalDbResult},
28 v2::migration_helpers::v1_to_latest,
29 v3::migration_helpers::v2_to_latest,
30};
31use polkadot_node_subsystem_util::database::{
32 kvdb_impl::DbAdapter as RocksDbAdapter, paritydb_impl::DbAdapter as ParityDbAdapter, Database,
33};
34type Version = u32;
35
36const VERSION_FILE_NAME: &'static str = "parachain_db_version";
38
39pub(crate) const CURRENT_VERSION: Version = 5;
44
45#[derive(thiserror::Error, Debug)]
46pub enum Error {
47 #[error("I/O error when reading/writing the version")]
48 Io(#[from] io::Error),
49 #[error("The version file format is incorrect")]
50 CorruptedVersionFile,
51 #[error("Parachains DB has a future version (expected {current:?}, found {got:?})")]
52 FutureVersion { current: Version, got: Version },
53 #[error("Parachain DB migration failed")]
54 MigrationFailed,
55 #[error("Parachain DB migration would take forever")]
56 MigrationLoop,
57}
58
59impl From<Error> for io::Error {
60 fn from(me: Error) -> io::Error {
61 match me {
62 Error::Io(e) => e,
63 _ => super::other_io_error(me.to_string()),
64 }
65 }
66}
67
68pub(crate) fn try_upgrade_db(
70 db_path: &Path,
71 db_kind: DatabaseKind,
72 target_version: Version,
73) -> Result<(), Error> {
74 const MAX_MIGRATIONS: u32 = 30;
76
77 #[cfg(test)]
78 remove_file_lock(&db_path);
79
80 for _ in 0..MAX_MIGRATIONS {
82 let version = try_upgrade_db_to_next_version(db_path, db_kind)?;
83
84 #[cfg(test)]
85 remove_file_lock(&db_path);
86
87 if version == target_version {
88 return Ok(())
89 }
90 }
91
92 Err(Error::MigrationLoop)
93}
94
95pub(crate) fn try_upgrade_db_to_next_version(
98 db_path: &Path,
99 db_kind: DatabaseKind,
100) -> Result<Version, Error> {
101 let is_empty = db_path.read_dir().map_or(true, |mut d| d.next().is_none());
102
103 let new_version = if !is_empty {
104 match get_db_version(db_path)? {
105 Some(0) => migrate_from_version_0_to_1(db_path, db_kind)?,
107 Some(1) => migrate_from_version_1_to_2(db_path, db_kind)?,
109 Some(2) => migrate_from_version_2_to_3(db_path, db_kind)?,
111 Some(3) => migrate_from_version_3_or_4_to_5(db_path, db_kind, v1_to_latest)?,
113 Some(4) => migrate_from_version_3_or_4_to_5(db_path, db_kind, v2_to_latest)?,
114 Some(CURRENT_VERSION) => CURRENT_VERSION,
116 Some(v) => return Err(Error::FutureVersion { current: CURRENT_VERSION, got: v }),
118 None if db_kind == DatabaseKind::RocksDB => CURRENT_VERSION,
120 None if db_kind == DatabaseKind::ParityDB =>
123 migrate_from_version_0_to_1(db_path, db_kind)?,
124 None => unreachable!(),
125 }
126 } else {
127 CURRENT_VERSION
128 };
129
130 update_version(db_path, new_version)?;
131 Ok(new_version)
132}
133
134fn get_db_version(path: &Path) -> Result<Option<Version>, Error> {
137 match fs::read_to_string(version_file_path(path)) {
138 Err(ref err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
139 Err(err) => Err(err.into()),
140 Ok(content) => u32::from_str(&content)
141 .map(|v| Some(v))
142 .map_err(|_| Error::CorruptedVersionFile),
143 }
144}
145
146fn update_version(path: &Path, new_version: Version) -> Result<(), Error> {
149 fs::create_dir_all(path)?;
150 fs::write(version_file_path(path), new_version.to_string()).map_err(Into::into)
151}
152
153fn version_file_path(path: &Path) -> PathBuf {
155 let mut file_path = path.to_owned();
156 file_path.push(VERSION_FILE_NAME);
157 file_path
158}
159
160fn migrate_from_version_0_to_1(path: &Path, db_kind: DatabaseKind) -> Result<Version, Error> {
161 gum::info!(target: LOG_TARGET, "Migrating parachains db from version 0 to version 1 ...");
162
163 match db_kind {
164 DatabaseKind::ParityDB => paritydb_migrate_from_version_0_to_1(path),
165 DatabaseKind::RocksDB => rocksdb_migrate_from_version_0_to_1(path),
166 }
167 .and_then(|result| {
168 gum::info!(target: LOG_TARGET, "Migration complete! ");
169 Ok(result)
170 })
171}
172
173fn migrate_from_version_1_to_2(path: &Path, db_kind: DatabaseKind) -> Result<Version, Error> {
174 gum::info!(target: LOG_TARGET, "Migrating parachains db from version 1 to version 2 ...");
175
176 match db_kind {
177 DatabaseKind::ParityDB => paritydb_migrate_from_version_1_to_2(path),
178 DatabaseKind::RocksDB => rocksdb_migrate_from_version_1_to_2(path),
179 }
180 .and_then(|result| {
181 gum::info!(target: LOG_TARGET, "Migration complete! ");
182 Ok(result)
183 })
184}
185
186fn migrate_from_version_3_or_4_to_5<F>(
191 path: &Path,
192 db_kind: DatabaseKind,
193 migration_function: F,
194) -> Result<Version, Error>
195where
196 F: Fn(Arc<dyn Database>, ApprovalDbConfig) -> ApprovalDbResult<()>,
197{
198 gum::info!(target: LOG_TARGET, "Migrating parachains db from version 3 to version 4 ...");
199
200 let approval_db_config =
201 ApprovalDbConfig { col_approval_data: super::REAL_COLUMNS.col_approval_data };
202
203 let _result = match db_kind {
204 DatabaseKind::ParityDB => {
205 let db = ParityDbAdapter::new(
206 parity_db::Db::open(&paritydb_version_3_config(path))
207 .map_err(|e| other_io_error(format!("Error opening db {:?}", e)))?,
208 super::columns::v3::ORDERED_COL,
209 );
210
211 migration_function(Arc::new(db), approval_db_config)
212 .map_err(|_| Error::MigrationFailed)?;
213 },
214 DatabaseKind::RocksDB => {
215 let db_path = path
216 .to_str()
217 .ok_or_else(|| super::other_io_error("Invalid database path".into()))?;
218 let db_cfg =
219 kvdb_rocksdb::DatabaseConfig::with_columns(super::columns::v3::NUM_COLUMNS);
220 let db = RocksDbAdapter::new(
221 kvdb_rocksdb::Database::open(&db_cfg, db_path)?,
222 &super::columns::v3::ORDERED_COL,
223 );
224
225 migration_function(Arc::new(db), approval_db_config)
226 .map_err(|_| Error::MigrationFailed)?;
227 },
228 };
229
230 gum::info!(target: LOG_TARGET, "Migration complete! ");
231 Ok(CURRENT_VERSION)
232}
233
234fn migrate_from_version_2_to_3(path: &Path, db_kind: DatabaseKind) -> Result<Version, Error> {
235 gum::info!(target: LOG_TARGET, "Migrating parachains db from version 2 to version 3 ...");
236 match db_kind {
237 DatabaseKind::ParityDB => paritydb_migrate_from_version_2_to_3(path),
238 DatabaseKind::RocksDB => rocksdb_migrate_from_version_2_to_3(path),
239 }
240 .and_then(|result| {
241 gum::info!(target: LOG_TARGET, "Migration complete! ");
242 Ok(result)
243 })
244}
245
246fn rocksdb_migrate_from_version_0_to_1(path: &Path) -> Result<Version, Error> {
249 use kvdb_rocksdb::{Database, DatabaseConfig};
250
251 let db_path = path
252 .to_str()
253 .ok_or_else(|| super::other_io_error("Invalid database path".into()))?;
254 let db_cfg = DatabaseConfig::with_columns(super::columns::v0::NUM_COLUMNS);
255 let mut db = Database::open(&db_cfg, db_path)?;
256
257 db.add_column()?;
258 db.add_column()?;
259
260 Ok(1)
261}
262
263fn rocksdb_migrate_from_version_1_to_2(path: &Path) -> Result<Version, Error> {
266 use kvdb_rocksdb::{Database, DatabaseConfig};
267
268 let db_path = path
269 .to_str()
270 .ok_or_else(|| super::other_io_error("Invalid database path".into()))?;
271 let db_cfg = DatabaseConfig::with_columns(super::columns::v1::NUM_COLUMNS);
272 let mut db = Database::open(&db_cfg, db_path)?;
273
274 db.add_column()?;
275
276 Ok(2)
277}
278
279fn rocksdb_migrate_from_version_2_to_3(path: &Path) -> Result<Version, Error> {
280 use kvdb_rocksdb::{Database, DatabaseConfig};
281
282 let db_path = path
283 .to_str()
284 .ok_or_else(|| super::other_io_error("Invalid database path".into()))?;
285 let db_cfg = DatabaseConfig::with_columns(super::columns::v2::NUM_COLUMNS);
286 let mut db = Database::open(&db_cfg, db_path)?;
287
288 db.remove_last_column()?;
289
290 Ok(3)
291}
292
293fn paritydb_fix_columns(
296 path: &Path,
297 options: parity_db::Options,
298 allowed_columns: Vec<u32>,
299) -> io::Result<()> {
300 if let Some(metadata) = parity_db::Options::load_metadata(&path)
303 .map_err(|e| other_io_error(format!("Error reading metadata {:?}", e)))?
304 {
305 let columns_to_clear = metadata
306 .columns
307 .into_iter()
308 .enumerate()
309 .filter(|(idx, _)| allowed_columns.contains(&(*idx as u32)))
310 .filter_map(|(idx, opts)| {
311 let changed = opts != options.columns[idx];
312 if changed {
313 gum::debug!(
314 target: LOG_TARGET,
315 "Column {} will be cleared. Old options: {:?}, New options: {:?}",
316 idx,
317 opts,
318 options.columns[idx]
319 );
320 Some(idx)
321 } else {
322 None
323 }
324 })
325 .collect::<Vec<_>>();
326
327 if columns_to_clear.len() > 0 {
328 gum::debug!(
329 target: LOG_TARGET,
330 "Database column changes detected, need to cleanup {} columns.",
331 columns_to_clear.len()
332 );
333 }
334
335 for column in columns_to_clear {
336 gum::debug!(target: LOG_TARGET, "Clearing column {}", column,);
337 parity_db::clear_column(path, column.try_into().expect("Invalid column ID"))
338 .map_err(|e| other_io_error(format!("Error clearing column {:?}", e)))?;
339 }
340
341 options
343 .write_metadata(path, &metadata.salt)
344 .map_err(|e| other_io_error(format!("Error writing metadata {:?}", e)))?;
345 }
346
347 Ok(())
348}
349
350pub(crate) fn paritydb_version_1_config(path: &Path) -> parity_db::Options {
352 let mut options =
353 parity_db::Options::with_columns(&path, super::columns::v1::NUM_COLUMNS as u8);
354 for i in columns::v4::ORDERED_COL {
355 options.columns[*i as usize].btree_index = true;
356 }
357
358 options
359}
360
361pub(crate) fn paritydb_version_2_config(path: &Path) -> parity_db::Options {
363 let mut options =
364 parity_db::Options::with_columns(&path, super::columns::v2::NUM_COLUMNS as u8);
365 for i in columns::v4::ORDERED_COL {
366 options.columns[*i as usize].btree_index = true;
367 }
368
369 options
370}
371
372pub(crate) fn paritydb_version_3_config(path: &Path) -> parity_db::Options {
374 let mut options =
375 parity_db::Options::with_columns(&path, super::columns::v3::NUM_COLUMNS as u8);
376 for i in columns::v3::ORDERED_COL {
377 options.columns[*i as usize].btree_index = true;
378 }
379
380 options
381}
382
383#[cfg(test)]
385pub(crate) fn paritydb_version_0_config(path: &Path) -> parity_db::Options {
386 let mut options =
387 parity_db::Options::with_columns(&path, super::columns::v0::NUM_COLUMNS as u8);
388 options.columns[super::columns::v4::COL_AVAILABILITY_META as usize].btree_index = true;
389
390 options
391}
392
393fn paritydb_migrate_from_version_0_to_1(path: &Path) -> Result<Version, Error> {
399 paritydb_fix_columns(
401 path,
402 paritydb_version_1_config(path),
403 vec![super::columns::v4::COL_DISPUTE_COORDINATOR_DATA],
404 )?;
405
406 Ok(1)
407}
408
409fn paritydb_migrate_from_version_1_to_2(path: &Path) -> Result<Version, Error> {
412 let mut options = paritydb_version_1_config(path);
413
414 parity_db::Db::add_column(&mut options, Default::default())
416 .map_err(|e| other_io_error(format!("Error adding column {:?}", e)))?;
417
418 Ok(2)
419}
420
421fn paritydb_migrate_from_version_2_to_3(path: &Path) -> Result<Version, Error> {
424 parity_db::Db::drop_last_column(&mut paritydb_version_2_config(path))
425 .map_err(|e| other_io_error(format!("Error removing COL_SESSION_WINDOW_DATA {:?}", e)))?;
426 Ok(3)
427}
428
429#[cfg(test)]
431pub fn remove_file_lock(path: &std::path::Path) {
432 use std::{io::ErrorKind, thread::sleep, time::Duration};
433
434 let mut lock_path = std::path::PathBuf::from(path);
435 lock_path.push("lock");
436
437 for _ in 0..10 {
438 let result = std::fs::remove_file(lock_path.as_path());
439 match result {
440 Err(error) => match error.kind() {
441 ErrorKind::WouldBlock => {
442 sleep(Duration::from_millis(100));
443 continue
444 },
445 _ => return,
446 },
447 Ok(_) => {},
448 }
449 }
450
451 unreachable!("Database is locked, waited 1s for lock file: {:?}", lock_path);
452}
453
454#[cfg(test)]
455mod tests {
456 use super::{
457 columns::{v2::COL_SESSION_WINDOW_DATA, v4::*},
458 *,
459 };
460 use kvdb_rocksdb::{Database, DatabaseConfig};
461 use polkadot_node_core_approval_voting::approval_db::{
462 v2::migration_helpers::v1_fill_test_data,
463 v3::migration_helpers::{v1_to_latest_sanity_check, v2_fill_test_data},
464 };
465 use polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter;
466 use polkadot_primitives_test_helpers::dummy_candidate_receipt_v2;
467
468 #[test]
469 fn test_paritydb_migrate_0_to_1() {
470 use parity_db::Db;
471
472 let db_dir = tempfile::tempdir().unwrap();
473 let path = db_dir.path();
474 {
475 let db = Db::open_or_create(&paritydb_version_0_config(&path)).unwrap();
476
477 db.commit(vec![(
478 COL_AVAILABILITY_META as u8,
479 b"5678".to_vec(),
480 Some(b"somevalue".to_vec()),
481 )])
482 .unwrap();
483 }
484
485 try_upgrade_db(&path, DatabaseKind::ParityDB, 1).unwrap();
486
487 let db = Db::open(&paritydb_version_1_config(&path)).unwrap();
488 assert_eq!(
489 db.get(COL_AVAILABILITY_META as u8, b"5678").unwrap(),
490 Some("somevalue".as_bytes().to_vec())
491 );
492 }
493
494 #[test]
495 fn test_paritydb_migrate_1_to_2() {
496 use parity_db::Db;
497
498 let db_dir = tempfile::tempdir().unwrap();
499 let path = db_dir.path();
500
501 fs::write(version_file_path(path), "1").expect("Failed to write DB version");
503
504 {
505 let db = Db::open_or_create(&paritydb_version_1_config(&path)).unwrap();
506
507 db.commit(vec![(
509 COL_DISPUTE_COORDINATOR_DATA as u8,
510 b"1234".to_vec(),
511 Some(b"somevalue".to_vec()),
512 )])
513 .unwrap();
514
515 assert_eq!(db.num_columns(), columns::v1::NUM_COLUMNS as u8);
516 }
517
518 try_upgrade_db(&path, DatabaseKind::ParityDB, 2).unwrap();
519
520 let db = Db::open(&paritydb_version_2_config(&path)).unwrap();
521
522 assert_eq!(db.num_columns(), columns::v2::NUM_COLUMNS as u8);
523
524 assert_eq!(
525 db.get(COL_DISPUTE_COORDINATOR_DATA as u8, b"1234").unwrap(),
526 Some("somevalue".as_bytes().to_vec())
527 );
528
529 db.commit(vec![(
531 COL_SESSION_WINDOW_DATA as u8,
532 b"1337".to_vec(),
533 Some(b"0xdeadb00b".to_vec()),
534 )])
535 .unwrap();
536
537 assert_eq!(
539 db.get(COL_SESSION_WINDOW_DATA as u8, b"1337").unwrap(),
540 Some("0xdeadb00b".as_bytes().to_vec())
541 );
542 }
543
544 #[test]
545 fn test_rocksdb_migrate_1_to_2() {
546 use kvdb::{DBKey, DBOp};
547 use kvdb_rocksdb::{Database, DatabaseConfig};
548 use polkadot_node_subsystem_util::database::{
549 kvdb_impl::DbAdapter, DBTransaction, KeyValueDB,
550 };
551
552 let db_dir = tempfile::tempdir().unwrap();
553 let db_path = db_dir.path().to_str().unwrap();
554 let db_cfg = DatabaseConfig::with_columns(super::columns::v1::NUM_COLUMNS);
555 let db = Database::open(&db_cfg, db_path).unwrap();
556 assert_eq!(db.num_columns(), super::columns::v1::NUM_COLUMNS as u32);
557
558 fs::write(version_file_path(db_dir.path()), "1").expect("Failed to write DB version");
560 {
561 let db = DbAdapter::new(db, columns::v4::ORDERED_COL);
562 db.write(DBTransaction {
563 ops: vec![DBOp::Insert {
564 col: COL_DISPUTE_COORDINATOR_DATA,
565 key: DBKey::from_slice(b"1234"),
566 value: b"0xdeadb00b".to_vec(),
567 }],
568 })
569 .unwrap();
570 }
571
572 try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB, 2).unwrap();
573
574 let db_cfg = DatabaseConfig::with_columns(super::columns::v2::NUM_COLUMNS);
575 let db = Database::open(&db_cfg, db_path).unwrap();
576
577 assert_eq!(db.num_columns(), super::columns::v2::NUM_COLUMNS);
578
579 let db = DbAdapter::new(db, columns::v4::ORDERED_COL);
580
581 assert_eq!(
582 db.get(COL_DISPUTE_COORDINATOR_DATA, b"1234").unwrap(),
583 Some("0xdeadb00b".as_bytes().to_vec())
584 );
585
586 db.write(DBTransaction {
588 ops: vec![DBOp::Insert {
589 col: COL_SESSION_WINDOW_DATA,
590 key: DBKey::from_slice(b"1337"),
591 value: b"0xdeadb00b".to_vec(),
592 }],
593 })
594 .unwrap();
595
596 assert_eq!(
598 db.get(COL_SESSION_WINDOW_DATA, b"1337").unwrap(),
599 Some("0xdeadb00b".as_bytes().to_vec())
600 );
601 }
602
603 #[test]
604 fn test_migrate_3_to_5() {
605 let db_dir = tempfile::tempdir().unwrap();
606 let db_path = db_dir.path().to_str().unwrap();
607 let db_cfg: DatabaseConfig = DatabaseConfig::with_columns(super::columns::v3::NUM_COLUMNS);
608
609 let approval_cfg = ApprovalDbConfig {
610 col_approval_data: crate::parachains_db::REAL_COLUMNS.col_approval_data,
611 };
612
613 fs::write(version_file_path(db_dir.path()), "3").expect("Failed to write DB version");
615 let expected_candidates = {
616 let db = Database::open(&db_cfg, db_path).unwrap();
617 assert_eq!(db.num_columns(), super::columns::v3::NUM_COLUMNS as u32);
618 let db = DbAdapter::new(db, columns::v3::ORDERED_COL);
619 v1_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt_v2)
621 .unwrap()
622 };
623
624 try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB, 5).unwrap();
625
626 let db_cfg = DatabaseConfig::with_columns(super::columns::v4::NUM_COLUMNS);
627 let db = Database::open(&db_cfg, db_path).unwrap();
628 let db = DbAdapter::new(db, columns::v4::ORDERED_COL);
629
630 v1_to_latest_sanity_check(std::sync::Arc::new(db), approval_cfg, expected_candidates)
631 .unwrap();
632 }
633
634 #[test]
635 fn test_migrate_4_to_5() {
636 let db_dir = tempfile::tempdir().unwrap();
637 let db_path = db_dir.path().to_str().unwrap();
638 let db_cfg: DatabaseConfig = DatabaseConfig::with_columns(super::columns::v3::NUM_COLUMNS);
639
640 let approval_cfg = ApprovalDbConfig {
641 col_approval_data: crate::parachains_db::REAL_COLUMNS.col_approval_data,
642 };
643
644 fs::write(version_file_path(db_dir.path()), "4").expect("Failed to write DB version");
646 let expected_candidates = {
647 let db = Database::open(&db_cfg, db_path).unwrap();
648 assert_eq!(db.num_columns(), super::columns::v3::NUM_COLUMNS as u32);
649 let db = DbAdapter::new(db, columns::v3::ORDERED_COL);
650 v2_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt_v2)
652 .unwrap()
653 };
654
655 try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB, 5).unwrap();
656
657 let db_cfg = DatabaseConfig::with_columns(super::columns::v4::NUM_COLUMNS);
658 let db = Database::open(&db_cfg, db_path).unwrap();
659 let db = DbAdapter::new(db, columns::v4::ORDERED_COL);
660
661 v1_to_latest_sanity_check(std::sync::Arc::new(db), approval_cfg, expected_candidates)
662 .unwrap();
663 }
664
665 #[test]
666 fn test_rocksdb_migrate_0_to_5() {
667 use kvdb_rocksdb::{Database, DatabaseConfig};
668
669 let db_dir = tempfile::tempdir().unwrap();
670 let db_path = db_dir.path().to_str().unwrap();
671
672 fs::write(version_file_path(db_dir.path()), "0").expect("Failed to write DB version");
673 try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB, 5).unwrap();
674
675 let db_cfg = DatabaseConfig::with_columns(super::columns::v4::NUM_COLUMNS);
676 let db = Database::open(&db_cfg, db_path).unwrap();
677
678 assert_eq!(db.num_columns(), columns::v4::NUM_COLUMNS);
679 }
680
681 #[test]
682 fn test_paritydb_migrate_0_to_5() {
683 use parity_db::Db;
684
685 let db_dir = tempfile::tempdir().unwrap();
686 let path = db_dir.path();
687
688 fs::write(version_file_path(path), "0").expect("Failed to write DB version");
690
691 {
692 let db = Db::open_or_create(&paritydb_version_0_config(&path)).unwrap();
693 assert_eq!(db.num_columns(), columns::v0::NUM_COLUMNS as u8);
694 }
695
696 try_upgrade_db(&path, DatabaseKind::ParityDB, 5).unwrap();
697
698 let db = Db::open(&paritydb_version_3_config(&path)).unwrap();
699 assert_eq!(db.num_columns(), columns::v4::NUM_COLUMNS as u8);
700 }
701
702 #[test]
703 fn test_paritydb_migrate_2_to_3() {
704 use parity_db::Db;
705
706 let db_dir = tempfile::tempdir().unwrap();
707 let path = db_dir.path();
708 let test_key = b"1337";
709
710 fs::write(version_file_path(path), "2").expect("Failed to write DB version");
712
713 {
714 let db = Db::open_or_create(&paritydb_version_2_config(&path)).unwrap();
715
716 db.commit(vec![(
718 COL_SESSION_WINDOW_DATA as u8,
719 test_key.to_vec(),
720 Some(b"0xdeadb00b".to_vec()),
721 )])
722 .unwrap();
723
724 assert_eq!(db.num_columns(), columns::v2::NUM_COLUMNS as u8);
725 }
726
727 try_upgrade_db(&path, DatabaseKind::ParityDB, 3).unwrap();
728
729 let db = Db::open(&paritydb_version_3_config(&path)).unwrap();
730
731 assert_eq!(db.num_columns(), columns::v3::NUM_COLUMNS as u8);
732 }
733
734 #[test]
735 fn test_rocksdb_migrate_2_to_3() {
736 use kvdb_rocksdb::{Database, DatabaseConfig};
737
738 let db_dir = tempfile::tempdir().unwrap();
739 let db_path = db_dir.path().to_str().unwrap();
740 let db_cfg = DatabaseConfig::with_columns(super::columns::v2::NUM_COLUMNS);
741
742 {
743 let db = Database::open(&db_cfg, db_path).unwrap();
744 assert_eq!(db.num_columns(), super::columns::v2::NUM_COLUMNS as u32);
745 }
746
747 fs::write(version_file_path(db_dir.path()), "2").expect("Failed to write DB version");
749
750 try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB, 3).unwrap();
751
752 let db_cfg = DatabaseConfig::with_columns(super::columns::v3::NUM_COLUMNS);
753 let db = Database::open(&db_cfg, db_path).unwrap();
754
755 assert_eq!(db.num_columns(), super::columns::v3::NUM_COLUMNS);
756 }
757}