| 112 | } |
| 113 | |
| 114 | fn check_table_drift(conn: &Connection, table_name: &str) -> Result<Option<TableDrift>, SchemaDriftError> { |
| 115 | // Get metadata columns |
| 116 | let metadata_columns = Self::get_metadata_columns(conn, table_name)?; |
| 117 | |
| 118 | // Get actual SQLite columns |
| 119 | let sqlite_columns = Self::get_sqlite_columns(conn, table_name)?; |
| 120 | |
| 121 | // Build sets for comparison |
| 122 | let metadata_names: HashSet<String> = metadata_columns.keys().cloned().collect(); |
| 123 | let sqlite_names: HashSet<String> = sqlite_columns.keys().cloned().collect(); |
| 124 | |
| 125 | // Find missing columns |
| 126 | let missing_in_sqlite: Vec<ColumnInfo> = metadata_names |
| 127 | .difference(&sqlite_names) |
| 128 | .map(|name| metadata_columns[name].clone()) |
| 129 | .collect(); |
| 130 | |
| 131 | let missing_in_metadata: Vec<ColumnInfo> = sqlite_names |
| 132 | .difference(&metadata_names) |
| 133 | .map(|name| sqlite_columns[name].clone()) |
| 134 | .collect(); |
| 135 | |
| 136 | // Check type mismatches |
| 137 | let mut type_mismatches = Vec::new(); |
| 138 | for name in metadata_names.intersection(&sqlite_names) { |
| 139 | let metadata_col = &metadata_columns[name]; |
| 140 | let sqlite_col = &sqlite_columns[name]; |
| 141 | |
| 142 | // Compare SQLite types (normalize for comparison) |
| 143 | let metadata_type_normalized = Self::normalize_sqlite_type(&metadata_col.sqlite_type); |
| 144 | let actual_type_normalized = Self::normalize_sqlite_type(&sqlite_col.sqlite_type); |
| 145 | |
| 146 | if metadata_type_normalized != actual_type_normalized { |
| 147 | type_mismatches.push(TypeMismatch { |
| 148 | column_name: name.clone(), |
| 149 | metadata_pg_type: metadata_col.pg_type.clone(), |
| 150 | metadata_sqlite_type: metadata_col.sqlite_type.clone(), |
| 151 | actual_sqlite_type: sqlite_col.sqlite_type.clone(), |
| 152 | }); |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | // Only return drift if there are actual differences |
| 157 | if missing_in_sqlite.is_empty() && missing_in_metadata.is_empty() && type_mismatches.is_empty() { |
| 158 | Ok(None) |
| 159 | } else { |
| 160 | Ok(Some(TableDrift { |
| 161 | table_name: table_name.to_string(), |
| 162 | missing_in_sqlite, |
| 163 | missing_in_metadata, |
| 164 | type_mismatches, |
| 165 | })) |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | fn get_metadata_columns(conn: &Connection, table_name: &str) -> Result<HashMap<String, ColumnInfo>, rusqlite::Error> { |
| 170 | let mut stmt = conn.prepare( |