Skip to content

Commit 4cd051d

Browse files
committed
Support sqlite enum
1 parent c527f9f commit 4cd051d

File tree

2 files changed

+48
-31
lines changed

2 files changed

+48
-31
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"changes":{"crates/vespertide-loader/Cargo.toml":"Patch","crates/vespertide-macro/Cargo.toml":"Patch","crates/vespertide-query/Cargo.toml":"Patch","crates/vespertide-core/Cargo.toml":"Patch","crates/vespertide-planner/Cargo.toml":"Patch","crates/vespertide/Cargo.toml":"Patch","crates/vespertide-cli/Cargo.toml":"Patch","crates/vespertide-exporter/Cargo.toml":"Patch","crates/vespertide-config/Cargo.toml":"Patch"},"note":"Fix ordering on diff, sql commands, Support enum on sqlite","date":"2025-12-18T06:48:29.591923100Z"}

crates/vespertide-planner/src/diff.rs

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::{HashMap, HashSet, VecDeque};
1+
use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque};
22

33
use 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

Comments
 (0)