geekorm_core/migrations/
validate.rs1use crate::error::MigrationError;
3use crate::{Database, backends::TableInfo};
4
5use super::MigrationState;
6
7#[derive(Debug)]
9pub struct Validator {
10 pub errors: Vec<MigrationError>,
12 pub quick: bool,
14}
15
16pub fn validate_database(
20 database_tables: &super::DatabaseTables,
21 migration_database: &Database,
22 validator: &mut Validator,
23) -> Result<MigrationState, crate::Error> {
24 let mut state = MigrationState::UpToDate;
25 #[cfg(feature = "log")]
26 {
27 log::debug!("Validating database schema");
28 }
29
30 if database_tables.is_empty() || migration_database.tables.is_empty() {
32 return Ok(MigrationState::Initialized);
33 }
34 if database_tables.len() != migration_database.tables.len() {
36 #[cfg(feature = "log")]
37 {
38 log::info!("Database Tables :: {:?}", database_tables);
39 log::info!(
40 "Migration Tables :: {:?}",
41 migration_database.get_table_names()
42 );
43 }
44 state = MigrationState::OutOfDate("Table count mismatch".to_string());
45 if validator.quick {
46 return Ok(state);
47 }
48 }
49
50 for (name, table) in database_tables {
52 if let Some(mtable) = migration_database.get_table(name.as_str()) {
53 if mtable.columns.len() != table.len() {
55 state = MigrationState::OutOfDate("Column count mismatch".to_string());
56 }
57
58 for dbcolumn in table {
59 #[cfg(feature = "log")]
60 {
61 log::debug!("Columns :: {:?}", dbcolumn);
62 }
63 if let Some(mcolumn) = mtable.columns.get(dbcolumn.name.as_str()) {
64 match validate_column(name, dbcolumn, mcolumn, &mut validator.errors) {
65 MigrationState::UpToDate | MigrationState::Initialized => {}
66 MigrationState::OutOfDate(reason) => {
67 state = MigrationState::OutOfDate(reason);
68 if validator.quick {
69 return Ok(state);
70 }
71 }
72 }
73 } else {
74 validator.errors.push(MigrationError::MissingColumn {
75 table: name.to_string(),
76 column: dbcolumn.name.to_string(),
77 });
78
79 state = MigrationState::OutOfDate(format!(
80 "Column not found: {}.{}",
81 name, dbcolumn.name
82 ));
83 if validator.quick {
84 return Ok(state);
85 }
86 }
87 }
88
89 for mcolumn in mtable.columns.columns.iter() {
91 #[cfg(feature = "log")]
92 {
93 log::debug!("Migration Columns :: {:?}", mcolumn);
94 }
95 if let Some(dbcolumn) = table.iter().find(|c| c.name == mcolumn.name) {
96 match validate_column(name, dbcolumn, mcolumn, &mut validator.errors) {
97 MigrationState::UpToDate | MigrationState::Initialized => {}
98 MigrationState::OutOfDate(reason) => {
99 state = MigrationState::OutOfDate(reason);
100 if validator.quick {
101 return Ok(state);
102 }
103 }
104 }
105 } else {
106 validator.errors.push(MigrationError::MissingColumn {
107 table: name.to_string(),
108 column: mcolumn.name.to_string(),
109 });
110 state = MigrationState::OutOfDate(format!(
111 "Column not found: {}.{}",
112 name, mcolumn.name
113 ));
114 if validator.quick {
115 return Ok(state);
116 }
117 }
118 }
119 } else {
120 validator
121 .errors
122 .push(MigrationError::MissingTable(name.to_string()));
123 state = MigrationState::OutOfDate(format!("Table not found: {}", name));
125 if validator.quick {
126 return Ok(state);
127 }
128 }
129 }
130 Ok(state)
131}
132
133fn validate_column(
137 table: &String,
138 dbcolumn: &TableInfo,
139 column: &crate::Column,
140 errors: &mut Vec<MigrationError>,
141) -> MigrationState {
142 let mut state = MigrationState::UpToDate;
143
144 if column.is_primary_key() && dbcolumn.pk != 1 {
146 errors.push(MigrationError::ColumnTypeMismatch {
147 table: table.to_string(),
148 column: column.name.clone(),
149 feature: "primary-key".to_string(),
150 });
151
152 state = MigrationState::OutOfDate("Primary key constraint not set".to_string());
153 }
154 if column.is_not_null() && dbcolumn.notnull == 0 {
156 errors.push(MigrationError::ColumnTypeMismatch {
157 table: table.to_string(),
158 column: column.name.clone(),
159 feature: "not-null".to_string(),
160 });
161 }
162
163 state
164}