vespertide_query/
builder.rs1use vespertide_core::{MigrationAction, MigrationPlan, TableDef};
2use vespertide_planner::apply_action;
3
4use crate::DatabaseBackend;
5use crate::error::QueryError;
6use crate::sql::{BuiltQuery, build_action_queries};
7
8pub struct PlanQueries {
9 pub action: MigrationAction,
10 pub postgres: Vec<BuiltQuery>,
11 pub mysql: Vec<BuiltQuery>,
12 pub sqlite: Vec<BuiltQuery>,
13}
14
15pub fn build_plan_queries(
16 plan: &MigrationPlan,
17 current_schema: &[TableDef],
18) -> Result<Vec<PlanQueries>, QueryError> {
19 let mut queries: Vec<PlanQueries> = Vec::new();
20 let mut evolving_schema = current_schema.to_vec();
22
23 for action in &plan.actions {
24 let postgres_queries =
26 build_action_queries(&DatabaseBackend::Postgres, action, &evolving_schema)?;
27 let mysql_queries =
28 build_action_queries(&DatabaseBackend::MySql, action, &evolving_schema)?;
29 let sqlite_queries =
30 build_action_queries(&DatabaseBackend::Sqlite, action, &evolving_schema)?;
31 queries.push(PlanQueries {
32 action: action.clone(),
33 postgres: postgres_queries,
34 mysql: mysql_queries,
35 sqlite: sqlite_queries,
36 });
37
38 let _ = apply_action(&mut evolving_schema, action);
43 }
44 Ok(queries)
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use crate::sql::DatabaseBackend;
51 use rstest::rstest;
52 use vespertide_core::{
53 ColumnDef, ColumnType, MigrationAction, MigrationPlan, SimpleColumnType,
54 };
55
56 fn col(name: &str, ty: ColumnType) -> ColumnDef {
57 ColumnDef {
58 name: name.to_string(),
59 r#type: ty,
60 nullable: true,
61 default: None,
62 comment: None,
63 primary_key: None,
64 unique: None,
65 index: None,
66 foreign_key: None,
67 }
68 }
69
70 #[rstest]
71 #[case::empty(
72 MigrationPlan {
73 comment: None,
74 created_at: None,
75 version: 1,
76 actions: vec![],
77 },
78 0
79 )]
80 #[case::single_action(
81 MigrationPlan {
82 comment: None,
83 created_at: None,
84 version: 1,
85 actions: vec![MigrationAction::DeleteTable {
86 table: "users".into(),
87 }],
88 },
89 1
90 )]
91 #[case::multiple_actions(
92 MigrationPlan {
93 comment: None,
94 created_at: None,
95 version: 1,
96 actions: vec![
97 MigrationAction::CreateTable {
98 table: "users".into(),
99 columns: vec![col("id", ColumnType::Simple(SimpleColumnType::Integer))],
100 constraints: vec![],
101 },
102 MigrationAction::DeleteTable {
103 table: "posts".into(),
104 },
105 ],
106 },
107 2
108 )]
109 fn test_build_plan_queries(#[case] plan: MigrationPlan, #[case] expected_count: usize) {
110 let result = build_plan_queries(&plan, &[]).unwrap();
111 assert_eq!(
112 result.len(),
113 expected_count,
114 "Expected {} queries, got {}",
115 expected_count,
116 result.len()
117 );
118 }
119
120 #[test]
121 fn test_build_plan_queries_sql_content() {
122 let plan = MigrationPlan {
123 comment: None,
124 created_at: None,
125 version: 1,
126 actions: vec![
127 MigrationAction::CreateTable {
128 table: "users".into(),
129 columns: vec![col("id", ColumnType::Simple(SimpleColumnType::Integer))],
130 constraints: vec![],
131 },
132 MigrationAction::DeleteTable {
133 table: "posts".into(),
134 },
135 ],
136 };
137
138 let result = build_plan_queries(&plan, &[]).unwrap();
139 assert_eq!(result.len(), 2);
140
141 let sql1 = result[0]
143 .postgres
144 .iter()
145 .map(|q| q.build(DatabaseBackend::Postgres))
146 .collect::<Vec<_>>()
147 .join(";\n");
148 assert!(sql1.contains("CREATE TABLE"));
149 assert!(sql1.contains("\"users\""));
150 assert!(sql1.contains("\"id\""));
151
152 let sql2 = result[1]
153 .postgres
154 .iter()
155 .map(|q| q.build(DatabaseBackend::Postgres))
156 .collect::<Vec<_>>()
157 .join(";\n");
158 assert!(sql2.contains("DROP TABLE"));
159 assert!(sql2.contains("\"posts\""));
160
161 let sql1_mysql = result[0]
163 .mysql
164 .iter()
165 .map(|q| q.build(DatabaseBackend::MySql))
166 .collect::<Vec<_>>()
167 .join(";\n");
168 assert!(sql1_mysql.contains("`users`"));
169
170 let sql2_mysql = result[1]
171 .mysql
172 .iter()
173 .map(|q| q.build(DatabaseBackend::MySql))
174 .collect::<Vec<_>>()
175 .join(";\n");
176 assert!(sql2_mysql.contains("`posts`"));
177 }
178}