1
- use std:: collections:: HashSet ;
1
+ use std:: { cmp :: Ordering , collections:: HashSet } ;
2
2
3
- use sqlparser:: ast:: { AlterTableOperation , CreateTable , Statement } ;
3
+ use sqlparser:: ast:: {
4
+ AlterTableOperation , AlterType , AlterTypeAddValue , AlterTypeAddValuePosition ,
5
+ AlterTypeOperation , CreateTable , ObjectName , Statement , UserDefinedTypeRepresentation ,
6
+ } ;
4
7
5
8
pub trait Diff : Sized {
6
- fn diff ( & self , other : & Self ) -> Option < Self > ;
9
+ fn diff ( & self , other : & Self ) -> Option < Vec < Statement > > ;
7
10
}
8
11
9
12
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
12
15
. iter ( )
13
16
. filter_map ( |sa| match sa {
14
17
// CreateTable: compare against another CreateTable with the same name
15
18
// TODO: handle renames (e.g. use comments to tag a previous name for a table in a schema)
16
19
Statement :: CreateTable ( a) => find_and_compare_create_table ( sa, a, other) ,
20
+ Statement :: CreateType { name, .. } => find_and_compare_create_type ( sa, name, other) ,
17
21
_ => todo ! ( "diff all kinds of statments" ) ,
18
22
} )
19
23
// find resources that are in `other` but not in `self`
@@ -23,12 +27,17 @@ impl Diff for Vec<Statement> {
23
27
Statement :: CreateTable ( a) => a. name == b. name ,
24
28
_ => false ,
25
29
} ) ,
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
+ } ) ,
26
34
_ => todo ! ( "diff all kinds of statements (other)" ) ,
27
35
}
28
36
// 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 )
30
38
} ) )
31
- . collect ( ) ;
39
+ . flatten ( )
40
+ . collect :: < Vec < _ > > ( ) ;
32
41
33
42
if res. is_empty ( ) {
34
43
None
@@ -38,47 +47,99 @@ impl Diff for Vec<Statement> {
38
47
}
39
48
}
40
49
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
+
41
68
fn find_and_compare_create_table (
42
69
sa : & Statement ,
43
70
a : & CreateTable ,
44
71
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 {
49
77
Statement :: CreateTable ( b) => a. name == b. name ,
50
78
_ => 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
+ )
67
118
}
68
119
69
120
impl Diff for Statement {
70
- fn diff ( & self , other : & Self ) -> Option < Self > {
121
+ fn diff ( & self , other : & Self ) -> Option < Vec < Statement > > {
71
122
match self {
72
123
Self :: CreateTable ( a) => match other {
73
124
Self :: CreateTable ( b) => compare_create_table ( a, b) ,
74
125
_ => None ,
75
126
} ,
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
+ } ,
76
137
_ => todo ! ( "implement diff for all `Statement`s" ) ,
77
138
}
78
139
}
79
140
}
80
141
81
- fn compare_create_table ( a : & CreateTable , b : & CreateTable ) -> Option < Statement > {
142
+ fn compare_create_table ( a : & CreateTable , b : & CreateTable ) -> Option < Vec < Statement > > {
82
143
if a == b {
83
144
return None ;
84
145
}
@@ -116,12 +177,117 @@ fn compare_create_table(a: &CreateTable, b: &CreateTable) -> Option<Statement> {
116
177
} ) )
117
178
. collect ( ) ;
118
179
119
- Some ( Statement :: AlterTable {
180
+ Some ( vec ! [ Statement :: AlterTable {
120
181
name: a. name. clone( ) ,
121
182
if_exists: a. if_not_exists,
122
183
only: false ,
123
184
operations: ops,
124
185
location: None ,
125
186
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
+ )
127
293
}
0 commit comments