Skip to content

Commit 9d72084

Browse files
committed
feat: handle CREATE/ALTER/DROP TYPE
1 parent d38e54b commit 9d72084

File tree

5 files changed

+433
-36
lines changed

5 files changed

+433
-36
lines changed

Cargo.lock

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ bon = "3.3.2"
2020
camino = "1.1.9"
2121
clap = { version = "4.5.29", features = ["derive"], optional = true }
2222
sqlformat = "0.3.5"
23-
sqlparser = "0.54.0"
23+
sqlparser = { version = "0.54.0", git = "https://github.com/apache/datafusion-sqlparser-rs.git", rev = "c75a99262102da1ac795c4272a640d7e36b0e157" }

src/diff.rs

+197-31
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
1-
use std::collections::HashSet;
1+
use std::{cmp::Ordering, collections::HashSet};
22

3-
use sqlparser::ast::{AlterTableOperation, CreateTable, Statement};
3+
use sqlparser::ast::{
4+
AlterTableOperation, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
5+
AlterTypeOperation, CreateTable, ObjectName, Statement, UserDefinedTypeRepresentation,
6+
};
47

58
pub trait Diff: Sized {
6-
fn diff(&self, other: &Self) -> Option<Self>;
9+
fn diff(&self, other: &Self) -> Option<Vec<Statement>>;
710
}
811

912
impl Diff for Vec<Statement> {
10-
fn diff(&self, other: &Self) -> Option<Self> {
11-
let res: Self = self
13+
fn diff(&self, other: &Self) -> Option<Vec<Statement>> {
14+
let res = self
1215
.iter()
1316
.filter_map(|sa| match sa {
1417
// CreateTable: compare against another CreateTable with the same name
1518
// TODO: handle renames (e.g. use comments to tag a previous name for a table in a schema)
1619
Statement::CreateTable(a) => find_and_compare_create_table(sa, a, other),
20+
Statement::CreateType { name, .. } => find_and_compare_create_type(sa, name, other),
1721
_ => todo!("diff all kinds of statments"),
1822
})
1923
// find resources that are in `other` but not in `self`
@@ -23,12 +27,17 @@ impl Diff for Vec<Statement> {
2327
Statement::CreateTable(a) => a.name == b.name,
2428
_ => false,
2529
}),
30+
Statement::CreateType { name: b_name, .. } => self.iter().find(|sa| match sa {
31+
Statement::CreateType { name: a_name, .. } => a_name == b_name,
32+
_ => false,
33+
}),
2634
_ => todo!("diff all kinds of statements (other)"),
2735
}
2836
// return the statement if it's not in `self`
29-
.map_or_else(|| Some(sb.clone()), |_| None)
37+
.map_or_else(|| Some(vec![sb.clone()]), |_| None)
3038
}))
31-
.collect();
39+
.flatten()
40+
.collect::<Vec<_>>();
3241

3342
if res.is_empty() {
3443
None
@@ -38,47 +47,99 @@ impl Diff for Vec<Statement> {
3847
}
3948
}
4049

50+
fn find_and_compare<MF, DF>(
51+
sa: &Statement,
52+
other: &[Statement],
53+
match_fn: MF,
54+
drop_fn: DF,
55+
) -> Option<Vec<Statement>>
56+
where
57+
MF: Fn(&&Statement) -> bool,
58+
DF: Fn() -> Option<Vec<Statement>>,
59+
{
60+
other.iter().find(match_fn).map_or_else(
61+
// drop the statement if it wasn't found in `other`
62+
drop_fn,
63+
// otherwise diff the two statements
64+
|sb| sa.diff(sb),
65+
)
66+
}
67+
4168
fn find_and_compare_create_table(
4269
sa: &Statement,
4370
a: &CreateTable,
4471
other: &[Statement],
45-
) -> Option<Statement> {
46-
other
47-
.iter()
48-
.find(|sb| match sb {
72+
) -> Option<Vec<Statement>> {
73+
find_and_compare(
74+
sa,
75+
other,
76+
|sb| match sb {
4977
Statement::CreateTable(b) => a.name == b.name,
5078
_ => false,
51-
})
52-
.map_or_else(
53-
|| {
54-
// drop the table if it wasn't found in `other`
55-
Some(Statement::Drop {
56-
object_type: sqlparser::ast::ObjectType::Table,
57-
if_exists: a.if_not_exists,
58-
names: vec![a.name.clone()],
59-
cascade: false,
60-
restrict: false,
61-
purge: false,
62-
temporary: false,
63-
})
64-
},
65-
|sb| sa.diff(sb),
66-
)
79+
},
80+
|| {
81+
Some(vec![Statement::Drop {
82+
object_type: sqlparser::ast::ObjectType::Table,
83+
if_exists: a.if_not_exists,
84+
names: vec![a.name.clone()],
85+
cascade: false,
86+
restrict: false,
87+
purge: false,
88+
temporary: false,
89+
}])
90+
},
91+
)
92+
}
93+
94+
fn find_and_compare_create_type(
95+
sa: &Statement,
96+
a_name: &ObjectName,
97+
other: &[Statement],
98+
) -> Option<Vec<Statement>> {
99+
find_and_compare(
100+
sa,
101+
other,
102+
|sb| match sb {
103+
Statement::CreateType { name: b_name, .. } => a_name == b_name,
104+
_ => false,
105+
},
106+
|| {
107+
Some(vec![Statement::Drop {
108+
object_type: sqlparser::ast::ObjectType::Type,
109+
if_exists: false,
110+
names: vec![a_name.clone()],
111+
cascade: false,
112+
restrict: false,
113+
purge: false,
114+
temporary: false,
115+
}])
116+
},
117+
)
67118
}
68119

