trailbase_refinery_core/traits/
async.rs1use crate::error::WrapMigrationError;
2use crate::traits::{
3 insert_migration_query, verify_migrations, ASSERT_MIGRATIONS_TABLE_QUERY,
4 GET_APPLIED_MIGRATIONS_QUERY, GET_LAST_APPLIED_MIGRATION_QUERY,
5};
6use crate::{Error, Migration, Report, Target};
7
8use async_trait::async_trait;
9use std::ops::Deref;
10use std::string::ToString;
11
12#[async_trait]
13pub trait AsyncTransaction {
14 type Error: std::error::Error + Send + Sync + 'static;
15
16 async fn execute<'a, T: Iterator<Item = &'a str> + Send>(
17 &mut self,
18 queries: T,
19 ) -> Result<usize, Self::Error>;
20}
21
22#[async_trait]
23pub trait AsyncQuery<T>: AsyncTransaction {
24 async fn query(&mut self, query: &str) -> Result<T, Self::Error>;
25}
26
27async fn migrate<T: AsyncTransaction>(
28 transaction: &mut T,
29 migrations: Vec<Migration>,
30 target: Target,
31 migration_table_name: &str,
32) -> Result<Report, Error> {
33 let mut applied_migrations = vec![];
34
35 for mut migration in migrations.into_iter() {
36 if let Target::Version(input_target) = target {
37 if input_target < migration.version() {
38 log::info!(
39 "stopping at migration: {}, due to user option",
40 input_target
41 );
42 break;
43 }
44 }
45
46 log::info!("applying migration: {}", migration);
47 migration.set_applied();
48 let update_query = insert_migration_query(&migration, migration_table_name);
49 transaction
50 .execute(
51 [
52 migration.sql().as_ref().expect("sql must be Some!"),
53 update_query.as_str(),
54 ]
55 .into_iter(),
56 )
57 .await
58 .migration_err(
59 &format!("error applying migration {}", migration),
60 Some(&applied_migrations),
61 )?;
62 applied_migrations.push(migration);
63 }
64 Ok(Report::new(applied_migrations))
65}
66
67async fn migrate_grouped<T: AsyncTransaction>(
68 transaction: &mut T,
69 migrations: Vec<Migration>,
70 target: Target,
71 migration_table_name: &str,
72) -> Result<Report, Error> {
73 let mut grouped_migrations = Vec::new();
74 let mut applied_migrations = Vec::new();
75
76 for mut migration in migrations.into_iter() {
77 if let Target::Version(input_target) | Target::FakeVersion(input_target) = target {
78 if input_target < migration.version() {
79 break;
80 }
81 }
82
83 migration.set_applied();
84 let query = insert_migration_query(&migration, migration_table_name);
85
86 let sql = migration.sql().expect("sql must be Some!").to_string();
87
88 if !matches!(target, Target::Fake | Target::FakeVersion(_)) {
90 applied_migrations.push(migration);
91 grouped_migrations.push(sql);
92 }
93 grouped_migrations.push(query);
94 }
95
96 match target {
97 Target::Fake | Target::FakeVersion(_) => {
98 log::info!("not going to apply any migration as fake flag is enabled");
99 }
100 Target::Latest | Target::Version(_) => {
101 log::info!(
102 "going to apply batch migrations in single transaction: {:#?}",
103 applied_migrations.iter().map(ToString::to_string)
104 );
105 }
106 };
107
108 if let Target::Version(input_target) = target {
109 log::info!(
110 "stopping at migration: {}, due to user option",
111 input_target
112 );
113 }
114
115 let refs = grouped_migrations.iter().map(AsRef::as_ref);
116
117 transaction
118 .execute(refs)
119 .await
120 .migration_err("error applying migrations", None)?;
121
122 Ok(Report::new(applied_migrations))
123}
124
125#[async_trait]
126pub trait AsyncMigrate: AsyncQuery<Vec<Migration>>
127where
128 Self: Sized,
129{
130 fn assert_migrations_table_query(migration_table_name: &str) -> String {
132 ASSERT_MIGRATIONS_TABLE_QUERY.replace("%MIGRATION_TABLE_NAME%", migration_table_name)
133 }
134
135 async fn get_last_applied_migration(
136 &mut self,
137 migration_table_name: &str,
138 ) -> Result<Option<Migration>, Error> {
139 let mut migrations = self
140 .query(
141 &GET_LAST_APPLIED_MIGRATION_QUERY
142 .replace("%MIGRATION_TABLE_NAME%", migration_table_name),
143 )
144 .await
145 .migration_err("error getting last applied migration", None)?;
146
147 Ok(migrations.pop())
148 }
149
150 async fn get_applied_migrations(
151 &mut self,
152 migration_table_name: &str,
153 ) -> Result<Vec<Migration>, Error> {
154 let migrations = self
155 .query(
156 &GET_APPLIED_MIGRATIONS_QUERY
157 .replace("%MIGRATION_TABLE_NAME%", migration_table_name),
158 )
159 .await
160 .migration_err("error getting applied migrations", None)?;
161
162 Ok(migrations)
163 }
164
165 async fn migrate(
166 &mut self,
167 migrations: &[Migration],
168 abort_divergent: bool,
169 abort_missing: bool,
170 grouped: bool,
171 target: Target,
172 migration_table_name: &str,
173 ) -> Result<Report, Error> {
174 self.execute(
175 [Self::assert_migrations_table_query(migration_table_name).as_str()].into_iter(),
176 )
177 .await
178 .migration_err("error asserting migrations table", None)?;
179
180 let applied_migrations = self
181 .query(
182 &GET_APPLIED_MIGRATIONS_QUERY
183 .replace("%MIGRATION_TABLE_NAME%", migration_table_name),
184 )
185 .await
186 .migration_err("error getting current schema version", None)?;
187
188 let migrations = verify_migrations(
189 applied_migrations,
190 migrations.to_vec(),
191 abort_divergent,
192 abort_missing,
193 )?;
194
195 if migrations.is_empty() {
196 log::info!("no migrations to apply");
197 }
198
199 if grouped || matches!(target, Target::Fake | Target::FakeVersion(_)) {
200 migrate_grouped(self, migrations, target, migration_table_name).await
201 } else {
202 migrate(self, migrations, target, migration_table_name).await
203 }
204 }
205}