1use crate::error::Error;
2use chrono::Utc;
3use rusqlite::{params, Connection, Transaction};
4use sha2::{Digest, Sha256};
5use std::time::Instant;
6
7#[derive(Debug, PartialEq)]
9pub struct MigrationFailure<'migration> {
10 migration: &'migration Box<dyn Migration>,
11 error: Error,
12}
13
14impl<'migration> MigrationFailure<'migration> {
15 pub fn migration(&self) -> &dyn Migration {
17 self.migration.as_ref()
18 }
19
20 pub fn error(&self) -> &Error {
22 &self.error
23 }
24}
25
26#[derive(Debug, PartialEq)]
28pub struct MigrationReport<'migration> {
29 pub schema_version_table_existed: bool,
30 pub schema_version_table_created: bool,
31 pub migrations_run: Vec<u32>,
32 pub failing_migration: Option<MigrationFailure<'migration>>,
33}
34
35#[derive(Debug, Clone, PartialEq)]
37pub enum Precondition {
38 AlreadySatisfied,
40 NeedsApply,
42}
43
44#[derive(Debug, Clone, PartialEq)]
46pub struct AppliedMigration {
47 pub version: u32,
49 pub name: String,
51 pub applied_at: chrono::DateTime<Utc>,
53 pub checksum: String,
55}
56
57const DEFAULT_VERSION_TABLE_NAME: &str = "_migratio_version_";
58
59pub trait Migration {
66 fn version(&self) -> u32;
81
82 fn up(&self, tx: &Transaction) -> Result<(), Error>;
83
84 fn down(&self, _tx: &Transaction) -> Result<(), Error> {
87 panic!(
88 "Migration {} ('{}') does not support downgrade. Implement the down() method to enable rollback.",
89 self.version(),
90 self.name()
91 )
92 }
93
94 fn name(&self) -> String {
105 format!("Migration {}", self.version())
106 }
107
108 fn description(&self) -> Option<&'static str> {
113 None
114 }
115
116 fn precondition(&self, _tx: &Transaction) -> Result<Precondition, Error> {
159 Ok(Precondition::NeedsApply)
160 }
161}
162
163impl PartialEq for dyn Migration {
164 fn eq(&self, other: &Self) -> bool {
165 self.version() == other.version()
166 }
167}
168
169impl std::fmt::Debug for dyn Migration {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 f.debug_struct("Migration")
172 .field("version", &self.version())
173 .field("name", &self.name())
174 .finish()
175 }
176}
177
178pub struct SqliteMigrator {
182 migrations: Vec<Box<dyn Migration>>,
183 schema_version_table_name: String,
184 busy_timeout: std::time::Duration,
185 on_migration_start: Option<Box<dyn Fn(u32, &str) + Send + Sync>>,
186 on_migration_complete: Option<Box<dyn Fn(u32, &str, std::time::Duration) + Send + Sync>>,
187 on_migration_skipped: Option<Box<dyn Fn(u32, &str) + Send + Sync>>,
188 on_migration_error: Option<Box<dyn Fn(u32, &str, &Error) + Send + Sync>>,
189}
190
191impl std::fmt::Debug for SqliteMigrator {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 f.debug_struct("SqliteMigrator")
195 .field("migrations", &self.migrations)
196 .field("schema_version_table_name", &self.schema_version_table_name)
197 .field("busy_timeout", &self.busy_timeout)
198 .field("on_migration_start", &self.on_migration_start.is_some())
199 .field(
200 "on_migration_complete",
201 &self.on_migration_complete.is_some(),
202 )
203 .field("on_migration_skipped", &self.on_migration_skipped.is_some())
204 .field("on_migration_error", &self.on_migration_error.is_some())
205 .finish()
206 }
207}
208
209impl SqliteMigrator {
210 fn setup_concurrency_protection(&self, conn: &Connection) -> Result<(), Error> {
213 conn.busy_timeout(self.busy_timeout)?;
216 Ok(())
217 }
218
219 fn calculate_checksum(migration: &Box<dyn Migration>) -> String {
222 let mut hasher = Sha256::new();
223 hasher.update(migration.version().to_string().as_bytes());
224 hasher.update(b"|");
225 hasher.update(migration.name().as_bytes());
226 format!("{:x}", hasher.finalize())
227 }
228
229 pub fn try_new(migrations: Vec<Box<dyn Migration>>) -> Result<Self, String> {
232 let mut versions: Vec<u32> = migrations.iter().map(|m| m.version()).collect();
234 versions.sort();
235
236 for (i, &version) in versions.iter().enumerate() {
238 if version == 0 {
240 return Err("Migration version must be greater than 0, found version 0".to_string());
241 }
242
243 if i > 0 && versions[i - 1] == version {
245 return Err(format!("Duplicate migration version found: {}", version));
246 }
247 }
248
249 if !versions.is_empty() {
251 if versions[0] != 1 {
252 return Err(format!(
253 "Migration versions must start at 1, found starting version: {}",
254 versions[0]
255 ));
256 }
257
258 for (i, &version) in versions.iter().enumerate() {
259 let expected = (i + 1) as u32;
260 if version != expected {
261 return Err(format!(
262 "Migration versions must be contiguous. Expected version {}, found {}",
263 expected, version
264 ));
265 }
266 }
267 }
268
269 Ok(Self {
270 migrations,
271 schema_version_table_name: DEFAULT_VERSION_TABLE_NAME.to_string(),
272 busy_timeout: std::time::Duration::from_secs(30),
273 on_migration_start: None,
274 on_migration_complete: None,
275 on_migration_skipped: None,
276 on_migration_error: None,
277 })
278 }
279
280 pub fn new(migrations: Vec<Box<dyn Migration>>) -> Self {
283 match Self::try_new(migrations) {
284 Ok(migrator) => migrator,
285 Err(err) => panic!("{}", err),
286 }
287 }
288
289 pub fn with_schema_version_table_name(mut self, name: impl Into<String>) -> Self {
292 self.schema_version_table_name = name.into();
293 self
294 }
295
296 pub fn with_busy_timeout(mut self, timeout: std::time::Duration) -> Self {
300 self.busy_timeout = timeout;
301 self
302 }
303
304 pub fn on_migration_start<F>(mut self, callback: F) -> Self
325 where
326 F: Fn(u32, &str) + Send + Sync + 'static,
327 {
328 self.on_migration_start = Some(Box::new(callback));
329 self
330 }
331
332 pub fn on_migration_complete<F>(mut self, callback: F) -> Self
353 where
354 F: Fn(u32, &str, std::time::Duration) + Send + Sync + 'static,
355 {
356 self.on_migration_complete = Some(Box::new(callback));
357 self
358 }
359
360 pub fn on_migration_skipped<F>(mut self, callback: F) -> Self
382 where
383 F: Fn(u32, &str) + Send + Sync + 'static,
384 {
385 self.on_migration_skipped = Some(Box::new(callback));
386 self
387 }
388
389 pub fn on_migration_error<F>(mut self, callback: F) -> Self
410 where
411 F: Fn(u32, &str, &Error) + Send + Sync + 'static,
412 {
413 self.on_migration_error = Some(Box::new(callback));
414 self
415 }
416
417 pub fn migrations(&self) -> &[Box<dyn Migration>] {
419 &self.migrations
420 }
421
422 pub fn get_current_version(&self, conn: &mut Connection) -> Result<u32, Error> {
425 let mut stmt =
427 conn.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?1")?;
428 let table_exists = stmt
429 .query([&self.schema_version_table_name])?
430 .next()?
431 .is_some();
432
433 if !table_exists {
434 return Ok(0);
435 }
436
437 let mut stmt = conn.prepare(&format!(
439 "SELECT MAX(version) from {}",
440 self.schema_version_table_name
441 ))?;
442 let version: Option<u32> = stmt.query_row([], |row| row.get(0))?;
443 Ok(version.unwrap_or(0))
444 }
445
446 pub fn get_migration_history(
450 &self,
451 conn: &mut Connection,
452 ) -> Result<Vec<AppliedMigration>, Error> {
453 let mut stmt =
455 conn.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?1")?;
456 let table_exists = stmt
457 .query([&self.schema_version_table_name])?
458 .next()?
459 .is_some();
460
461 if !table_exists {
462 return Ok(vec![]);
463 }
464
465 let mut stmt = conn.prepare(&format!(
467 "SELECT version, name, applied_at, checksum FROM {} ORDER BY version",
468 self.schema_version_table_name
469 ))?;
470
471 let migrations = stmt
472 .query_map([], |row| {
473 let applied_at_str: String = row.get(2)?;
474 let applied_at = chrono::DateTime::parse_from_rfc3339(&applied_at_str)
475 .map_err(|e| {
476 rusqlite::Error::FromSqlConversionFailure(
477 2,
478 rusqlite::types::Type::Text,
479 Box::new(e),
480 )
481 })?
482 .with_timezone(&Utc);
483
484 Ok(AppliedMigration {
485 version: row.get(0)?,
486 name: row.get(1)?,
487 applied_at,
488 checksum: row.get(3)?,
489 })
490 })?
491 .collect::<Result<Vec<_>, _>>()?;
492
493 Ok(migrations)
494 }
495
496 pub fn preview_upgrade(
499 &self,
500 conn: &mut Connection,
501 ) -> Result<Vec<&Box<dyn Migration>>, Error> {
502 let current_version = self.get_current_version(conn)?;
503
504 let mut pending_migrations = self
505 .migrations
506 .iter()
507 .filter(|m| m.version() > current_version)
508 .collect::<Vec<_>>();
509 pending_migrations.sort_by_key(|m| m.version());
510
511 Ok(pending_migrations)
512 }
513
514 pub fn preview_downgrade(
517 &self,
518 conn: &mut Connection,
519 target_version: u32,
520 ) -> Result<Vec<&Box<dyn Migration>>, Error> {
521 let current_version = self.get_current_version(conn)?;
522
523 if target_version > current_version {
525 return Err(Error::Rusqlite(rusqlite::Error::InvalidParameterName(
526 format!(
527 "Cannot downgrade to version {} when current version is {}. Target must be <= current version.",
528 target_version, current_version
529 ),
530 )));
531 }
532
533 let mut migrations_to_rollback = self
534 .migrations
535 .iter()
536 .filter(|m| m.version() > target_version && m.version() <= current_version)
537 .collect::<Vec<_>>();
538 migrations_to_rollback.sort_by_key(|m| std::cmp::Reverse(m.version())); Ok(migrations_to_rollback)
541 }
542
543 pub fn upgrade_to(
551 &self,
552 conn: &mut Connection,
553 target_version: u32,
554 ) -> Result<MigrationReport<'_>, Error> {
555 if target_version > 0
557 && !self
558 .migrations
559 .iter()
560 .any(|m| m.version() == target_version)
561 {
562 return Err(Error::Rusqlite(rusqlite::Error::InvalidParameterName(
563 format!(
564 "Target version {} does not exist in migration list",
565 target_version
566 ),
567 )));
568 }
569
570 self.upgrade_internal(conn, Some(target_version))
571 }
572
573 pub fn upgrade(&self, conn: &mut Connection) -> Result<MigrationReport<'_>, Error> {
575 self.upgrade_internal(conn, None)
576 }
577
578 fn upgrade_internal(
579 &self,
580 conn: &mut Connection,
581 target_version: Option<u32>,
582 ) -> Result<MigrationReport<'_>, Error> {
583 self.setup_concurrency_protection(conn)?;
585 let schema_version_table_existed = {
587 let mut stmt =
588 conn.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?1")?;
589 let schema_version_table_existed = stmt
590 .query([&self.schema_version_table_name])?
591 .next()?
592 .is_some();
593 if !schema_version_table_existed {
594 conn.execute(
597 &format!(
598 "CREATE TABLE IF NOT EXISTS {} (version integer primary key not null, name text not null, applied_at text not null, checksum text not null)",
599 self.schema_version_table_name
600 ),
601 [],)?;
602 }
603 schema_version_table_existed
604 };
605
606 if schema_version_table_existed {
608 let has_checksum_column = {
610 let mut stmt = conn.prepare(&format!(
611 "PRAGMA table_info({})",
612 self.schema_version_table_name
613 ))?;
614 let columns: Vec<String> = stmt
615 .query_map([], |row| row.get::<_, String>(1))?
616 .collect::<Result<Vec<_>, _>>()?;
617 columns.contains(&"checksum".to_string())
618 };
619
620 if has_checksum_column {
621 let mut stmt = conn.prepare(&format!(
623 "SELECT version, name, checksum FROM {}",
624 self.schema_version_table_name
625 ))?;
626 let applied_migrations: Vec<(u32, String, String)> = stmt
627 .query_map([], |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)))?
628 .collect::<Result<Vec<_>, _>>()?;
629
630 for (applied_version, applied_name, applied_checksum) in &applied_migrations {
633 if let Some(migration) = self
634 .migrations
635 .iter()
636 .find(|m| m.version() == *applied_version)
637 {
638 let current_checksum = Self::calculate_checksum(migration);
639 if current_checksum != *applied_checksum {
640 return Err(Error::Rusqlite(rusqlite::Error::InvalidParameterName(
641 format!(
642 "Migration {} checksum mismatch. Expected '{}' but found '{}'. \
643 Migration name in DB: '{}', current name: '{}'. \
644 This indicates the migration was modified after being applied.",
645 applied_version,
646 applied_checksum,
647 current_checksum,
648 applied_name,
649 migration.name()
650 ),
651 )));
652 }
653 } else {
654 return Err(Error::Rusqlite(rusqlite::Error::InvalidParameterName(
656 format!(
657 "Migration {} ('{}') was previously applied but is no longer present in the migration list. \
658 Applied migrations cannot be removed from the codebase.",
659 applied_version,
660 applied_name
661 ),
662 )));
663 }
664 }
665
666 let applied_versions: Vec<u32> =
670 applied_migrations.iter().map(|(v, _, _)| *v).collect();
671 if !applied_versions.is_empty() {
672 let max_applied = *applied_versions.iter().max().unwrap();
673
674 for expected_version in 1..=max_applied {
676 if !applied_versions.contains(&expected_version) {
677 if let Some(missing_migration) = self
679 .migrations
680 .iter()
681 .find(|m| m.version() == expected_version)
682 {
683 return Err(Error::Rusqlite(rusqlite::Error::InvalidParameterName(
684 format!(
685 "Migration {} ('{}') exists in code but was not applied, yet later migrations are already applied. \
686 This likely means migration {} was added after migration {} was already applied. \
687 Applied migrations: {:?}",
688 expected_version,
689 missing_migration.name(),
690 expected_version,
691 max_applied,
692 applied_versions
693 ),
694 )));
695 }
696 }
697 }
698 }
699 }
700 }
701
702 let current_version: u32 = {
704 let mut stmt = conn.prepare(&format!(
705 "SELECT MAX(version) from {}",
706 self.schema_version_table_name
707 ))?;
708 let version: Option<u32> = stmt.query_row([], |row| row.get(0))?;
709 version.unwrap_or(0)
710 };
711 let mut migrations_run: Vec<u32> = Vec::new();
713 let mut migrations_sorted = self
714 .migrations
715 .iter()
716 .map(|x| (x.version(), x))
717 .collect::<Vec<_>>();
718 migrations_sorted.sort_by_key(|m| m.0);
719 let mut failing_migration: Option<MigrationFailure> = None;
720 let mut schema_version_table_created = false;
721 let batch_applied_at = Utc::now().to_rfc3339();
723
724 #[cfg(feature = "tracing")]
725 tracing::debug!(
726 current_version = current_version,
727 target_version = ?target_version,
728 available_migrations = ?migrations_sorted.iter().map(|(v, m)| (*v, m.name())).collect::<Vec<_>>(),
729 "Considering migrations to run"
730 );
731
732 for (migration_version, migration) in migrations_sorted {
733 if let Some(target) = target_version {
735 if migration_version > target {
736 #[cfg(feature = "tracing")]
737 tracing::debug!(
738 migration_version = migration_version,
739 target_version = target,
740 "Skipping migration (beyond target version)"
741 );
742 break;
743 }
744 }
745
746 if current_version < migration_version {
747 #[cfg(feature = "tracing")]
748 tracing::debug!(
749 migration_version = migration_version,
750 migration_name = %migration.name(),
751 "Migration needs to be applied"
752 );
753 #[cfg(feature = "tracing")]
754 let _span = tracing::info_span!(
755 "migration_up",
756 version = migration_version,
757 name = %migration.name()
758 )
759 .entered();
760
761 #[cfg(feature = "tracing")]
762 tracing::info!("Starting migration");
763
764 if let Some(ref callback) = self.on_migration_start {
766 callback(migration_version, &migration.name());
767 }
768
769 let migration_start = Instant::now();
770
771 let migration_result = {
774 let tx = conn.transaction()?;
776
777 let precondition = match migration.precondition(&tx) {
779 Ok(p) => p,
780 Err(error) => {
781 #[cfg(feature = "tracing")]
782 tracing::error!(
783 error = %error,
784 "Precondition check failed"
785 );
786
787 if let Some(ref callback) = self.on_migration_error {
789 callback(migration_version, &migration.name(), &error);
790 }
791
792 failing_migration = Some(MigrationFailure { migration, error });
794 break;
795 }
796 };
797
798 match precondition {
799 Precondition::AlreadySatisfied => {
800 #[cfg(feature = "tracing")]
801 tracing::info!("Precondition already satisfied, stamping migration without running up()");
802
803 if let Some(ref callback) = self.on_migration_skipped {
805 callback(migration_version, &migration.name());
806 }
807
808 tx.commit()?;
810 Ok(())
811 }
812 Precondition::NeedsApply => {
813 let result = migration.up(&tx);
814 if result.is_ok() {
815 tx.commit()?;
817 }
818 result
819 }
820 }
821 };
822
823 match migration_result {
824 Ok(_) => {
825 let migration_duration = migration_start.elapsed();
826
827 #[cfg(feature = "tracing")]
828 tracing::info!(
829 duration_ms = migration_duration.as_millis(),
830 "Migration completed successfully"
831 );
832
833 let checksum = Self::calculate_checksum(migration);
835
836 conn.execute(
838 &format!(
839 "INSERT INTO {} (version, name, applied_at, checksum) VALUES(?1, ?2, ?3, ?4)",
840 self.schema_version_table_name
841 ),
842 params![migration_version, migration.name(), &batch_applied_at, checksum],
843 )?;
844
845 migrations_run.push(migration_version);
847 schema_version_table_created = true;
850
851 if let Some(ref callback) = self.on_migration_complete {
853 callback(migration_version, &migration.name(), migration_duration);
854 }
855 }
856 Err(e) => {
857 #[cfg(feature = "tracing")]
858 tracing::error!(
859 error = %e,
860 "Migration failed"
861 );
862
863 if let Some(ref callback) = self.on_migration_error {
865 callback(migration_version, &migration.name(), &e);
866 }
867
868 failing_migration = Some(MigrationFailure {
870 migration,
871 error: e,
872 });
873 break;
874 }
875 }
876 } else {
877 #[cfg(feature = "tracing")]
878 tracing::debug!(
879 migration_version = migration_version,
880 current_version = current_version,
881 "Skipping migration (already applied)"
882 );
883 }
884 }
885 Ok(MigrationReport {
887 schema_version_table_existed,
888 schema_version_table_created,
889 failing_migration,
890 migrations_run,
891 })
892 }
893
894 pub fn downgrade(
900 &self,
901 conn: &mut Connection,
902 target_version: u32,
903 ) -> Result<MigrationReport<'_>, Error> {
904 self.setup_concurrency_protection(conn)?;
906 let schema_version_table_existed = {
908 let mut stmt =
909 conn.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?1")?;
910 let exists = stmt
911 .query([&self.schema_version_table_name])?
912 .next()?
913 .is_some();
914 exists
915 };
916
917 if !schema_version_table_existed {
918 return Ok(MigrationReport {
920 schema_version_table_existed: false,
921 schema_version_table_created: false,
922 failing_migration: None,
923 migrations_run: vec![],
924 });
925 }
926
927 let has_checksum_column = {
929 let mut stmt = conn.prepare(&format!(
930 "PRAGMA table_info({})",
931 self.schema_version_table_name
932 ))?;
933 let columns: Vec<String> = stmt
934 .query_map([], |row| row.get::<_, String>(1))?
935 .collect::<Result<Vec<_>, _>>()?;
936 columns.contains(&"checksum".to_string())
937 };
938
939 if has_checksum_column {
940 let mut stmt = conn.prepare(&format!(
941 "SELECT version, name, checksum FROM {}",
942 self.schema_version_table_name
943 ))?;
944 let applied_migrations: Vec<(u32, String, String)> = stmt
945 .query_map([], |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)))?
946 .collect::<Result<Vec<_>, _>>()?;
947
948 for (applied_version, applied_name, applied_checksum) in &applied_migrations {
950 if let Some(migration) = self
951 .migrations
952 .iter()
953 .find(|m| m.version() == *applied_version)
954 {
955 let current_checksum = Self::calculate_checksum(migration);
956 if current_checksum != *applied_checksum {
957 return Err(Error::Rusqlite(rusqlite::Error::InvalidParameterName(
958 format!(
959 "Migration {} checksum mismatch. Expected '{}' but found '{}'. \
960 Migration name in DB: '{}', current name: '{}'. \
961 This indicates the migration was modified after being applied.",
962 applied_version,
963 applied_checksum,
964 current_checksum,
965 applied_name,
966 migration.name()
967 ),
968 )));
969 }
970 } else {
971 return Err(Error::Rusqlite(rusqlite::Error::InvalidParameterName(
973 format!(
974 "Migration {} ('{}') was previously applied but is no longer present in the migration list. \
975 Applied migrations cannot be removed from the codebase.",
976 applied_version,
977 applied_name
978 ),
979 )));
980 }
981 }
982
983 let applied_versions: Vec<u32> =
985 applied_migrations.iter().map(|(v, _, _)| *v).collect();
986 if !applied_versions.is_empty() {
987 let max_applied = *applied_versions.iter().max().unwrap();
988
989 for expected_version in 1..=max_applied {
990 if !applied_versions.contains(&expected_version) {
991 if let Some(missing_migration) = self
992 .migrations
993 .iter()
994 .find(|m| m.version() == expected_version)
995 {
996 return Err(Error::Rusqlite(rusqlite::Error::InvalidParameterName(
997 format!(
998 "Migration {} ('{}') exists in code but was not applied, yet later migrations are already applied. \
999 This likely means migration {} was added after migration {} was already applied. \
1000 Applied migrations: {:?}",
1001 expected_version,
1002 missing_migration.name(),
1003 expected_version,
1004 max_applied,
1005 applied_versions
1006 ),
1007 )));
1008 }
1009 }
1010 }
1011 }
1012 }
1013
1014 let current_version: u32 = {
1016 let mut stmt = conn.prepare(&format!(
1017 "SELECT MAX(version) from {}",
1018 self.schema_version_table_name
1019 ))?;
1020 let version: Option<u32> = stmt.query_row([], |row| row.get(0))?;
1021 version.unwrap_or(0)
1022 };
1023
1024 if target_version > current_version {
1026 return Err(Error::Rusqlite(rusqlite::Error::InvalidParameterName(
1027 format!(
1028 "Cannot downgrade to version {} when current version is {}. Target must be <= current version.",
1029 target_version, current_version
1030 ),
1031 )));
1032 }
1033
1034 let mut migrations_to_rollback = self
1036 .migrations
1037 .iter()
1038 .filter(|m| m.version() > target_version && m.version() <= current_version)
1039 .map(|x| (x.version(), x))
1040 .collect::<Vec<_>>();
1041 migrations_to_rollback.sort_by_key(|m| std::cmp::Reverse(m.0)); let mut migrations_run: Vec<u32> = Vec::new();
1044 let mut failing_migration: Option<MigrationFailure> = None;
1045
1046 for (migration_version, migration) in migrations_to_rollback {
1047 #[cfg(feature = "tracing")]
1048 let _span = tracing::info_span!(
1049 "migration_down",
1050 version = migration_version,
1051 name = %migration.name()
1052 )
1053 .entered();
1054
1055 #[cfg(feature = "tracing")]
1056 tracing::info!("Rolling back migration");
1057
1058 if let Some(ref callback) = self.on_migration_start {
1060 callback(migration_version, &migration.name());
1061 }
1062
1063 let migration_start = Instant::now();
1064
1065 let tx = conn.transaction()?;
1067 let migration_result = migration.down(&tx);
1068
1069 match migration_result {
1070 Ok(_) => {
1071 tx.commit()?;
1073
1074 let migration_duration = migration_start.elapsed();
1075
1076 #[cfg(feature = "tracing")]
1077 tracing::info!(
1078 duration_ms = migration_duration.as_millis(),
1079 "Migration rolled back successfully"
1080 );
1081
1082 conn.execute(
1084 &format!(
1085 "DELETE FROM {} WHERE version = ?1",
1086 self.schema_version_table_name
1087 ),
1088 params![migration_version],
1089 )?;
1090
1091 migrations_run.push(migration_version);
1093
1094 if let Some(ref callback) = self.on_migration_complete {
1096 callback(migration_version, &migration.name(), migration_duration);
1097 }
1098 }
1099 Err(e) => {
1100 #[cfg(feature = "tracing")]
1101 tracing::error!(
1102 error = %e,
1103 "Migration rollback failed"
1104 );
1105
1106 if let Some(ref callback) = self.on_migration_error {
1108 callback(migration_version, &migration.name(), &e);
1109 }
1110
1111 failing_migration = Some(MigrationFailure {
1113 migration,
1114 error: e,
1115 });
1116 break;
1117 }
1118 }
1119 }
1120
1121 Ok(MigrationReport {
1122 schema_version_table_existed,
1123 schema_version_table_created: false,
1124 failing_migration,
1125 migrations_run,
1126 })
1127 }
1128}
1129
1130#[cfg(test)]
1131mod tests {
1132 use super::*;
1133
1134 #[test]
1135 fn single_successful_from_clean() {
1136 use chrono::{DateTime, FixedOffset};
1137
1138 let mut conn = Connection::open_in_memory().unwrap();
1139 struct Migration1;
1140 impl Migration for Migration1 {
1141 fn version(&self) -> u32 {
1142 1
1143 }
1144 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1145 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
1146 Ok(())
1147 }
1148 }
1149 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
1150 let report = migrator.upgrade(&mut conn).unwrap();
1151 assert_eq!(
1152 report,
1153 MigrationReport {
1154 schema_version_table_existed: false,
1155 schema_version_table_created: true,
1156 migrations_run: vec![1],
1157 failing_migration: None,
1158 }
1159 );
1160 let mut stmt = conn.prepare("SELECT * FROM _migratio_version_").unwrap();
1162 let rows = stmt
1163 .query_map([], |row| {
1164 let version: u32 = row.get("version").unwrap();
1165 let name: String = row.get("name").unwrap();
1166 let applied_at: String = row.get("applied_at").unwrap();
1167 Ok((version, name, applied_at))
1168 })
1169 .unwrap()
1170 .collect::<Result<Vec<_>, _>>()
1171 .unwrap();
1172 assert_eq!(rows.len(), 1);
1173 assert_eq!(rows[0].0, 1); assert_eq!(rows[0].1, "Migration 1"); let date_string_raw = &rows[0].2;
1176 let date = DateTime::parse_from_rfc3339(&date_string_raw).unwrap();
1177 assert_eq!(date.timezone(), FixedOffset::east_opt(0).unwrap());
1178 let now = Utc::now();
1181 let diff = now.timestamp() - date.timestamp();
1182 assert!(diff < 5);
1183 }
1184
1185 #[test]
1186 fn single_unsuccessful_from_clean() {
1187 let mut conn = Connection::open_in_memory().unwrap();
1188 conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])
1190 .unwrap();
1191 conn.execute("INSERT INTO test (id) VALUES (1)", [])
1192 .unwrap();
1193 conn.execute("INSERT INTO test (id) VALUES (2)", [])
1194 .unwrap();
1195 struct Migration1;
1197 impl Migration for Migration1 {
1198 fn version(&self) -> u32 {
1199 1
1200 }
1201 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1202 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
1204 tx.execute("INSERT INTO test2 (id) SELECT id FROM test", [])?;
1205 tx.execute("DROP TABLE test", [])?;
1206 tx.execute("bleep blorp", [])?;
1208 Ok(())
1209 }
1210 }
1211 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
1213 let report = migrator.upgrade(&mut conn).unwrap();
1214 assert_eq!(
1215 report,
1216 MigrationReport {
1217 schema_version_table_existed: false,
1218 schema_version_table_created: false,
1219 migrations_run: vec![],
1220 failing_migration: Some(MigrationFailure {
1221 migration: &(Box::new(Migration1) as Box<dyn Migration>),
1222 error: Error::Rusqlite(rusqlite::Error::SqliteFailure(
1223 rusqlite::ffi::Error {
1224 code: rusqlite::ffi::ErrorCode::Unknown,
1225 extended_code: 1
1226 },
1227 Some("near \"bleep\": syntax error".to_string())
1228 ))
1229 })
1230 }
1231 );
1232 let mut stmt = conn
1235 .prepare("SELECT name FROM sqlite_master WHERE type='table'")
1236 .unwrap();
1237 let mut tables = stmt
1238 .query_map([], |x| {
1239 let name: String = x.get(0)?;
1240 Ok(name)
1241 })
1242 .unwrap()
1243 .collect::<Result<Vec<String>, rusqlite::Error>>()
1244 .unwrap();
1245 tables.sort();
1246 assert_eq!(
1247 tables,
1248 vec!["_migratio_version_".to_string(), "test".to_string()]
1249 );
1250 let mut stmt = conn.prepare("SELECT * FROM test").unwrap();
1252 let rows = stmt
1253 .query_map([], |x| {
1254 let x: i64 = x.get(0)?;
1255 Ok(x)
1256 })
1257 .unwrap()
1258 .collect::<Result<Vec<i64>, rusqlite::Error>>()
1259 .unwrap();
1260 assert_eq!(rows, vec![1, 2]);
1261 }
1262
1263 #[test]
1264 fn upgrade_to_specific_version() {
1265 let mut conn = Connection::open_in_memory().unwrap();
1266
1267 struct Migration1;
1268 impl Migration for Migration1 {
1269 fn version(&self) -> u32 {
1270 1
1271 }
1272 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1273 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
1274 Ok(())
1275 }
1276 }
1277
1278 struct Migration2;
1279 impl Migration for Migration2 {
1280 fn version(&self) -> u32 {
1281 2
1282 }
1283 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1284 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
1285 Ok(())
1286 }
1287 }
1288
1289 struct Migration3;
1290 impl Migration for Migration3 {
1291 fn version(&self) -> u32 {
1292 3
1293 }
1294 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1295 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
1296 Ok(())
1297 }
1298 }
1299
1300 let migrator = SqliteMigrator::new(vec![
1301 Box::new(Migration1),
1302 Box::new(Migration2),
1303 Box::new(Migration3),
1304 ]);
1305
1306 let report = migrator.upgrade_to(&mut conn, 2).unwrap();
1308 assert_eq!(report.migrations_run, vec![1, 2]);
1309
1310 let version = migrator.get_current_version(&mut conn).unwrap();
1312 assert_eq!(version, 2);
1313
1314 let table_count: i32 = conn
1316 .query_row(
1317 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name IN ('test1', 'test2', 'test3')",
1318 [],
1319 |row| row.get(0),
1320 )
1321 .unwrap();
1322 assert_eq!(table_count, 2); let report = migrator.upgrade_to(&mut conn, 3).unwrap();
1326 assert_eq!(report.migrations_run, vec![3]);
1327
1328 let version = migrator.get_current_version(&mut conn).unwrap();
1329 assert_eq!(version, 3);
1330 }
1331
1332 #[test]
1333 fn upgrade_to_nonexistent_version() {
1334 let mut conn = Connection::open_in_memory().unwrap();
1335
1336 struct Migration1;
1337 impl Migration for Migration1 {
1338 fn version(&self) -> u32 {
1339 1
1340 }
1341 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1342 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
1343 Ok(())
1344 }
1345 }
1346
1347 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
1348
1349 let result = migrator.upgrade_to(&mut conn, 5);
1350 assert!(result.is_err());
1351 let err_msg = format!("{:?}", result.unwrap_err());
1352 assert!(err_msg.contains("Target version 5 does not exist"));
1353 }
1354
1355 #[test]
1356 fn success_then_failure_from_clean() {
1357 let mut conn = Connection::open_in_memory().unwrap();
1358 conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])
1360 .unwrap();
1361 conn.execute("INSERT INTO test (id) VALUES (1)", [])
1362 .unwrap();
1363 conn.execute("INSERT INTO test (id) VALUES (2)", [])
1364 .unwrap();
1365 struct Migration1;
1367 impl Migration for Migration1 {
1368 fn version(&self) -> u32 {
1369 1
1370 }
1371 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1372 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
1374 tx.execute("INSERT INTO test2 (id) SELECT id FROM test", [])?;
1376 tx.execute("DROP TABLE test", [])?;
1378 Ok(())
1379 }
1380 }
1381 struct Migration2;
1383 impl Migration for Migration2 {
1384 fn version(&self) -> u32 {
1385 2
1386 }
1387 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1388 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
1390 tx.execute("INSERT INTO test3 (id) SELECT id FROM test2", [])?;
1391 tx.execute("DROP TABLE test2", [])?;
1392 tx.execute("bleep blorp", [])?;
1394 Ok(())
1395 }
1396 }
1397 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
1399 let report = migrator.upgrade(&mut conn).unwrap();
1400 assert_eq!(
1401 report,
1402 MigrationReport {
1403 schema_version_table_existed: false,
1404 schema_version_table_created: true,
1405 migrations_run: vec![1],
1406 failing_migration: Some(MigrationFailure {
1407 migration: &(Box::new(Migration2) as Box<dyn Migration>),
1408 error: Error::Rusqlite(rusqlite::Error::SqliteFailure(
1409 rusqlite::ffi::Error {
1410 code: rusqlite::ffi::ErrorCode::Unknown,
1411 extended_code: 1
1412 },
1413 Some("near \"bleep\": syntax error".to_string())
1414 ))
1415 })
1416 }
1417 );
1418 let mut stmt = conn
1421 .prepare("SELECT name FROM sqlite_master WHERE type='table'")
1422 .unwrap();
1423 let mut tables = stmt
1424 .query_map([], |x| {
1425 let name: String = x.get(0)?;
1426 Ok(name)
1427 })
1428 .unwrap()
1429 .collect::<Result<Vec<String>, rusqlite::Error>>()
1430 .unwrap();
1431 tables.sort();
1432 assert_eq!(
1433 tables,
1434 vec!["_migratio_version_".to_string(), "test2".to_string()]
1435 );
1436 let mut stmt = conn.prepare("SELECT * FROM test2").unwrap();
1438 let rows = stmt
1439 .query_map([], |x| {
1440 let x: i64 = x.get(0)?;
1441 Ok(x)
1442 })
1443 .unwrap()
1444 .collect::<Result<Vec<i64>, rusqlite::Error>>()
1445 .unwrap();
1446 assert_eq!(rows, vec![1, 2]);
1447 }
1448
1449 #[test]
1450 fn panic_in_migration_verify_state() {
1451 let mut conn = Connection::open_in_memory().unwrap();
1452 conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])
1454 .unwrap();
1455 conn.execute("INSERT INTO test (id) VALUES (1)", [])
1456 .unwrap();
1457 conn.execute("INSERT INTO test (id) VALUES (2)", [])
1458 .unwrap();
1459 struct Migration1;
1461 impl Migration for Migration1 {
1462 fn version(&self) -> u32 {
1463 1
1464 }
1465 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1466 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
1468 tx.execute("INSERT INTO test2 (id) SELECT id FROM test", [])?;
1469 tx.execute("DROP TABLE test", [])?;
1470 Ok(())
1471 }
1472 }
1473 struct Migration2;
1475 impl Migration for Migration2 {
1476 fn version(&self) -> u32 {
1477 2
1478 }
1479 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1480 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
1482 tx.execute("INSERT INTO test3 (id) SELECT id FROM test2", [])?;
1483 tx.execute("DROP TABLE test2", [])?;
1484 tx.execute("bleep blorp", [])?;
1486 Ok(())
1487 }
1488 }
1489 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
1491 let report = migrator.upgrade(&mut conn).unwrap();
1492 assert_eq!(
1493 report,
1494 MigrationReport {
1495 schema_version_table_existed: false,
1496 schema_version_table_created: true,
1497 migrations_run: vec![1],
1498 failing_migration: Some(MigrationFailure {
1499 migration: &(Box::new(Migration2) as Box<dyn Migration>),
1500 error: Error::Rusqlite(rusqlite::Error::SqliteFailure(
1501 rusqlite::ffi::Error {
1502 code: rusqlite::ffi::ErrorCode::Unknown,
1503 extended_code: 1
1504 },
1505 Some("near \"bleep\": syntax error".to_string())
1506 ))
1507 })
1508 }
1509 );
1510 let mut stmt = conn
1515 .prepare("SELECT name FROM sqlite_master WHERE type='table'")
1516 .unwrap();
1517 let mut tables = stmt
1518 .query_map([], |x| {
1519 let name: String = x.get(0)?;
1520 Ok(name)
1521 })
1522 .unwrap()
1523 .collect::<Result<Vec<String>, rusqlite::Error>>()
1524 .unwrap();
1525 tables.sort();
1526 assert_eq!(
1527 tables,
1528 vec!["_migratio_version_".to_string(), "test2".to_string()]
1529 );
1530 let mut stmt = conn.prepare("SELECT * FROM test2").unwrap();
1532 let rows = stmt
1533 .query_map([], |x| {
1534 let x: i64 = x.get(0)?;
1535 Ok(x)
1536 })
1537 .unwrap()
1538 .collect::<Result<Vec<i64>, rusqlite::Error>>()
1539 .unwrap();
1540 assert_eq!(rows, vec![1, 2]);
1541 }
1542
1543 #[test]
1544 fn panic_with_successful_prior_migration() {
1545 use std::panic;
1546
1547 let mut conn = Connection::open_in_memory().unwrap();
1548 conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])
1550 .unwrap();
1551 conn.execute("INSERT INTO test (id) VALUES (1)", [])
1552 .unwrap();
1553 conn.execute("INSERT INTO test (id) VALUES (2)", [])
1554 .unwrap();
1555
1556 struct Migration1;
1558 impl Migration for Migration1 {
1559 fn version(&self) -> u32 {
1560 1
1561 }
1562 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1563 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
1565 tx.execute("INSERT INTO test2 (id) SELECT id FROM test", [])?;
1566 tx.execute("DROP TABLE test", [])?;
1567 Ok(())
1568 }
1569 }
1570
1571 struct Migration2;
1573 impl Migration for Migration2 {
1574 fn version(&self) -> u32 {
1575 2
1576 }
1577 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1578 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
1580 tx.execute("INSERT INTO test3 (id) SELECT id FROM test2", [])?;
1581 tx.execute("DROP TABLE test2", [])?;
1582 panic!("Migration panic!");
1584 }
1585 }
1586
1587 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
1589 let result = panic::catch_unwind(panic::AssertUnwindSafe(|| migrator.upgrade(&mut conn)));
1590
1591 assert!(result.is_err());
1593
1594 let mut stmt = conn
1599 .prepare("SELECT name FROM sqlite_master WHERE type='table'")
1600 .unwrap();
1601 let mut tables = stmt
1602 .query_map([], |x| {
1603 let name: String = x.get(0)?;
1604 Ok(name)
1605 })
1606 .unwrap()
1607 .collect::<Result<Vec<String>, rusqlite::Error>>()
1608 .unwrap();
1609 tables.sort();
1610
1611 assert_eq!(
1613 tables,
1614 vec!["_migratio_version_".to_string(), "test2".to_string()]
1615 );
1616
1617 let mut stmt = conn.prepare("SELECT * FROM test2").unwrap();
1619 let rows = stmt
1620 .query_map([], |x| {
1621 let x: i64 = x.get(0)?;
1622 Ok(x)
1623 })
1624 .unwrap()
1625 .collect::<Result<Vec<i64>, rusqlite::Error>>()
1626 .unwrap();
1627 assert_eq!(rows, vec![1, 2]);
1628
1629 let mut stmt = conn
1631 .prepare("SELECT version FROM _migratio_version_")
1632 .unwrap();
1633 let version: u32 = stmt.query_row([], |row| row.get(0)).unwrap();
1634 assert_eq!(version, 1);
1635 }
1636
1637 #[test]
1638 fn incremental_migrations_different_applied_at() {
1639 use std::thread;
1640 use std::time::Duration;
1641
1642 let mut conn = Connection::open_in_memory().unwrap();
1643
1644 struct Migration1;
1646 impl Migration for Migration1 {
1647 fn version(&self) -> u32 {
1648 1
1649 }
1650 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1651 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
1652 Ok(())
1653 }
1654 fn name(&self) -> String {
1655 "create_test1_table".to_string()
1656 }
1657 }
1658
1659 struct Migration2;
1660 impl Migration for Migration2 {
1661 fn version(&self) -> u32 {
1662 2
1663 }
1664 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1665 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
1666 Ok(())
1667 }
1668 fn name(&self) -> String {
1669 "create_test2_table".to_string()
1670 }
1671 }
1672
1673 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
1675 let report = migrator.upgrade(&mut conn).unwrap();
1676 assert_eq!(report.migrations_run, vec![1, 2]);
1677
1678 let first_batch: Vec<(u32, String, String)> = {
1680 let mut stmt = conn
1681 .prepare(
1682 "SELECT version, name, applied_at FROM _migratio_version_ ORDER BY version",
1683 )
1684 .unwrap();
1685 stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)))
1686 .unwrap()
1687 .collect::<Result<Vec<_>, _>>()
1688 .unwrap()
1689 };
1690
1691 assert_eq!(first_batch.len(), 2);
1692 assert_eq!(first_batch[0].0, 1);
1693 assert_eq!(first_batch[0].1, "create_test1_table");
1694 assert_eq!(first_batch[1].0, 2);
1695 assert_eq!(first_batch[1].1, "create_test2_table");
1696 assert_eq!(first_batch[0].2, first_batch[1].2);
1698 let first_batch_timestamp = first_batch[0].2.clone();
1699
1700 thread::sleep(Duration::from_millis(2));
1702
1703 struct Migration3;
1705 impl Migration for Migration3 {
1706 fn version(&self) -> u32 {
1707 3
1708 }
1709 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1710 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
1711 Ok(())
1712 }
1713 fn name(&self) -> String {
1714 "create_test3_table".to_string()
1715 }
1716 }
1717
1718 let migrator = SqliteMigrator::new(vec![
1720 Box::new(Migration1),
1721 Box::new(Migration2),
1722 Box::new(Migration3),
1723 ]);
1724 let report = migrator.upgrade(&mut conn).unwrap();
1725 assert_eq!(report.migrations_run, vec![3]);
1726
1727 let all_migrations: Vec<(u32, String, String)> = {
1729 let mut stmt = conn
1730 .prepare(
1731 "SELECT version, name, applied_at FROM _migratio_version_ ORDER BY version",
1732 )
1733 .unwrap();
1734 stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)))
1735 .unwrap()
1736 .collect::<Result<Vec<_>, _>>()
1737 .unwrap()
1738 };
1739
1740 assert_eq!(all_migrations.len(), 3);
1741 assert_eq!(all_migrations[0].0, 1);
1742 assert_eq!(all_migrations[1].0, 2);
1743 assert_eq!(all_migrations[2].0, 3);
1744 assert_eq!(all_migrations[2].1, "create_test3_table");
1745
1746 assert_eq!(all_migrations[0].2, all_migrations[1].2);
1748 assert_ne!(all_migrations[2].2, first_batch_timestamp);
1750 }
1751
1752 #[test]
1753 #[should_panic(expected = "Migration version must be greater than 0")]
1754 fn new_rejects_zero_version() {
1755 struct Migration0;
1756 impl Migration for Migration0 {
1757 fn version(&self) -> u32 {
1758 0
1759 }
1760 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1761 Ok(())
1762 }
1763 }
1764
1765 let _ = SqliteMigrator::new(vec![Box::new(Migration0)]);
1766 }
1767
1768 #[test]
1769 #[should_panic(expected = "Duplicate migration version found: 2")]
1770 fn new_rejects_duplicate_versions() {
1771 struct Migration1;
1772 impl Migration for Migration1 {
1773 fn version(&self) -> u32 {
1774 1
1775 }
1776 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1777 Ok(())
1778 }
1779 }
1780
1781 struct Migration2a;
1782 impl Migration for Migration2a {
1783 fn version(&self) -> u32 {
1784 2
1785 }
1786 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1787 Ok(())
1788 }
1789 }
1790
1791 struct Migration2b;
1792 impl Migration for Migration2b {
1793 fn version(&self) -> u32 {
1794 2
1795 }
1796 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1797 Ok(())
1798 }
1799 }
1800
1801 let _ = SqliteMigrator::new(vec![
1802 Box::new(Migration1),
1803 Box::new(Migration2a),
1804 Box::new(Migration2b),
1805 ]);
1806 }
1807
1808 #[test]
1809 #[should_panic(expected = "Migration versions must start at 1, found starting version: 2")]
1810 fn new_rejects_non_starting_at_one() {
1811 struct Migration2;
1812 impl Migration for Migration2 {
1813 fn version(&self) -> u32 {
1814 2
1815 }
1816 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1817 Ok(())
1818 }
1819 }
1820
1821 struct Migration3;
1822 impl Migration for Migration3 {
1823 fn version(&self) -> u32 {
1824 3
1825 }
1826 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1827 Ok(())
1828 }
1829 }
1830
1831 let _ = SqliteMigrator::new(vec![Box::new(Migration2), Box::new(Migration3)]);
1832 }
1833
1834 #[test]
1835 #[should_panic(expected = "Migration versions must be contiguous. Expected version 2, found 3")]
1836 fn new_rejects_non_contiguous() {
1837 struct Migration1;
1838 impl Migration for Migration1 {
1839 fn version(&self) -> u32 {
1840 1
1841 }
1842 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1843 Ok(())
1844 }
1845 }
1846
1847 struct Migration3;
1848 impl Migration for Migration3 {
1849 fn version(&self) -> u32 {
1850 3
1851 }
1852 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1853 Ok(())
1854 }
1855 }
1856
1857 let _ = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration3)]);
1858 }
1859
1860 #[test]
1861 fn try_new_returns_err_for_non_starting_at_one() {
1862 struct Migration2;
1863 impl Migration for Migration2 {
1864 fn version(&self) -> u32 {
1865 2
1866 }
1867 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1868 Ok(())
1869 }
1870 }
1871
1872 let result = SqliteMigrator::try_new(vec![Box::new(Migration2)]);
1873 assert!(result.is_err());
1874 assert_eq!(
1875 result.unwrap_err(),
1876 "Migration versions must start at 1, found starting version: 2"
1877 );
1878 }
1879
1880 #[test]
1881 fn try_new_returns_err_for_duplicate_versions() {
1882 struct Migration1;
1883 impl Migration for Migration1 {
1884 fn version(&self) -> u32 {
1885 1
1886 }
1887 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1888 Ok(())
1889 }
1890 }
1891
1892 struct Migration2a;
1893 impl Migration for Migration2a {
1894 fn version(&self) -> u32 {
1895 2
1896 }
1897 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1898 Ok(())
1899 }
1900 }
1901
1902 struct Migration2b;
1903 impl Migration for Migration2b {
1904 fn version(&self) -> u32 {
1905 2
1906 }
1907 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1908 Ok(())
1909 }
1910 }
1911
1912 let result = SqliteMigrator::try_new(vec![
1913 Box::new(Migration1),
1914 Box::new(Migration2a),
1915 Box::new(Migration2b),
1916 ]);
1917 assert!(result.is_err());
1918 assert_eq!(result.unwrap_err(), "Duplicate migration version found: 2");
1919 }
1920
1921 #[test]
1922 fn try_new_returns_ok_for_valid_migrations() {
1923 struct Migration1;
1924 impl Migration for Migration1 {
1925 fn version(&self) -> u32 {
1926 1
1927 }
1928 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1929 Ok(())
1930 }
1931 }
1932
1933 struct Migration2;
1934 impl Migration for Migration2 {
1935 fn version(&self) -> u32 {
1936 2
1937 }
1938 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
1939 Ok(())
1940 }
1941 }
1942
1943 let result = SqliteMigrator::try_new(vec![Box::new(Migration1), Box::new(Migration2)]);
1944 assert!(result.is_ok());
1945 }
1946
1947 #[test]
1948 fn checksum_validation_detects_modified_migration() {
1949 let mut conn = Connection::open_in_memory().unwrap();
1950
1951 struct Migration1V1;
1953 impl Migration for Migration1V1 {
1954 fn version(&self) -> u32 {
1955 1
1956 }
1957 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1958 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
1959 Ok(())
1960 }
1961 fn name(&self) -> String {
1962 "create_test_table".to_string()
1963 }
1964 }
1965
1966 let migrator = SqliteMigrator::new(vec![Box::new(Migration1V1)]);
1968 let report = migrator.upgrade(&mut conn).unwrap();
1969 assert_eq!(report.migrations_run, vec![1]);
1970
1971 struct Migration1V2;
1973 impl Migration for Migration1V2 {
1974 fn version(&self) -> u32 {
1975 1
1976 }
1977 fn up(&self, tx: &Transaction) -> Result<(), Error> {
1978 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
1979 Ok(())
1980 }
1981 fn name(&self) -> String {
1982 "create_test_table_modified".to_string() }
1984 }
1985
1986 let migrator = SqliteMigrator::new(vec![Box::new(Migration1V2)]);
1988 let result = migrator.upgrade(&mut conn);
1989
1990 assert!(result.is_err());
1991 let err_msg = format!("{:?}", result.unwrap_err());
1992 assert!(err_msg.contains("checksum mismatch"));
1993 assert!(err_msg.contains("Migration 1"));
1994 }
1995
1996 #[test]
1997 fn checksum_validation_passes_for_unmodified_migrations() {
1998 let mut conn = Connection::open_in_memory().unwrap();
1999
2000 struct Migration1;
2002 impl Migration for Migration1 {
2003 fn version(&self) -> u32 {
2004 1
2005 }
2006 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2007 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2008 Ok(())
2009 }
2010 fn name(&self) -> String {
2011 "create_test1_table".to_string()
2012 }
2013 }
2014
2015 struct Migration2;
2016 impl Migration for Migration2 {
2017 fn version(&self) -> u32 {
2018 2
2019 }
2020 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2021 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
2022 Ok(())
2023 }
2024 fn name(&self) -> String {
2025 "create_test2_table".to_string()
2026 }
2027 }
2028
2029 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2031 let report = migrator.upgrade(&mut conn).unwrap();
2032 assert_eq!(report.migrations_run, vec![1]);
2033
2034 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
2036 let report = migrator.upgrade(&mut conn).unwrap();
2037 assert_eq!(report.migrations_run, vec![2]);
2038
2039 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
2041 let report = migrator.upgrade(&mut conn).unwrap();
2042 assert_eq!(report.migrations_run, vec![] as Vec<u32>);
2043 }
2044
2045 #[test]
2046 fn checksums_stored_in_database() {
2047 let mut conn = Connection::open_in_memory().unwrap();
2048
2049 struct Migration1;
2050 impl Migration for Migration1 {
2051 fn version(&self) -> u32 {
2052 1
2053 }
2054 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2055 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
2056 Ok(())
2057 }
2058 fn name(&self) -> String {
2059 "my_migration".to_string()
2060 }
2061 }
2062
2063 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2064 migrator.upgrade(&mut conn).unwrap();
2065
2066 let mut stmt = conn
2068 .prepare("SELECT checksum FROM _migratio_version_ WHERE version = 1")
2069 .unwrap();
2070 let checksum: String = stmt.query_row([], |row| row.get(0)).unwrap();
2071
2072 assert_eq!(checksum.len(), 64);
2074 assert!(checksum.chars().all(|c| c.is_ascii_hexdigit()));
2075
2076 let migration = Box::new(Migration1) as Box<dyn Migration>;
2078 let expected_checksum = SqliteMigrator::calculate_checksum(&migration);
2079 assert_eq!(checksum, expected_checksum);
2080 }
2081
2082 #[test]
2083 fn downgrade_single_migration() {
2084 let mut conn = Connection::open_in_memory().unwrap();
2085
2086 struct Migration1;
2087 impl Migration for Migration1 {
2088 fn version(&self) -> u32 {
2089 1
2090 }
2091 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2092 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
2093 Ok(())
2094 }
2095 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2096 tx.execute("DROP TABLE test", [])?;
2097 Ok(())
2098 }
2099 }
2100
2101 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2103 let report = migrator.upgrade(&mut conn).unwrap();
2104 assert_eq!(report.migrations_run, vec![1]);
2105
2106 {
2108 let mut stmt = conn
2109 .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='test'")
2110 .unwrap();
2111 assert!(stmt.query([]).unwrap().next().unwrap().is_some());
2112 }
2113
2114 let report = migrator.downgrade(&mut conn, 0).unwrap();
2116 assert_eq!(report.migrations_run, vec![1]);
2117
2118 {
2120 let mut stmt = conn
2121 .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='test'")
2122 .unwrap();
2123 assert!(stmt.query([]).unwrap().next().unwrap().is_none());
2124 }
2125
2126 let mut stmt = conn
2128 .prepare("SELECT COUNT(*) FROM _migratio_version_")
2129 .unwrap();
2130 let count: i64 = stmt.query_row([], |row| row.get(0)).unwrap();
2131 assert_eq!(count, 0);
2132 }
2133
2134 #[test]
2135 fn downgrade_multiple_migrations() {
2136 let mut conn = Connection::open_in_memory().unwrap();
2137
2138 struct Migration1;
2139 impl Migration for Migration1 {
2140 fn version(&self) -> u32 {
2141 1
2142 }
2143 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2144 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2145 Ok(())
2146 }
2147 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2148 tx.execute("DROP TABLE test1", [])?;
2149 Ok(())
2150 }
2151 }
2152
2153 struct Migration2;
2154 impl Migration for Migration2 {
2155 fn version(&self) -> u32 {
2156 2
2157 }
2158 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2159 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
2160 Ok(())
2161 }
2162 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2163 tx.execute("DROP TABLE test2", [])?;
2164 Ok(())
2165 }
2166 }
2167
2168 struct Migration3;
2169 impl Migration for Migration3 {
2170 fn version(&self) -> u32 {
2171 3
2172 }
2173 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2174 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
2175 Ok(())
2176 }
2177 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2178 tx.execute("DROP TABLE test3", [])?;
2179 Ok(())
2180 }
2181 }
2182
2183 let migrator = SqliteMigrator::new(vec![
2185 Box::new(Migration1),
2186 Box::new(Migration2),
2187 Box::new(Migration3),
2188 ]);
2189 let report = migrator.upgrade(&mut conn).unwrap();
2190 assert_eq!(report.migrations_run, vec![1, 2, 3]);
2191
2192 let report = migrator.downgrade(&mut conn, 1).unwrap();
2194 assert_eq!(report.migrations_run, vec![3, 2]);
2195
2196 let mut stmt = conn
2198 .prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
2199 .unwrap();
2200 let tables: Vec<String> = stmt
2201 .query_map([], |row| row.get(0))
2202 .unwrap()
2203 .collect::<Result<Vec<_>, _>>()
2204 .unwrap();
2205 assert_eq!(tables, vec!["_migratio_version_", "test1"]);
2206
2207 let mut stmt = conn
2209 .prepare("SELECT version FROM _migratio_version_ ORDER BY version")
2210 .unwrap();
2211 let versions: Vec<u32> = stmt
2212 .query_map([], |row| row.get(0))
2213 .unwrap()
2214 .collect::<Result<Vec<_>, _>>()
2215 .unwrap();
2216 assert_eq!(versions, vec![1]);
2217 }
2218
2219 #[test]
2220 fn downgrade_all_migrations() {
2221 let mut conn = Connection::open_in_memory().unwrap();
2222
2223 struct Migration1;
2224 impl Migration for Migration1 {
2225 fn version(&self) -> u32 {
2226 1
2227 }
2228 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2229 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2230 Ok(())
2231 }
2232 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2233 tx.execute("DROP TABLE test1", [])?;
2234 Ok(())
2235 }
2236 }
2237
2238 struct Migration2;
2239 impl Migration for Migration2 {
2240 fn version(&self) -> u32 {
2241 2
2242 }
2243 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2244 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
2245 Ok(())
2246 }
2247 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2248 tx.execute("DROP TABLE test2", [])?;
2249 Ok(())
2250 }
2251 }
2252
2253 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
2255 let report = migrator.upgrade(&mut conn).unwrap();
2256 assert_eq!(report.migrations_run, vec![1, 2]);
2257
2258 let report = migrator.downgrade(&mut conn, 0).unwrap();
2260 assert_eq!(report.migrations_run, vec![2, 1]);
2261
2262 let mut stmt = conn
2264 .prepare("SELECT name FROM sqlite_master WHERE type='table'")
2265 .unwrap();
2266 let tables: Vec<String> = stmt
2267 .query_map([], |row| row.get(0))
2268 .unwrap()
2269 .collect::<Result<Vec<_>, _>>()
2270 .unwrap();
2271 assert_eq!(tables, vec!["_migratio_version_"]);
2272
2273 let mut stmt = conn
2275 .prepare("SELECT COUNT(*) FROM _migratio_version_")
2276 .unwrap();
2277 let count: i64 = stmt.query_row([], |row| row.get(0)).unwrap();
2278 assert_eq!(count, 0);
2279 }
2280
2281 #[test]
2282 fn downgrade_on_clean_database() {
2283 let mut conn = Connection::open_in_memory().unwrap();
2284
2285 struct Migration1;
2286 impl Migration for Migration1 {
2287 fn version(&self) -> u32 {
2288 1
2289 }
2290 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
2291 Ok(())
2292 }
2293 fn down(&self, _tx: &Transaction) -> Result<(), Error> {
2294 Ok(())
2295 }
2296 }
2297
2298 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2300 let report = migrator.downgrade(&mut conn, 0).unwrap();
2301 assert_eq!(report.migrations_run, vec![] as Vec<u32>);
2302 assert!(!report.schema_version_table_existed);
2303 }
2304
2305 #[test]
2306 fn downgrade_with_invalid_target_version() {
2307 let mut conn = Connection::open_in_memory().unwrap();
2308
2309 struct Migration1;
2310 impl Migration for Migration1 {
2311 fn version(&self) -> u32 {
2312 1
2313 }
2314 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2315 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
2316 Ok(())
2317 }
2318 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2319 tx.execute("DROP TABLE test", [])?;
2320 Ok(())
2321 }
2322 }
2323
2324 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2326 migrator.upgrade(&mut conn).unwrap();
2327
2328 let result = migrator.downgrade(&mut conn, 5);
2330 assert!(result.is_err());
2331 let err_msg = format!("{:?}", result.unwrap_err());
2332 assert!(err_msg.contains("Cannot downgrade to version 5 when current version is 1"));
2333 }
2334
2335 #[test]
2336 #[should_panic(expected = "does not support downgrade")]
2337 fn downgrade_panics_when_down_not_implemented() {
2338 let mut conn = Connection::open_in_memory().unwrap();
2339
2340 struct Migration1;
2341 impl Migration for Migration1 {
2342 fn version(&self) -> u32 {
2343 1
2344 }
2345 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2346 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
2347 Ok(())
2348 }
2349 }
2351
2352 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2354 migrator.upgrade(&mut conn).unwrap();
2355
2356 let _ = migrator.downgrade(&mut conn, 0);
2358 }
2359
2360 #[test]
2361 fn downgrade_validates_checksums() {
2362 let mut conn = Connection::open_in_memory().unwrap();
2363
2364 struct Migration1V1;
2365 impl Migration for Migration1V1 {
2366 fn version(&self) -> u32 {
2367 1
2368 }
2369 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2370 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
2371 Ok(())
2372 }
2373 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2374 tx.execute("DROP TABLE test", [])?;
2375 Ok(())
2376 }
2377 fn name(&self) -> String {
2378 "create_test_table".to_string()
2379 }
2380 }
2381
2382 let migrator = SqliteMigrator::new(vec![Box::new(Migration1V1)]);
2384 migrator.upgrade(&mut conn).unwrap();
2385
2386 struct Migration1V2;
2388 impl Migration for Migration1V2 {
2389 fn version(&self) -> u32 {
2390 1
2391 }
2392 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2393 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
2394 Ok(())
2395 }
2396 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2397 tx.execute("DROP TABLE test", [])?;
2398 Ok(())
2399 }
2400 fn name(&self) -> String {
2401 "create_test_table_modified".to_string() }
2403 }
2404
2405 let migrator = SqliteMigrator::new(vec![Box::new(Migration1V2)]);
2406 let result = migrator.downgrade(&mut conn, 0);
2407
2408 assert!(result.is_err());
2409 let err_msg = format!("{:?}", result.unwrap_err());
2410 assert!(err_msg.contains("checksum mismatch"));
2411 }
2412
2413 #[test]
2414 fn get_current_version_on_clean_database() {
2415 let mut conn = Connection::open_in_memory().unwrap();
2416
2417 struct Migration1;
2418 impl Migration for Migration1 {
2419 fn version(&self) -> u32 {
2420 1
2421 }
2422 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
2423 Ok(())
2424 }
2425 }
2426
2427 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2428 let version = migrator.get_current_version(&mut conn).unwrap();
2429 assert_eq!(version, 0);
2430 }
2431
2432 #[test]
2433 fn get_current_version_after_migrations() {
2434 let mut conn = Connection::open_in_memory().unwrap();
2435
2436 struct Migration1;
2437 impl Migration for Migration1 {
2438 fn version(&self) -> u32 {
2439 1
2440 }
2441 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2442 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2443 Ok(())
2444 }
2445 }
2446
2447 struct Migration2;
2448 impl Migration for Migration2 {
2449 fn version(&self) -> u32 {
2450 2
2451 }
2452 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2453 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
2454 Ok(())
2455 }
2456 }
2457
2458 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
2459
2460 assert_eq!(migrator.get_current_version(&mut conn).unwrap(), 0);
2462
2463 migrator.upgrade(&mut conn).unwrap();
2465 assert_eq!(migrator.get_current_version(&mut conn).unwrap(), 2);
2466 }
2467
2468 #[test]
2469 fn preview_upgrade_on_clean_database() {
2470 let mut conn = Connection::open_in_memory().unwrap();
2471
2472 struct Migration1;
2473 impl Migration for Migration1 {
2474 fn version(&self) -> u32 {
2475 1
2476 }
2477 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
2478 Ok(())
2479 }
2480 }
2481
2482 struct Migration2;
2483 impl Migration for Migration2 {
2484 fn version(&self) -> u32 {
2485 2
2486 }
2487 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
2488 Ok(())
2489 }
2490 }
2491
2492 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
2493 let pending = migrator.preview_upgrade(&mut conn).unwrap();
2494
2495 assert_eq!(pending.len(), 2);
2496 assert_eq!(pending[0].version(), 1);
2497 assert_eq!(pending[1].version(), 2);
2498 }
2499
2500 #[test]
2501 fn preview_upgrade_with_partial_migrations() {
2502 let mut conn = Connection::open_in_memory().unwrap();
2503
2504 struct Migration1;
2505 impl Migration for Migration1 {
2506 fn version(&self) -> u32 {
2507 1
2508 }
2509 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2510 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2511 Ok(())
2512 }
2513 }
2514
2515 struct Migration2;
2516 impl Migration for Migration2 {
2517 fn version(&self) -> u32 {
2518 2
2519 }
2520 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2521 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
2522 Ok(())
2523 }
2524 }
2525
2526 struct Migration3;
2527 impl Migration for Migration3 {
2528 fn version(&self) -> u32 {
2529 3
2530 }
2531 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2532 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
2533 Ok(())
2534 }
2535 }
2536
2537 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2539 migrator.upgrade(&mut conn).unwrap();
2540
2541 let migrator = SqliteMigrator::new(vec![
2543 Box::new(Migration1),
2544 Box::new(Migration2),
2545 Box::new(Migration3),
2546 ]);
2547 let pending = migrator.preview_upgrade(&mut conn).unwrap();
2548
2549 assert_eq!(pending.len(), 2);
2550 assert_eq!(pending[0].version(), 2);
2551 assert_eq!(pending[1].version(), 3);
2552 }
2553
2554 #[test]
2555 fn preview_upgrade_when_up_to_date() {
2556 let mut conn = Connection::open_in_memory().unwrap();
2557
2558 struct Migration1;
2559 impl Migration for Migration1 {
2560 fn version(&self) -> u32 {
2561 1
2562 }
2563 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2564 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2565 Ok(())
2566 }
2567 }
2568
2569 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2570 migrator.upgrade(&mut conn).unwrap();
2571
2572 let pending = migrator.preview_upgrade(&mut conn).unwrap();
2573 assert_eq!(pending.len(), 0);
2574 }
2575
2576 #[test]
2577 fn preview_downgrade_to_zero() {
2578 let mut conn = Connection::open_in_memory().unwrap();
2579
2580 struct Migration1;
2581 impl Migration for Migration1 {
2582 fn version(&self) -> u32 {
2583 1
2584 }
2585 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2586 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2587 Ok(())
2588 }
2589 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2590 tx.execute("DROP TABLE test1", [])?;
2591 Ok(())
2592 }
2593 }
2594
2595 struct Migration2;
2596 impl Migration for Migration2 {
2597 fn version(&self) -> u32 {
2598 2
2599 }
2600 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2601 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
2602 Ok(())
2603 }
2604 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2605 tx.execute("DROP TABLE test2", [])?;
2606 Ok(())
2607 }
2608 }
2609
2610 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
2611 migrator.upgrade(&mut conn).unwrap();
2612
2613 let to_rollback = migrator.preview_downgrade(&mut conn, 0).unwrap();
2614
2615 assert_eq!(to_rollback.len(), 2);
2616 assert_eq!(to_rollback[0].version(), 2); assert_eq!(to_rollback[1].version(), 1);
2618 }
2619
2620 #[test]
2621 fn preview_downgrade_to_specific_version() {
2622 let mut conn = Connection::open_in_memory().unwrap();
2623
2624 struct Migration1;
2625 impl Migration for Migration1 {
2626 fn version(&self) -> u32 {
2627 1
2628 }
2629 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2630 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2631 Ok(())
2632 }
2633 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2634 tx.execute("DROP TABLE test1", [])?;
2635 Ok(())
2636 }
2637 }
2638
2639 struct Migration2;
2640 impl Migration for Migration2 {
2641 fn version(&self) -> u32 {
2642 2
2643 }
2644 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2645 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
2646 Ok(())
2647 }
2648 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2649 tx.execute("DROP TABLE test2", [])?;
2650 Ok(())
2651 }
2652 }
2653
2654 struct Migration3;
2655 impl Migration for Migration3 {
2656 fn version(&self) -> u32 {
2657 3
2658 }
2659 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2660 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
2661 Ok(())
2662 }
2663 fn down(&self, tx: &Transaction) -> Result<(), Error> {
2664 tx.execute("DROP TABLE test3", [])?;
2665 Ok(())
2666 }
2667 }
2668
2669 let migrator = SqliteMigrator::new(vec![
2670 Box::new(Migration1),
2671 Box::new(Migration2),
2672 Box::new(Migration3),
2673 ]);
2674 migrator.upgrade(&mut conn).unwrap();
2675
2676 let to_rollback = migrator.preview_downgrade(&mut conn, 1).unwrap();
2677
2678 assert_eq!(to_rollback.len(), 2);
2679 assert_eq!(to_rollback[0].version(), 3); assert_eq!(to_rollback[1].version(), 2);
2681 }
2682
2683 #[test]
2684 fn preview_downgrade_on_clean_database() {
2685 let mut conn = Connection::open_in_memory().unwrap();
2686
2687 struct Migration1;
2688 impl Migration for Migration1 {
2689 fn version(&self) -> u32 {
2690 1
2691 }
2692 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
2693 Ok(())
2694 }
2695 fn down(&self, _tx: &Transaction) -> Result<(), Error> {
2696 Ok(())
2697 }
2698 }
2699
2700 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2701 let to_rollback = migrator.preview_downgrade(&mut conn, 0).unwrap();
2702
2703 assert_eq!(to_rollback.len(), 0);
2704 }
2705
2706 #[test]
2707 fn preview_downgrade_with_invalid_target() {
2708 let mut conn = Connection::open_in_memory().unwrap();
2709
2710 struct Migration1;
2711 impl Migration for Migration1 {
2712 fn version(&self) -> u32 {
2713 1
2714 }
2715 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2716 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2717 Ok(())
2718 }
2719 fn down(&self, _tx: &Transaction) -> Result<(), Error> {
2720 Ok(())
2721 }
2722 }
2723
2724 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2725 migrator.upgrade(&mut conn).unwrap();
2726
2727 let result = migrator.preview_downgrade(&mut conn, 5);
2728 assert!(result.is_err());
2729 let err_msg = format!("{:?}", result.unwrap_err());
2730 assert!(err_msg.contains("Cannot downgrade to version 5 when current version is 1"));
2731 }
2732
2733 #[test]
2734 fn migration_failure_accessors() {
2735 let mut conn = Connection::open_in_memory().unwrap();
2736
2737 struct Migration1;
2738 impl Migration for Migration1 {
2739 fn version(&self) -> u32 {
2740 1
2741 }
2742 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2743 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
2744 Ok(())
2745 }
2746 fn name(&self) -> String {
2747 "create_test_table".to_string()
2748 }
2749 }
2750
2751 struct Migration2;
2752 impl Migration for Migration2 {
2753 fn version(&self) -> u32 {
2754 2
2755 }
2756 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2757 tx.execute("INVALID SQL HERE", [])?;
2759 Ok(())
2760 }
2761 fn name(&self) -> String {
2762 "failing_migration".to_string()
2763 }
2764 }
2765
2766 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
2767 let report = migrator.upgrade(&mut conn).unwrap();
2768
2769 assert!(report.failing_migration.is_some());
2771
2772 let failure = report.failing_migration.as_ref().unwrap();
2773
2774 let migration = failure.migration();
2776 assert_eq!(migration.version(), 2);
2777 assert_eq!(migration.name(), "failing_migration");
2778
2779 let error = failure.error();
2781 assert!(matches!(error, Error::Rusqlite(_)));
2782 }
2783
2784 #[test]
2785 fn migration_failure_can_be_pattern_matched() {
2786 let mut conn = Connection::open_in_memory().unwrap();
2787
2788 struct Migration1;
2789 impl Migration for Migration1 {
2790 fn version(&self) -> u32 {
2791 1
2792 }
2793 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2794 tx.execute("INVALID SQL", [])?;
2795 Ok(())
2796 }
2797 }
2798
2799 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2800 let report = migrator.upgrade(&mut conn).unwrap();
2801
2802 match report.failing_migration {
2804 Some(ref failure) => {
2805 println!(
2806 "Migration {} failed: {:?}",
2807 failure.migration().version(),
2808 failure.error()
2809 );
2810 assert_eq!(failure.migration().version(), 1);
2811 }
2812 None => panic!("Expected a failure"),
2813 }
2814 }
2815
2816 #[test]
2817 fn get_migration_history_on_clean_database() {
2818 let mut conn = Connection::open_in_memory().unwrap();
2819
2820 struct Migration1;
2821 impl Migration for Migration1 {
2822 fn version(&self) -> u32 {
2823 1
2824 }
2825 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
2826 Ok(())
2827 }
2828 }
2829
2830 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2831 let history = migrator.get_migration_history(&mut conn).unwrap();
2832 assert_eq!(history.len(), 0);
2833 }
2834
2835 #[test]
2836 fn get_migration_history_after_migrations() {
2837 let mut conn = Connection::open_in_memory().unwrap();
2838
2839 struct Migration1;
2840 impl Migration for Migration1 {
2841 fn version(&self) -> u32 {
2842 1
2843 }
2844 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2845 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2846 Ok(())
2847 }
2848 fn name(&self) -> String {
2849 "create_test1_table".to_string()
2850 }
2851 }
2852
2853 struct Migration2;
2854 impl Migration for Migration2 {
2855 fn version(&self) -> u32 {
2856 2
2857 }
2858 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2859 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
2860 Ok(())
2861 }
2862 fn name(&self) -> String {
2863 "create_test2_table".to_string()
2864 }
2865 }
2866
2867 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
2868 migrator.upgrade(&mut conn).unwrap();
2869
2870 let history = migrator.get_migration_history(&mut conn).unwrap();
2871
2872 assert_eq!(history.len(), 2);
2873
2874 assert_eq!(history[0].version, 1);
2876 assert_eq!(history[0].name, "create_test1_table");
2877 assert!(history[0].applied_at.timestamp() > 0);
2878 assert_eq!(history[0].checksum.len(), 64); assert_eq!(history[1].version, 2);
2882 assert_eq!(history[1].name, "create_test2_table");
2883 assert!(history[1].applied_at.timestamp() > 0);
2884 assert_eq!(history[1].checksum.len(), 64);
2885
2886 assert_eq!(history[0].applied_at, history[1].applied_at);
2888 }
2889
2890 #[test]
2891 fn get_migration_history_shows_incremental_batches() {
2892 use std::thread;
2893 use std::time::Duration;
2894
2895 let mut conn = Connection::open_in_memory().unwrap();
2896
2897 struct Migration1;
2898 impl Migration for Migration1 {
2899 fn version(&self) -> u32 {
2900 1
2901 }
2902 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2903 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2904 Ok(())
2905 }
2906 fn name(&self) -> String {
2907 "migration_one".to_string()
2908 }
2909 }
2910
2911 struct Migration2;
2912 impl Migration for Migration2 {
2913 fn version(&self) -> u32 {
2914 2
2915 }
2916 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2917 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
2918 Ok(())
2919 }
2920 fn name(&self) -> String {
2921 "migration_two".to_string()
2922 }
2923 }
2924
2925 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2927 migrator.upgrade(&mut conn).unwrap();
2928
2929 let history = migrator.get_migration_history(&mut conn).unwrap();
2930 assert_eq!(history.len(), 1);
2931 let first_timestamp = history[0].applied_at;
2932
2933 thread::sleep(Duration::from_millis(2));
2935
2936 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
2938 migrator.upgrade(&mut conn).unwrap();
2939
2940 let history = migrator.get_migration_history(&mut conn).unwrap();
2941 assert_eq!(history.len(), 2);
2942
2943 assert_eq!(history[0].applied_at, first_timestamp);
2945
2946 assert_ne!(history[1].applied_at, first_timestamp);
2948 }
2949
2950 #[test]
2951 fn get_migration_history_includes_checksums() {
2952 let mut conn = Connection::open_in_memory().unwrap();
2953
2954 struct Migration1;
2955 impl Migration for Migration1 {
2956 fn version(&self) -> u32 {
2957 1
2958 }
2959 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2960 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
2961 Ok(())
2962 }
2963 fn name(&self) -> String {
2964 "test_migration".to_string()
2965 }
2966 }
2967
2968 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
2969 migrator.upgrade(&mut conn).unwrap();
2970
2971 let history = migrator.get_migration_history(&mut conn).unwrap();
2972 assert_eq!(history.len(), 1);
2973
2974 let migration = Box::new(Migration1) as Box<dyn Migration>;
2976 let expected_checksum = SqliteMigrator::calculate_checksum(&migration);
2977 assert_eq!(history[0].checksum, expected_checksum);
2978 }
2979
2980 #[test]
2981 fn concurrent_migrations_are_safe() {
2982 use std::sync::{Arc, Barrier};
2983 use std::thread;
2984 use tempfile::NamedTempFile;
2985
2986 let temp_file = NamedTempFile::new().unwrap();
2988 let db_path = temp_file.path().to_str().unwrap().to_string();
2989
2990 struct Migration1;
2991 impl Migration for Migration1 {
2992 fn version(&self) -> u32 {
2993 1
2994 }
2995 fn up(&self, tx: &Transaction) -> Result<(), Error> {
2996 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
2998 std::thread::sleep(std::time::Duration::from_millis(10));
2999 tx.execute("INSERT INTO test1 (id) VALUES (1)", [])?;
3000 Ok(())
3001 }
3002 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3003 tx.execute("DROP TABLE test1", [])?;
3004 Ok(())
3005 }
3006 }
3007
3008 struct Migration2;
3009 impl Migration for Migration2 {
3010 fn version(&self) -> u32 {
3011 2
3012 }
3013 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3014 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
3015 std::thread::sleep(std::time::Duration::from_millis(10));
3016 tx.execute("INSERT INTO test2 (id) VALUES (1)", [])?;
3017 Ok(())
3018 }
3019 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3020 tx.execute("DROP TABLE test2", [])?;
3021 Ok(())
3022 }
3023 }
3024
3025 let barrier = Arc::new(Barrier::new(3));
3027 let db_path_arc = Arc::new(db_path);
3028
3029 let handles: Vec<_> = (0..3)
3031 .map(|i| {
3032 let barrier = Arc::clone(&barrier);
3033 let db_path = Arc::clone(&db_path_arc);
3034 thread::spawn(move || {
3035 barrier.wait();
3037
3038 let mut conn = Connection::open(db_path.as_str()).unwrap();
3040 let migrator =
3041 SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
3042 let result = migrator.upgrade(&mut conn);
3043
3044 assert!(result.is_ok(), "Thread {} failed: {:?}", i, result);
3046
3047 result.unwrap().migrations_run.len()
3049 })
3050 })
3051 .collect();
3052
3053 let migrations_run_counts: Vec<_> =
3055 handles.into_iter().map(|h| h.join().unwrap()).collect();
3056
3057 let two_migrations = migrations_run_counts.iter().filter(|&&c| c == 2).count();
3058 let zero_migrations = migrations_run_counts.iter().filter(|&&c| c == 0).count();
3059
3060 assert_eq!(
3061 two_migrations, 1,
3062 "Exactly one thread should have run 2 migrations"
3063 );
3064 assert_eq!(
3065 zero_migrations, 2,
3066 "Two threads should have found db already migrated"
3067 );
3068
3069 let mut conn = Connection::open(db_path_arc.as_str()).unwrap();
3071 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
3072 let current_version = migrator.get_current_version(&mut conn).unwrap();
3073 assert_eq!(current_version, 2);
3074
3075 let count1: i64 = conn
3077 .query_row("SELECT COUNT(*) FROM test1", [], |row| row.get(0))
3078 .unwrap();
3079 let count2: i64 = conn
3080 .query_row("SELECT COUNT(*) FROM test2", [], |row| row.get(0))
3081 .unwrap();
3082 assert_eq!(count1, 1);
3083 assert_eq!(count2, 1);
3084 }
3085
3086 #[test]
3087 fn concurrent_upgrade_and_downgrade() {
3088 use std::sync::{Arc, Barrier};
3089 use std::thread;
3090 use tempfile::NamedTempFile;
3091
3092 let temp_file = NamedTempFile::new().unwrap();
3094 let db_path = temp_file.path().to_str().unwrap().to_string();
3095
3096 {
3098 let mut conn = Connection::open(&db_path).unwrap();
3099
3100 struct Migration1;
3101 impl Migration for Migration1 {
3102 fn version(&self) -> u32 {
3103 1
3104 }
3105 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3106 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
3107 Ok(())
3108 }
3109 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3110 tx.execute("DROP TABLE test1", [])?;
3111 Ok(())
3112 }
3113 }
3114
3115 struct Migration2;
3116 impl Migration for Migration2 {
3117 fn version(&self) -> u32 {
3118 2
3119 }
3120 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3121 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
3122 Ok(())
3123 }
3124 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3125 tx.execute("DROP TABLE test2", [])?;
3126 Ok(())
3127 }
3128 }
3129
3130 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
3131 migrator.upgrade(&mut conn).unwrap();
3132 }
3133
3134 struct Migration1;
3135 impl Migration for Migration1 {
3136 fn version(&self) -> u32 {
3137 1
3138 }
3139 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3140 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
3141 Ok(())
3142 }
3143 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3144 tx.execute("DROP TABLE test1", [])?;
3145 Ok(())
3146 }
3147 }
3148
3149 struct Migration2;
3150 impl Migration for Migration2 {
3151 fn version(&self) -> u32 {
3152 2
3153 }
3154 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3155 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
3156 Ok(())
3157 }
3158 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3159 tx.execute("DROP TABLE test2", [])?;
3160 Ok(())
3161 }
3162 }
3163
3164 let barrier = Arc::new(Barrier::new(4));
3166 let db_path_arc = Arc::new(db_path);
3167
3168 let handles: Vec<_> = (0..4)
3169 .map(|i| {
3170 let barrier = Arc::clone(&barrier);
3171 let db_path = Arc::clone(&db_path_arc);
3172 thread::spawn(move || {
3173 barrier.wait();
3174
3175 let mut conn = Connection::open(db_path.as_str()).unwrap();
3176 let migrator =
3177 SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
3178
3179 let result = if i % 2 == 0 {
3180 migrator.upgrade(&mut conn)
3182 } else {
3183 migrator.downgrade(&mut conn, 1)
3185 };
3186
3187 assert!(result.is_ok(), "Thread {} got error: {:?}", i, result);
3189 true
3190 })
3191 })
3192 .collect();
3193
3194 for (i, handle) in handles.into_iter().enumerate() {
3196 let result = handle.join();
3197 assert!(result.is_ok(), "Thread {} panicked", i);
3198 assert!(result.unwrap(), "Thread {} failed", i);
3199 }
3200
3201 let mut conn = Connection::open(db_path_arc.as_str()).unwrap();
3203 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
3204 let current_version = migrator.get_current_version(&mut conn).unwrap();
3205 assert!(
3206 current_version == 1 || current_version == 2,
3207 "Version should be 1 or 2, got {}",
3208 current_version
3209 );
3210 }
3211
3212 #[test]
3213 fn custom_busy_timeout() {
3214 use tempfile::NamedTempFile;
3215
3216 let temp_file = NamedTempFile::new().unwrap();
3218 let db_path = temp_file.path().to_str().unwrap().to_string();
3219
3220 struct Migration1;
3221 impl Migration for Migration1 {
3222 fn version(&self) -> u32 {
3223 1
3224 }
3225 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3226 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
3227 Ok(())
3228 }
3229 }
3230
3231 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)])
3233 .with_busy_timeout(std::time::Duration::from_secs(5));
3234
3235 let mut conn = Connection::open(&db_path).unwrap();
3237 let report = migrator.upgrade(&mut conn).unwrap();
3238 assert_eq!(report.migrations_run, vec![1]);
3239
3240 let current_version = migrator.get_current_version(&mut conn).unwrap();
3243 assert_eq!(current_version, 1);
3244 }
3245
3246 #[test]
3247 fn detects_missing_migration_in_code() {
3248 let mut conn = Connection::open_in_memory().unwrap();
3249
3250 struct Migration1;
3251 impl Migration for Migration1 {
3252 fn version(&self) -> u32 {
3253 1
3254 }
3255 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3256 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
3257 Ok(())
3258 }
3259 fn name(&self) -> String {
3260 "create_test1".to_string()
3261 }
3262 }
3263
3264 struct Migration2;
3265 impl Migration for Migration2 {
3266 fn version(&self) -> u32 {
3267 2
3268 }
3269 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3270 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
3271 Ok(())
3272 }
3273 fn name(&self) -> String {
3274 "create_test2".to_string()
3275 }
3276 }
3277
3278 struct Migration3;
3279 impl Migration for Migration3 {
3280 fn version(&self) -> u32 {
3281 3
3282 }
3283 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3284 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
3285 Ok(())
3286 }
3287 fn name(&self) -> String {
3288 "create_test3".to_string()
3289 }
3290 }
3291
3292 let migrator = SqliteMigrator::new(vec![
3294 Box::new(Migration1),
3295 Box::new(Migration2),
3296 Box::new(Migration3),
3297 ]);
3298 migrator.upgrade(&mut conn).unwrap();
3299
3300 conn.execute("DELETE FROM _migratio_version_ WHERE version = 2", [])
3302 .unwrap();
3303
3304 conn.execute(
3311 "INSERT INTO _migratio_version_ (version, name, applied_at, checksum) VALUES (4, 'deleted_migration', datetime('now'), 'fakechecksum')",
3312 [],
3313 )
3314 .unwrap();
3315
3316 let result = migrator.upgrade(&mut conn);
3318
3319 assert!(result.is_err());
3320 let err_msg = format!("{:?}", result.unwrap_err());
3321 assert!(err_msg.contains("Migration 4"));
3322 assert!(err_msg.contains("deleted_migration"));
3323 assert!(err_msg.contains("was previously applied but is no longer present"));
3324 }
3325
3326 #[test]
3327 fn detects_orphaned_migration_added_late() {
3328 let mut conn = Connection::open_in_memory().unwrap();
3329
3330 struct Migration1;
3331 impl Migration for Migration1 {
3332 fn version(&self) -> u32 {
3333 1
3334 }
3335 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3336 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
3337 Ok(())
3338 }
3339 fn name(&self) -> String {
3340 "create_test1".to_string()
3341 }
3342 }
3343
3344 struct Migration2;
3345 impl Migration for Migration2 {
3346 fn version(&self) -> u32 {
3347 2
3348 }
3349 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3350 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
3351 Ok(())
3352 }
3353 fn name(&self) -> String {
3354 "create_test2".to_string()
3355 }
3356 }
3357
3358 struct Migration3;
3359 impl Migration for Migration3 {
3360 fn version(&self) -> u32 {
3361 3
3362 }
3363 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3364 tx.execute("CREATE TABLE test3 (id INTEGER PRIMARY KEY)", [])?;
3365 Ok(())
3366 }
3367 fn name(&self) -> String {
3368 "create_test3".to_string()
3369 }
3370 }
3371
3372 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
3375 migrator.upgrade(&mut conn).unwrap();
3376
3377 let checksum = {
3379 let migration = Box::new(Migration3) as Box<dyn Migration>;
3380 SqliteMigrator::calculate_checksum(&migration)
3381 };
3382 conn.execute(
3383 "INSERT INTO _migratio_version_ (version, name, applied_at, checksum) VALUES (3, 'create_test3', datetime('now'), ?1)",
3384 [&checksum],
3385 )
3386 .unwrap();
3387
3388 let migrator_all = SqliteMigrator::new(vec![
3392 Box::new(Migration1),
3393 Box::new(Migration2),
3394 Box::new(Migration3),
3395 ]);
3396 let result = migrator_all.upgrade(&mut conn);
3397
3398 assert!(result.is_err());
3399 let err_msg = format!("{:?}", result.unwrap_err());
3400 assert!(err_msg.contains("Migration 2"));
3401 assert!(err_msg.contains("create_test2"));
3402 assert!(err_msg.contains("exists in code but was not applied"));
3403 assert!(err_msg.contains("later migrations are already applied"));
3404 }
3405
3406 #[test]
3407 fn detects_missing_migration_during_downgrade() {
3408 let mut conn = Connection::open_in_memory().unwrap();
3409
3410 struct Migration1;
3411 impl Migration for Migration1 {
3412 fn version(&self) -> u32 {
3413 1
3414 }
3415 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3416 tx.execute("CREATE TABLE test1 (id INTEGER PRIMARY KEY)", [])?;
3417 Ok(())
3418 }
3419 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3420 tx.execute("DROP TABLE test1", [])?;
3421 Ok(())
3422 }
3423 fn name(&self) -> String {
3424 "create_test1".to_string()
3425 }
3426 }
3427
3428 struct Migration2;
3429 impl Migration for Migration2 {
3430 fn version(&self) -> u32 {
3431 2
3432 }
3433 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3434 tx.execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY)", [])?;
3435 Ok(())
3436 }
3437 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3438 tx.execute("DROP TABLE test2", [])?;
3439 Ok(())
3440 }
3441 fn name(&self) -> String {
3442 "create_test2".to_string()
3443 }
3444 }
3445
3446 let migrator = SqliteMigrator::new(vec![Box::new(Migration1), Box::new(Migration2)]);
3448 migrator.upgrade(&mut conn).unwrap();
3449
3450 conn.execute(
3452 "INSERT INTO _migratio_version_ (version, name, applied_at, checksum) VALUES (3, 'orphaned_migration', datetime('now'), 'fakechecksum')",
3453 [],
3454 )
3455 .unwrap();
3456
3457 let result = migrator.downgrade(&mut conn, 0);
3459
3460 assert!(result.is_err());
3461 let err_msg = format!("{:?}", result.unwrap_err());
3462 assert!(err_msg.contains("Migration 3"));
3463 assert!(err_msg.contains("orphaned_migration"));
3464 assert!(err_msg.contains("was previously applied but is no longer present"));
3465 }
3466
3467 #[test]
3468 fn hooks_are_called_on_successful_migration() {
3469 use std::sync::{Arc, Mutex};
3470
3471 let mut conn = Connection::open_in_memory().unwrap();
3472
3473 struct Migration1;
3474 impl Migration for Migration1 {
3475 fn version(&self) -> u32 {
3476 1
3477 }
3478 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3479 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
3480 Ok(())
3481 }
3482 fn name(&self) -> String {
3483 "test_migration".to_string()
3484 }
3485 }
3486
3487 let start_calls = Arc::new(Mutex::new(Vec::new()));
3488 let complete_calls = Arc::new(Mutex::new(Vec::new()));
3489 let error_calls = Arc::new(Mutex::new(Vec::new()));
3490
3491 let start_calls_clone = Arc::clone(&start_calls);
3492 let complete_calls_clone = Arc::clone(&complete_calls);
3493 let error_calls_clone = Arc::clone(&error_calls);
3494
3495 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)])
3496 .on_migration_start(move |version, name| {
3497 start_calls_clone
3498 .lock()
3499 .unwrap()
3500 .push((version, name.to_string()));
3501 })
3502 .on_migration_complete(move |version, name, duration| {
3503 complete_calls_clone
3504 .lock()
3505 .unwrap()
3506 .push((version, name.to_string(), duration));
3507 })
3508 .on_migration_error(move |version, name, error| {
3509 error_calls_clone.lock().unwrap().push((
3510 version,
3511 name.to_string(),
3512 format!("{:?}", error),
3513 ));
3514 });
3515
3516 migrator.upgrade(&mut conn).unwrap();
3517
3518 let starts = start_calls.lock().unwrap();
3520 assert_eq!(starts.len(), 1);
3521 assert_eq!(starts[0], (1, "test_migration".to_string()));
3522
3523 let completes = complete_calls.lock().unwrap();
3524 assert_eq!(completes.len(), 1);
3525 assert_eq!(completes[0].0, 1);
3526 assert_eq!(completes[0].1, "test_migration");
3527 let _ = completes[0].2;
3529
3530 let errors = error_calls.lock().unwrap();
3531 assert_eq!(errors.len(), 0); }
3533
3534 #[test]
3535 fn hooks_are_called_on_failed_migration() {
3536 use std::sync::{Arc, Mutex};
3537
3538 let mut conn = Connection::open_in_memory().unwrap();
3539
3540 struct Migration1;
3541 impl Migration for Migration1 {
3542 fn version(&self) -> u32 {
3543 1
3544 }
3545 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3546 tx.execute("INVALID SQL", [])?;
3547 Ok(())
3548 }
3549 fn name(&self) -> String {
3550 "failing_migration".to_string()
3551 }
3552 }
3553
3554 let start_calls = Arc::new(Mutex::new(Vec::new()));
3555 let complete_calls = Arc::new(Mutex::new(Vec::new()));
3556 let error_calls = Arc::new(Mutex::new(Vec::new()));
3557
3558 let start_calls_clone = Arc::clone(&start_calls);
3559 let complete_calls_clone = Arc::clone(&complete_calls);
3560 let error_calls_clone = Arc::clone(&error_calls);
3561
3562 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)])
3563 .on_migration_start(move |version, name| {
3564 start_calls_clone
3565 .lock()
3566 .unwrap()
3567 .push((version, name.to_string()));
3568 })
3569 .on_migration_complete(move |version, name, duration| {
3570 complete_calls_clone
3571 .lock()
3572 .unwrap()
3573 .push((version, name.to_string(), duration));
3574 })
3575 .on_migration_error(move |version, name, _error| {
3576 error_calls_clone
3577 .lock()
3578 .unwrap()
3579 .push((version, name.to_string()));
3580 });
3581
3582 let _ = migrator.upgrade(&mut conn); let starts = start_calls.lock().unwrap();
3586 assert_eq!(starts.len(), 1);
3587 assert_eq!(starts[0], (1, "failing_migration".to_string()));
3588
3589 let completes = complete_calls.lock().unwrap();
3590 assert_eq!(completes.len(), 0); let errors = error_calls.lock().unwrap();
3593 assert_eq!(errors.len(), 1);
3594 assert_eq!(errors[0], (1, "failing_migration".to_string()));
3595 }
3596
3597 #[test]
3598 fn hooks_are_called_on_downgrade() {
3599 use std::sync::{Arc, Mutex};
3600
3601 let mut conn = Connection::open_in_memory().unwrap();
3602
3603 struct Migration1;
3604 impl Migration for Migration1 {
3605 fn version(&self) -> u32 {
3606 1
3607 }
3608 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3609 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
3610 Ok(())
3611 }
3612 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3613 tx.execute("DROP TABLE test", [])?;
3614 Ok(())
3615 }
3616 fn name(&self) -> String {
3617 "test_migration".to_string()
3618 }
3619 }
3620
3621 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
3623 migrator.upgrade(&mut conn).unwrap();
3624
3625 let start_calls = Arc::new(Mutex::new(Vec::new()));
3627 let complete_calls = Arc::new(Mutex::new(Vec::new()));
3628
3629 let start_calls_clone = Arc::clone(&start_calls);
3630 let complete_calls_clone = Arc::clone(&complete_calls);
3631
3632 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)])
3633 .on_migration_start(move |version, name| {
3634 start_calls_clone
3635 .lock()
3636 .unwrap()
3637 .push((version, name.to_string()));
3638 })
3639 .on_migration_complete(move |version, name, duration| {
3640 complete_calls_clone
3641 .lock()
3642 .unwrap()
3643 .push((version, name.to_string(), duration));
3644 });
3645
3646 migrator.downgrade(&mut conn, 0).unwrap();
3647
3648 let starts = start_calls.lock().unwrap();
3650 assert_eq!(starts.len(), 1);
3651 assert_eq!(starts[0], (1, "test_migration".to_string()));
3652
3653 let completes = complete_calls.lock().unwrap();
3654 assert_eq!(completes.len(), 1);
3655 assert_eq!(completes[0].0, 1);
3656 assert_eq!(completes[0].1, "test_migration");
3657 }
3658
3659 #[test]
3660 #[cfg(feature = "tracing")]
3661 fn tracing_logs_successful_migration() {
3662 use tracing_test::traced_test;
3663
3664 #[traced_test]
3665 fn run_test() {
3666 let mut conn = Connection::open_in_memory().unwrap();
3667
3668 struct Migration1;
3669 impl Migration for Migration1 {
3670 fn version(&self) -> u32 {
3671 1
3672 }
3673 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3674 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
3675 Ok(())
3676 }
3677 fn name(&self) -> String {
3678 "test_migration".to_string()
3679 }
3680 }
3681
3682 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
3683 migrator.upgrade(&mut conn).unwrap();
3684
3685 assert!(logs_contain("Starting migration"));
3687 assert!(logs_contain("Migration completed successfully"));
3688 assert!(logs_contain("duration_ms"));
3689 }
3690
3691 run_test();
3692 }
3693
3694 #[test]
3695 #[cfg(feature = "tracing")]
3696 fn tracing_logs_failed_migration() {
3697 use tracing_test::traced_test;
3698
3699 #[traced_test]
3700 fn run_test() {
3701 let mut conn = Connection::open_in_memory().unwrap();
3702
3703 struct Migration1;
3704 impl Migration for Migration1 {
3705 fn version(&self) -> u32 {
3706 1
3707 }
3708 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3709 tx.execute("INVALID SQL", [])?;
3710 Ok(())
3711 }
3712 fn name(&self) -> String {
3713 "failing_migration".to_string()
3714 }
3715 }
3716
3717 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
3718 let _ = migrator.upgrade(&mut conn);
3719
3720 assert!(logs_contain("Starting migration"));
3722 assert!(logs_contain("Migration failed"));
3723 }
3724
3725 run_test();
3726 }
3727
3728 #[test]
3729 #[cfg(feature = "tracing")]
3730 fn tracing_logs_downgrade() {
3731 use tracing_test::traced_test;
3732
3733 #[traced_test]
3734 fn run_test() {
3735 let mut conn = Connection::open_in_memory().unwrap();
3736
3737 struct Migration1;
3738 impl Migration for Migration1 {
3739 fn version(&self) -> u32 {
3740 1
3741 }
3742 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3743 tx.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)", [])?;
3744 Ok(())
3745 }
3746 fn down(&self, tx: &Transaction) -> Result<(), Error> {
3747 tx.execute("DROP TABLE test", [])?;
3748 Ok(())
3749 }
3750 fn name(&self) -> String {
3751 "test_migration".to_string()
3752 }
3753 }
3754
3755 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
3756 migrator.upgrade(&mut conn).unwrap();
3757 migrator.downgrade(&mut conn, 0).unwrap();
3758
3759 assert!(logs_contain("Rolling back migration"));
3761 assert!(logs_contain("Migration rolled back successfully"));
3762 }
3763
3764 run_test();
3765 }
3766
3767 #[test]
3768 fn precondition_already_satisfied() {
3769 use std::sync::{Arc, Mutex};
3770
3771 let mut conn = Connection::open_in_memory().unwrap();
3772
3773 conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY)", [])
3775 .unwrap();
3776
3777 let up_called = Arc::new(Mutex::new(false));
3779 let up_called_clone = Arc::clone(&up_called);
3780
3781 struct Migration1 {
3782 up_called: Arc<Mutex<bool>>,
3783 }
3784 impl Migration for Migration1 {
3785 fn version(&self) -> u32 {
3786 1
3787 }
3788 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3789 *self.up_called.lock().unwrap() = true;
3791 tx.execute("CREATE TABLE users (id INTEGER PRIMARY KEY)", [])?;
3792 Ok(())
3793 }
3794 fn precondition(&self, tx: &Transaction) -> Result<Precondition, Error> {
3795 let mut stmt = tx.prepare(
3797 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='users'",
3798 )?;
3799 let count: i64 = stmt.query_row([], |row| row.get(0))?;
3800
3801 if count > 0 {
3802 Ok(Precondition::AlreadySatisfied)
3803 } else {
3804 Ok(Precondition::NeedsApply)
3805 }
3806 }
3807 }
3808
3809 let migrator = SqliteMigrator::new(vec![Box::new(Migration1 {
3810 up_called: up_called_clone,
3811 })]);
3812
3813 let report = migrator.upgrade(&mut conn).unwrap();
3814
3815 assert_eq!(report.migrations_run, vec![1]);
3817 assert!(report.failing_migration.is_none());
3818
3819 assert!(!*up_called.lock().unwrap());
3821
3822 let version = migrator.get_current_version(&mut conn).unwrap();
3824 assert_eq!(version, 1);
3825
3826 let table_exists: i64 = conn
3828 .query_row(
3829 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='users'",
3830 [],
3831 |row| row.get(0),
3832 )
3833 .unwrap();
3834 assert_eq!(table_exists, 1);
3835 }
3836
3837 #[test]
3838 fn precondition_needs_apply() {
3839 use std::sync::{Arc, Mutex};
3840
3841 let mut conn = Connection::open_in_memory().unwrap();
3842
3843 let up_called = Arc::new(Mutex::new(false));
3845 let up_called_clone = Arc::clone(&up_called);
3846
3847 struct Migration1 {
3848 up_called: Arc<Mutex<bool>>,
3849 }
3850 impl Migration for Migration1 {
3851 fn version(&self) -> u32 {
3852 1
3853 }
3854 fn up(&self, tx: &Transaction) -> Result<(), Error> {
3855 *self.up_called.lock().unwrap() = true;
3857 tx.execute("CREATE TABLE users (id INTEGER PRIMARY KEY)", [])?;
3858 Ok(())
3859 }
3860 fn precondition(&self, tx: &Transaction) -> Result<Precondition, Error> {
3861 let mut stmt = tx.prepare(
3863 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='users'",
3864 )?;
3865 let count: i64 = stmt.query_row([], |row| row.get(0))?;
3866
3867 if count > 0 {
3868 Ok(Precondition::AlreadySatisfied)
3869 } else {
3870 Ok(Precondition::NeedsApply)
3871 }
3872 }
3873 }
3874
3875 let migrator = SqliteMigrator::new(vec![Box::new(Migration1 {
3876 up_called: up_called_clone,
3877 })]);
3878
3879 let report = migrator.upgrade(&mut conn).unwrap();
3880
3881 assert_eq!(report.migrations_run, vec![1]);
3883 assert!(report.failing_migration.is_none());
3884
3885 assert!(*up_called.lock().unwrap());
3887
3888 let version = migrator.get_current_version(&mut conn).unwrap();
3890 assert_eq!(version, 1);
3891
3892 let table_exists: i64 = conn
3894 .query_row(
3895 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='users'",
3896 [],
3897 |row| row.get(0),
3898 )
3899 .unwrap();
3900 assert_eq!(table_exists, 1);
3901 }
3902
3903 #[test]
3904 fn precondition_error() {
3905 let mut conn = Connection::open_in_memory().unwrap();
3906
3907 struct Migration1;
3908 impl Migration for Migration1 {
3909 fn version(&self) -> u32 {
3910 1
3911 }
3912 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
3913 Ok(())
3914 }
3915 fn precondition(&self, _tx: &Transaction) -> Result<Precondition, Error> {
3916 Err(rusqlite::Error::InvalidQuery.into())
3918 }
3919 }
3920
3921 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)]);
3922 let report = migrator.upgrade(&mut conn).unwrap();
3923
3924 assert_eq!(report.migrations_run, vec![]);
3926 assert!(report.failing_migration.is_some());
3927
3928 let failure = report.failing_migration.unwrap();
3929 assert_eq!(failure.migration().version(), 1);
3930
3931 let version = migrator.get_current_version(&mut conn).unwrap();
3933 assert_eq!(version, 0);
3934 }
3935
3936 #[test]
3937 fn precondition_hooks_already_satisfied() {
3938 use std::sync::{Arc, Mutex};
3939
3940 let mut conn = Connection::open_in_memory().unwrap();
3941
3942 conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY)", [])
3944 .unwrap();
3945
3946 let events = Arc::new(Mutex::new(Vec::new()));
3948 let events_clone1 = Arc::clone(&events);
3949 let events_clone2 = Arc::clone(&events);
3950 let events_clone3 = Arc::clone(&events);
3951 let events_clone4 = Arc::clone(&events);
3952
3953 struct Migration1;
3954 impl Migration for Migration1 {
3955 fn version(&self) -> u32 {
3956 1
3957 }
3958 fn up(&self, _tx: &Transaction) -> Result<(), Error> {
3959 panic!("up() should not be called when precondition is AlreadySatisfied");
3961 }
3962 fn precondition(&self, tx: &Transaction) -> Result<Precondition, Error> {
3963 let mut stmt = tx.prepare(
3965 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='users'",
3966 )?;
3967 let count: i64 = stmt.query_row([], |row| row.get(0))?;
3968
3969 if count > 0 {
3970 Ok(Precondition::AlreadySatisfied)
3971 } else {
3972 Ok(Precondition::NeedsApply)
3973 }
3974 }
3975 }
3976
3977 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)])
3978 .on_migration_start(move |version, name| {
3979 events_clone1
3980 .lock()
3981 .unwrap()
3982 .push(format!("start:{}:{}", version, name));
3983 })
3984 .on_migration_skipped(move |version, name| {
3985 events_clone2
3986 .lock()
3987 .unwrap()
3988 .push(format!("skipped:{}:{}", version, name));
3989 })
3990 .on_migration_complete(move |version, name, _duration| {
3991 events_clone3
3992 .lock()
3993 .unwrap()
3994 .push(format!("complete:{}:{}", version, name));
3995 })
3996 .on_migration_error(move |version, name, _error| {
3997 events_clone4
3998 .lock()
3999 .unwrap()
4000 .push(format!("error:{}:{}", version, name));
4001 });
4002
4003 let report = migrator.upgrade(&mut conn).unwrap();
4004
4005 assert_eq!(report.migrations_run, vec![1]);
4007 assert!(report.failing_migration.is_none());
4008
4009 let events_vec = events.lock().unwrap();
4011 assert_eq!(events_vec.len(), 3);
4012 assert_eq!(events_vec[0], "start:1:Migration 1");
4013 assert_eq!(events_vec[1], "skipped:1:Migration 1");
4014 assert_eq!(events_vec[2], "complete:1:Migration 1");
4015 }
4016
4017 #[test]
4018 fn precondition_hooks_needs_apply() {
4019 use std::sync::{Arc, Mutex};
4020
4021 let mut conn = Connection::open_in_memory().unwrap();
4022
4023 let events = Arc::new(Mutex::new(Vec::new()));
4025 let events_clone1 = Arc::clone(&events);
4026 let events_clone2 = Arc::clone(&events);
4027 let events_clone3 = Arc::clone(&events);
4028 let events_clone4 = Arc::clone(&events);
4029
4030 struct Migration1;
4031 impl Migration for Migration1 {
4032 fn version(&self) -> u32 {
4033 1
4034 }
4035 fn up(&self, tx: &Transaction) -> Result<(), Error> {
4036 tx.execute("CREATE TABLE users (id INTEGER PRIMARY KEY)", [])?;
4037 Ok(())
4038 }
4039 fn precondition(&self, tx: &Transaction) -> Result<Precondition, Error> {
4040 let mut stmt = tx.prepare(
4042 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='users'",
4043 )?;
4044 let count: i64 = stmt.query_row([], |row| row.get(0))?;
4045
4046 if count > 0 {
4047 Ok(Precondition::AlreadySatisfied)
4048 } else {
4049 Ok(Precondition::NeedsApply)
4050 }
4051 }
4052 }
4053
4054 let migrator = SqliteMigrator::new(vec![Box::new(Migration1)])
4055 .on_migration_start(move |version, name| {
4056 events_clone1
4057 .lock()
4058 .unwrap()
4059 .push(format!("start:{}:{}", version, name));
4060 })
4061 .on_migration_skipped(move |version, name| {
4062 events_clone2
4063 .lock()
4064 .unwrap()
4065 .push(format!("skipped:{}:{}", version, name));
4066 })
4067 .on_migration_complete(move |version, name, _duration| {
4068 events_clone3
4069 .lock()
4070 .unwrap()
4071 .push(format!("complete:{}:{}", version, name));
4072 })
4073 .on_migration_error(move |version, name, _error| {
4074 events_clone4
4075 .lock()
4076 .unwrap()
4077 .push(format!("error:{}:{}", version, name));
4078 });
4079
4080 let report = migrator.upgrade(&mut conn).unwrap();
4081
4082 assert_eq!(report.migrations_run, vec![1]);
4084 assert!(report.failing_migration.is_none());
4085
4086 let events_vec = events.lock().unwrap();
4088 assert_eq!(events_vec.len(), 2);
4089 assert_eq!(events_vec[0], "start:1:Migration 1");
4090 assert_eq!(events_vec[1], "complete:1:Migration 1");
4091 }
4092
4093 #[test]
4094 fn precondition_mixed_migrations() {
4095 use std::sync::{Arc, Mutex};
4096
4097 let mut conn = Connection::open_in_memory().unwrap();
4098
4099 conn.execute("CREATE TABLE table1 (id INTEGER PRIMARY KEY)", [])
4101 .unwrap();
4102
4103 let up_calls = Arc::new(Mutex::new(Vec::new()));
4105 let up_calls_clone1 = Arc::clone(&up_calls);
4106 let up_calls_clone2 = Arc::clone(&up_calls);
4107 let up_calls_clone3 = Arc::clone(&up_calls);
4108
4109 struct Migration1 {
4110 up_calls: Arc<Mutex<Vec<u32>>>,
4111 }
4112 impl Migration for Migration1 {
4113 fn version(&self) -> u32 {
4114 1
4115 }
4116 fn up(&self, tx: &Transaction) -> Result<(), Error> {
4117 self.up_calls.lock().unwrap().push(1);
4118 tx.execute("CREATE TABLE table1 (id INTEGER PRIMARY KEY)", [])?;
4119 Ok(())
4120 }
4121 fn precondition(&self, tx: &Transaction) -> Result<Precondition, Error> {
4122 let mut stmt = tx.prepare(
4123 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='table1'",
4124 )?;
4125 let count: i64 = stmt.query_row([], |row| row.get(0))?;
4126 Ok(if count > 0 {
4127 Precondition::AlreadySatisfied
4128 } else {
4129 Precondition::NeedsApply
4130 })
4131 }
4132 }
4133
4134 struct Migration2 {
4135 up_calls: Arc<Mutex<Vec<u32>>>,
4136 }
4137 impl Migration for Migration2 {
4138 fn version(&self) -> u32 {
4139 2
4140 }
4141 fn up(&self, tx: &Transaction) -> Result<(), Error> {
4142 self.up_calls.lock().unwrap().push(2);
4143 tx.execute("CREATE TABLE table2 (id INTEGER PRIMARY KEY)", [])?;
4144 Ok(())
4145 }
4146 fn precondition(&self, tx: &Transaction) -> Result<Precondition, Error> {
4147 let mut stmt = tx.prepare(
4148 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='table2'",
4149 )?;
4150 let count: i64 = stmt.query_row([], |row| row.get(0))?;
4151 Ok(if count > 0 {
4152 Precondition::AlreadySatisfied
4153 } else {
4154 Precondition::NeedsApply
4155 })
4156 }
4157 }
4158
4159 struct Migration3 {
4160 up_calls: Arc<Mutex<Vec<u32>>>,
4161 }
4162 impl Migration for Migration3 {
4163 fn version(&self) -> u32 {
4164 3
4165 }
4166 fn up(&self, tx: &Transaction) -> Result<(), Error> {
4167 self.up_calls.lock().unwrap().push(3);
4168 tx.execute("CREATE TABLE table3 (id INTEGER PRIMARY KEY)", [])?;
4169 Ok(())
4170 }
4171 fn precondition(&self, tx: &Transaction) -> Result<Precondition, Error> {
4172 let mut stmt = tx.prepare(
4173 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='table3'",
4174 )?;
4175 let count: i64 = stmt.query_row([], |row| row.get(0))?;
4176 Ok(if count > 0 {
4177 Precondition::AlreadySatisfied
4178 } else {
4179 Precondition::NeedsApply
4180 })
4181 }
4182 }
4183
4184 let migrator = SqliteMigrator::new(vec![
4185 Box::new(Migration1 {
4186 up_calls: up_calls_clone1,
4187 }),
4188 Box::new(Migration2 {
4189 up_calls: up_calls_clone2,
4190 }),
4191 Box::new(Migration3 {
4192 up_calls: up_calls_clone3,
4193 }),
4194 ]);
4195
4196 let report = migrator.upgrade(&mut conn).unwrap();
4197
4198 assert_eq!(report.migrations_run, vec![1, 2, 3]);
4200 assert!(report.failing_migration.is_none());
4201
4202 let calls = up_calls.lock().unwrap();
4204 assert_eq!(*calls, vec![2, 3]);
4205
4206 let table_count: i64 = conn
4208 .query_row(
4209 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name IN ('table1', 'table2', 'table3')",
4210 [],
4211 |row| row.get(0),
4212 )
4213 .unwrap();
4214 assert_eq!(table_count, 3);
4215
4216 let version = migrator.get_current_version(&mut conn).unwrap();
4218 assert_eq!(version, 3);
4219 }
4220}