69120
impl Diff for Statement {
70-
fn diff(&self, other: &Self) -> Option<Self> {
121+
fn diff(&self, other: &Self) -> Option<Vec<Statement>> {
71122
match self {
72123
Self::CreateTable(a) => match other {
73124
Self::CreateTable(b) => compare_create_table(a, b),
74125
_ => None,
75126
},
127+
Self::CreateType {
128+
name: a_name,
129+
representation: a_rep,
130+
} => match other {
131+
Self::CreateType {
132+
name: b_name,
133+
representation: b_rep,
134+
} => compare_create_type(a_name, a_rep, b_name, b_rep),
135+
_ => None,
136+
},
76137
_ => todo!("implement diff for all `Statement`s"),
77138
}
78139
}
79140
}
80141

81-
fn compare_create_table(a: &CreateTable, b: &CreateTable) -> Option<Statement> {
142+
fn compare_create_table(a: &CreateTable, b: &CreateTable) -> Option<Vec<Statement>> {
82143
if a == b {
83144
return None;
84145
}
@@ -116,12 +177,117 @@ fn compare_create_table(a: &CreateTable, b: &CreateTable) -> Option<Statement> {
116177
}))
117178
.collect();
118179

119-
Some(Statement::AlterTable {
180+
Some(vec![Statement::AlterTable {
120181
name: a.name.clone(),
121182
if_exists: a.if_not_exists,
122183
only: false,
123184
operations: ops,
124185
location: None,
125186
on_cluster: a.on_cluster.clone(),
126-
})
187+
}])
188+
}
189+
190+
fn compare_create_type(
191+
a_name: &ObjectName,
192+
a_rep: &UserDefinedTypeRepresentation,
193+
b_name: &ObjectName,
194+
b_rep: &UserDefinedTypeRepresentation,
195+
) -> Option<Vec<Statement>> {
196+
if a_name == b_name && a_rep == b_rep {
197+
return None;
198+
}
199+
200+
let operations = match a_rep {
201+
UserDefinedTypeRepresentation::Enum { labels: a_labels } => match b_rep {
202+
UserDefinedTypeRepresentation::Enum { labels: b_labels } => {
203+
match a_labels.len().cmp(&b_labels.len()) {
204+
Ordering::Equal => {
205+
let rename_labels: Vec<_> = a_labels
206+
.iter()
207+
.zip(b_labels.iter())
208+
.filter_map(|(a, b)| {
209+
if a == b {
210+
None
211+
} else {
212+
Some(AlterTypeOperation::RenameValue(
213+
sqlparser::ast::AlterTypeRenameValue {
214+
from: a.clone(),
215+
to: b.clone(),
216+
},
217+
))
218+
}
219+
})
220+
.collect();
221+
rename_labels
222+
}
223+
Ordering::Less => {
224+
let mut a_labels_iter = a_labels.iter().peekable();
225+
let mut operations = Vec::new();
226+
let mut prev = None;
227+
for b in b_labels {
228+
match a_labels_iter.peek() {
229+
Some(a) => {
230+
let a = *a;
231+
if a == b {
232+
prev = Some(a);
233+
a_labels_iter.next();
234+
continue;
235+
}
236+
237+
let position = match prev {
238+
Some(a) => AlterTypeAddValuePosition::After(a.clone()),
239+
None => AlterTypeAddValuePosition::Before(a.clone()),
240+
};
241+
242+
prev = Some(b);
243+
operations.push(AlterTypeOperation::AddValue(
244+
AlterTypeAddValue {
245+
if_not_exists: false,
246+
value: b.clone(),
247+
position: Some(position),
248+
},
249+
));
250+
}
251+
None => {
252+
if a_labels.contains(b) {
253+
continue;
254+
}
255+
// labels occuring after all existing ones get added to the end
256+
operations.push(AlterTypeOperation::AddValue(
257+
AlterTypeAddValue {
258+
if_not_exists: false,
259+
value: b.clone(),
260+
position: None,
261+
},
262+
));
263+
}
264+
}
265+
}
266+
operations
267+
}
268+
_ => {
269+
todo!("Handle removing labels from an enum")
270+
}
271+
}
272+
}
273+
_ => todo!("DROP type and CREATE type"),
274+
},
275+
_ => todo!("handle diffing composite attributes for CREATE TYPE"),
276+
};
277+
278+
if operations.is_empty() {
279+
return None;
280+
}
281+
282+
Some(
283+
operations
284+
.into_iter()
285+
.map(|operation| {
286+
Statement::AlterType(AlterType {
287+
name: a_name.clone(),
288+
operation,
289+
})
290+
})
291+
.collect(),
292+
)
127293
}

0 commit comments

Comments
 (0)