1- use std:: collections:: { HashMap , HashSet , VecDeque } ;
1+ use std:: collections:: { BTreeMap , BTreeSet , HashSet , VecDeque } ;
22
33use vespertide_core:: { MigrationAction , MigrationPlan , TableConstraint , TableDef } ;
44
@@ -16,7 +16,8 @@ fn topological_sort_tables<'a>(tables: &[&'a TableDef]) -> Result<Vec<&'a TableD
1616 let table_names: HashSet < & str > = tables. iter ( ) . map ( |t| t. name . as_str ( ) ) . collect ( ) ;
1717
1818 // Build adjacency list: for each table, list the tables it depends on (via FK)
19- let mut dependencies: HashMap < & str , Vec < & str > > = HashMap :: new ( ) ;
19+ // Use BTreeMap for consistent ordering
20+ let mut dependencies: BTreeMap < & str , Vec < & str > > = BTreeMap :: new ( ) ;
2021 for table in tables {
2122 let mut deps = Vec :: new ( ) ;
2223 for constraint in & table. constraints {
@@ -32,7 +33,8 @@ fn topological_sort_tables<'a>(tables: &[&'a TableDef]) -> Result<Vec<&'a TableD
3233
3334 // Kahn's algorithm for topological sort
3435 // Calculate in-degrees (number of tables that depend on each table)
35- let mut in_degree: HashMap < & str , usize > = HashMap :: new ( ) ;
36+ // Use BTreeMap for consistent ordering
37+ let mut in_degree: BTreeMap < & str , usize > = BTreeMap :: new ( ) ;
3638 for table in tables {
3739 in_degree. entry ( table. name . as_str ( ) ) . or_insert ( 0 ) ;
3840 }
@@ -49,33 +51,38 @@ fn topological_sort_tables<'a>(tables: &[&'a TableDef]) -> Result<Vec<&'a TableD
4951 }
5052
5153 // Start with tables that have no dependencies
52- let mut queue : VecDeque < & str > = VecDeque :: new ( ) ;
53- for table in tables {
54- if in_degree . get ( table . name . as_str ( ) ) == Some ( & 0 ) {
55- queue . push_back ( table . name . as_str ( ) ) ;
56- }
57- }
54+ // BTreeMap iteration is already sorted by key
55+ let mut queue : VecDeque < & str > = in_degree
56+ . iter ( )
57+ . filter ( | ( _ , deg ) | * * deg == 0 )
58+ . map ( | ( name , _ ) | * name )
59+ . collect ( ) ;
5860
5961 let mut result: Vec < & TableDef > = Vec :: new ( ) ;
60- let table_map: HashMap < & str , & TableDef > =
62+ let table_map: BTreeMap < & str , & TableDef > =
6163 tables. iter ( ) . map ( |t| ( t. name . as_str ( ) , * t) ) . collect ( ) ;
6264
6365 while let Some ( table_name) = queue. pop_front ( ) {
6466 if let Some ( & table) = table_map. get ( table_name) {
6567 result. push ( table) ;
6668 }
6769
68- // For each table that depends on this one, decrement its in-degree
70+ // Collect tables that become ready (in-degree becomes 0)
71+ // Use BTreeSet for consistent ordering
72+ let mut ready_tables: BTreeSet < & str > = BTreeSet :: new ( ) ;
6973 for ( dependent, deps) in & dependencies {
7074 if deps. contains ( & table_name)
7175 && let Some ( degree) = in_degree. get_mut ( dependent)
7276 {
7377 * degree -= 1 ;
7478 if * degree == 0 {
75- queue . push_back ( dependent) ;
79+ ready_tables . insert ( dependent) ;
7680 }
7781 }
7882 }
83+ for t in ready_tables {
84+ queue. push_back ( t) ;
85+ }
7986 }
8087
8188 // Check for cycles
@@ -105,7 +112,7 @@ fn extract_delete_table_name(action: &MigrationAction) -> &str {
105112 }
106113}
107114
108- fn sort_delete_tables ( actions : & mut [ MigrationAction ] , all_tables : & HashMap < & str , & TableDef > ) {
115+ fn sort_delete_tables ( actions : & mut [ MigrationAction ] , all_tables : & BTreeMap < & str , & TableDef > ) {
109116 // Collect DeleteTable actions and their indices
110117 let delete_indices: Vec < usize > = actions
111118 . iter ( )
@@ -124,14 +131,16 @@ fn sort_delete_tables(actions: &mut [MigrationAction], all_tables: &HashMap<&str
124131 }
125132
126133 // Extract table names being deleted
127- let delete_table_names: HashSet < & str > = delete_indices
134+ // Use BTreeSet for consistent ordering
135+ let delete_table_names: BTreeSet < & str > = delete_indices
128136 . iter ( )
129137 . map ( |& i| extract_delete_table_name ( & actions[ i] ) )
130138 . collect ( ) ;
131139
132140 // Build dependency graph for tables being deleted
133141 // dependencies[A] = [B] means A has FK referencing B
134- let mut dependencies: HashMap < & str , Vec < & str > > = HashMap :: new ( ) ;
142+ // Use BTreeMap for consistent ordering
143+ let mut dependencies: BTreeMap < & str , Vec < & str > > = BTreeMap :: new ( ) ;
135144 for & table_name in & delete_table_names {
136145 let mut deps = Vec :: new ( ) ;
137146 if let Some ( table_def) = all_tables. get ( table_name) {
@@ -149,7 +158,8 @@ fn sort_delete_tables(actions: &mut [MigrationAction], all_tables: &HashMap<&str
149158
150159 // Use Kahn's algorithm for topological sort
151160 // in_degree[A] = number of tables A depends on
152- let mut in_degree: HashMap < & str , usize > = HashMap :: new ( ) ;
161+ // Use BTreeMap for consistent ordering
162+ let mut in_degree: BTreeMap < & str , usize > = BTreeMap :: new ( ) ;
153163 for & table_name in & delete_table_names {
154164 in_degree. insert (
155165 table_name,
@@ -158,28 +168,33 @@ fn sort_delete_tables(actions: &mut [MigrationAction], all_tables: &HashMap<&str
158168 }
159169
160170 // Start with tables that have no dependencies (can be deleted last in creation order)
161- let mut queue : VecDeque < & str > = VecDeque :: new ( ) ;
162- for & table_name in & delete_table_names {
163- if in_degree . get ( table_name ) == Some ( & 0 ) {
164- queue . push_back ( table_name ) ;
165- }
166- }
171+ // BTreeMap iteration is already sorted
172+ let mut queue : VecDeque < & str > = in_degree
173+ . iter ( )
174+ . filter ( | ( _ , deg ) | * * deg == 0 )
175+ . map ( | ( name , _ ) | * name )
176+ . collect ( ) ;
167177
168178 let mut sorted_tables: Vec < & str > = Vec :: new ( ) ;
169179 while let Some ( table_name) = queue. pop_front ( ) {
170180 sorted_tables. push ( table_name) ;
171181
172182 // For each table that has this one as a dependency, decrement its in-degree
183+ // Use BTreeSet for consistent ordering of newly ready tables
184+ let mut ready_tables: BTreeSet < & str > = BTreeSet :: new ( ) ;
173185 for ( & dependent, deps) in & dependencies {
174186 if deps. contains ( & table_name)
175187 && let Some ( degree) = in_degree. get_mut ( dependent)
176188 {
177189 * degree -= 1 ;
178190 if * degree == 0 {
179- queue . push_back ( dependent) ;
191+ ready_tables . insert ( dependent) ;
180192 }
181193 }
182194 }
195+ for t in ready_tables {
196+ queue. push_back ( t) ;
197+ }
183198 }
184199
185200 // Reverse to get deletion order (tables with dependencies should be deleted first)
@@ -234,11 +249,12 @@ pub fn diff_schemas(from: &[TableDef], to: &[TableDef]) -> Result<MigrationPlan,
234249 } )
235250 . collect :: < Result < Vec < _ > , _ > > ( ) ?;
236251
237- let from_map: HashMap < _ , _ > = from_normalized
252+ // Use BTreeMap for consistent ordering
253+ let from_map: BTreeMap < _ , _ > = from_normalized
238254 . iter ( )
239255 . map ( |t| ( t. name . as_str ( ) , t) )
240256 . collect ( ) ;
241- let to_map: HashMap < _ , _ > = to_normalized. iter ( ) . map ( |t| ( t. name . as_str ( ) , t) ) . collect ( ) ;
257+ let to_map: BTreeMap < _ , _ > = to_normalized. iter ( ) . map ( |t| ( t. name . as_str ( ) , t) ) . collect ( ) ;
242258
243259 // Drop tables that disappeared.
244260 for name in from_map. keys ( ) {
@@ -252,13 +268,13 @@ pub fn diff_schemas(from: &[TableDef], to: &[TableDef]) -> Result<MigrationPlan,
252268 // Update existing tables and their indexes/columns.
253269 for ( name, to_tbl) in & to_map {
254270 if let Some ( from_tbl) = from_map. get ( name) {
255- // Columns
256- let from_cols: HashMap < _ , _ > = from_tbl
271+ // Columns - use BTreeMap for consistent ordering
272+ let from_cols: BTreeMap < _ , _ > = from_tbl
257273 . columns
258274 . iter ( )
259275 . map ( |c| ( c. name . as_str ( ) , c) )
260276 . collect ( ) ;
261- let to_cols: HashMap < _ , _ > = to_tbl
277+ let to_cols: BTreeMap < _ , _ > = to_tbl
262278 . columns
263279 . iter ( )
264280 . map ( |c| ( c. name . as_str ( ) , c) )
@@ -300,13 +316,13 @@ pub fn diff_schemas(from: &[TableDef], to: &[TableDef]) -> Result<MigrationPlan,
300316 }
301317 }
302318
303- // Indexes
304- let from_indexes: HashMap < _ , _ > = from_tbl
319+ // Indexes - use BTreeMap for consistent ordering
320+ let from_indexes: BTreeMap < _ , _ > = from_tbl
305321 . indexes
306322 . iter ( )
307323 . map ( |i| ( i. name . as_str ( ) , i) )
308324 . collect ( ) ;
309- let to_indexes: HashMap < _ , _ > = to_tbl
325+ let to_indexes: BTreeMap < _ , _ > = to_tbl
310326 . indexes
311327 . iter ( )
312328 . map ( |i| ( i. name . as_str ( ) , i) )
0 commit comments