Skip to content

Commit 078be8d

Browse files
committed
feat: basic CREATE/DROP index support
1 parent e6ec585 commit 078be8d

File tree

3 files changed

+143
-5
lines changed

3 files changed

+143
-5
lines changed

src/diff.rs

+65-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::{cmp::Ordering, collections::HashSet};
22

33
use sqlparser::ast::{
44
AlterTableOperation, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
5-
AlterTypeOperation, CreateTable, Ident, ObjectName, Statement, UserDefinedTypeRepresentation,
5+
AlterTypeOperation, CreateIndex, CreateTable, Ident, ObjectName, ObjectType, Statement,
6+
UserDefinedTypeRepresentation,
67
};
78

89
pub trait Diff: Sized {
@@ -21,6 +22,7 @@ impl Diff for Vec<Statement> {
2122
// CreateTable: compare against another CreateTable with the same name
2223
// TODO: handle renames (e.g. use comments to tag a previous name for a table in a schema)
2324
Statement::CreateTable(a) => find_and_compare_create_table(sa, a, other),
25+
Statement::CreateIndex(a) => find_and_compare_create_index(sa, a, other),
2426
Statement::CreateType { name, .. } => find_and_compare_create_type(sa, name, other),
2527
Statement::CreateExtension {
2628
name,
@@ -37,6 +39,10 @@ impl Diff for Vec<Statement> {
3739
Statement::CreateTable(a) => a.name == b.name,
3840
_ => false,
3941
}),
42+
Statement::CreateIndex(b) => self.iter().find(|sa| match sa {
43+
Statement::CreateIndex(a) => a.name == b.name,
44+
_ => false,
45+
}),
4046
Statement::CreateType { name: b_name, .. } => self.iter().find(|sa| match sa {
4147
Statement::CreateType { name: a_name, .. } => a_name == b_name,
4248
_ => false,
@@ -107,6 +113,36 @@ fn find_and_compare_create_table(
107113
)
108114
}
109115

116+
fn find_and_compare_create_index(
117+
sa: &Statement,
118+
a: &CreateIndex,
119+
other: &[Statement],
120+
) -> Option<Vec<Statement>> {
121+
find_and_compare(
122+
sa,
123+
other,
124+
|sb| match sb {
125+
Statement::CreateIndex(b) => a.name == b.name,
126+
_ => false,
127+
},
128+
|| {
129+
let name = a
130+
.name
131+
.clone()
132+
.expect("can't drop an unnamed index, please ensure all indexes are named");
133+
Some(vec![Statement::Drop {
134+
object_type: sqlparser::ast::ObjectType::Index,
135+
if_exists: a.if_not_exists,
136+
names: vec![name],
137+
cascade: false,
138+
restrict: false,
139+
purge: false,
140+
temporary: false,
141+
}])
142+
},
143+
)
144+
}
145+
110146
fn find_and_compare_create_type(
111147
sa: &Statement,
112148
a_name: &ObjectName,
@@ -170,6 +206,10 @@ impl Diff for Statement {
170206
Self::CreateTable(b) => compare_create_table(a, b),
171207
_ => None,
172208
},
209+
Self::CreateIndex(a) => match other {
210+
Self::CreateIndex(b) => compare_create_index(a, b),
211+
_ => None,
212+
},
173213
Self::CreateType {
174214
name: a_name,
175215
representation: a_rep,
@@ -233,6 +273,30 @@ fn compare_create_table(a: &CreateTable, b: &CreateTable) -> Option<Vec<Statemen
233273
}])
234274
}
235275

276+
fn compare_create_index(a: &CreateIndex, b: &CreateIndex) -> Option<Vec<Statement>> {
277+
if a == b {
278+
return None;
279+
}
280+
281+
if a.name.is_none() || b.name.is_none() {
282+
panic!("can't diff unnamed indexes!");
283+
}
284+
let name = a.name.clone().unwrap();
285+
286+
Some(vec![
287+
Statement::Drop {
288+
object_type: ObjectType::Index,
289+
if_exists: a.if_not_exists,
290+
names: vec![name],
291+
cascade: false,
292+
restrict: false,
293+
purge: false,
294+
temporary: false,
295+
},
296+
Statement::CreateIndex(b.clone()),
297+
])
298+
}
299+
236300
fn compare_create_type(
237301
a_name: &ObjectName,
238302
a_rep: &UserDefinedTypeRepresentation,

src/lib.rs

+46
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,27 @@ mod tests {
244244
);
245245
}
246246

247+
#[test]
248+
fn diff_create_index() {
249+
run_test_cases(
250+
vec![
251+
TestCase {
252+
dialect: Dialect::Generic,
253+
sql_a: "CREATE UNIQUE INDEX title_idx ON films (title);",
254+
sql_b: "CREATE UNIQUE INDEX title_idx ON films ((lower(title)));",
255+
expect: "DROP INDEX title_idx;\n\nCREATE UNIQUE INDEX title_idx ON films((lower(title)));",
256+
},
257+
TestCase {
258+
dialect: Dialect::Generic,
259+
sql_a: "CREATE UNIQUE INDEX IF NOT EXISTS title_idx ON films (title);",
260+
sql_b: "CREATE UNIQUE INDEX IF NOT EXISTS title_idx ON films ((lower(title)));",
261+
expect: "DROP INDEX IF EXISTS title_idx;\n\nCREATE UNIQUE INDEX IF NOT EXISTS title_idx ON films((lower(title)));",
262+
},
263+
],
264+
|ast_a, ast_b| ast_a.diff(&ast_b).unwrap(),
265+
);
266+
}
267+
247268
#[test]
248269
fn diff_create_type() {
249270
run_test_cases(
@@ -409,6 +430,31 @@ mod tests {
409430
], |ast_a, ast_b| ast_a.migrate(&ast_b).unwrap());
410431
}
411432

433+
#[test]
434+
fn apply_create_index() {
435+
run_test_cases(vec![
436+
TestCase {
437+
dialect: Dialect::Generic,
438+
sql_a: "CREATE UNIQUE INDEX title_idx ON films (title);",
439+
sql_b: "CREATE INDEX code_idx ON films (code);",
440+
expect: "CREATE UNIQUE INDEX title_idx ON films(title);\n\nCREATE INDEX code_idx ON films(code);",
441+
},
442+
TestCase {
443+
dialect: Dialect::Generic,
444+
sql_a: "CREATE UNIQUE INDEX title_idx ON films (title);",
445+
sql_b: "DROP INDEX title_idx;",
446+
expect: "",
447+
},
448+
TestCase {
449+
dialect: Dialect::Generic,
450+
sql_a: "CREATE UNIQUE INDEX title_idx ON films (title);",
451+
sql_b: "DROP INDEX title_idx;CREATE INDEX code_idx ON films (code);",
452+
expect: "CREATE INDEX code_idx ON films(code);",
453+
},
454+
],
455+
|ast_a, ast_b| ast_a.migrate(&ast_b).unwrap());
456+
}
457+
412458
#[test]
413459
fn apply_alter_create_type() {
414460
run_test_cases(

src/migration.rs

+32-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ impl Migrate for Vec<Statement> {
3030
_ => false,
3131
})
3232
.map_or(Some(orig), |sb| sa.migrate(sb)),
33+
Statement::CreateIndex(a) => other
34+
.iter()
35+
.find(|sb| match sb {
36+
Statement::Drop {
37+
object_type, names, ..
38+
} => {
39+
*object_type == ObjectType::Index
40+
&& names.len() == 1
41+
&& Some(&names[0]) == a.name.as_ref()
42+
}
43+
_ => false,
44+
})
45+
.map_or(Some(orig), |sb| sa.migrate(sb)),
3346
Statement::CreateType { name, .. } => other
3447
.iter()
3548
.find(|sb| match sb {
@@ -56,9 +69,10 @@ impl Migrate for Vec<Statement> {
5669
})
5770
// CREATE table etc.
5871
.chain(other.iter().filter_map(|sb| match sb {
59-
Statement::CreateTable(_) => Some(sb.clone()),
60-
Statement::CreateType { .. } => Some(sb.clone()),
61-
Statement::CreateExtension { .. } => Some(sb.clone()),
72+
Statement::CreateTable(_)
73+
| Statement::CreateIndex { .. }
74+
| Statement::CreateType { .. }
75+
| Statement::CreateExtension { .. } => Some(sb.clone()),
6276
_ => None,
6377
}))
6478
.collect();
@@ -92,6 +106,20 @@ impl Migrate for Statement {
92106
}
93107
_ => todo!("handle migrating statement: {:?}", other),
94108
},
109+
Self::CreateIndex(a) => match other {
110+
Self::Drop {
111+
object_type, names, ..
112+
} => {
113+
let name = a.name.clone().expect("can't migrate unnamed index: {a}");
114+
if *object_type == ObjectType::Index && names.contains(&name) {
115+
None
116+
} else {
117+
// DROP statement is for another index
118+
Some(Self::CreateIndex(a))
119+
}
120+
}
121+
_ => todo!("handle migrating statement: {:?}", other),
122+
},
95123
Self::CreateType {
96124
name,
97125
representation,
@@ -127,7 +155,7 @@ impl Migrate for Statement {
127155
}
128156
_ => todo!("handle migrating statement: {:?}", other),
129157
},
130-
_ => todo!("handle migrating statement: {:?}", other),
158+
_ => todo!("handle migrating statement: {self:?} -> {other:?}"),
131159
}
132160
}
133161
}

0 commit comments

Comments
 (0)