tern_core/executor/sqlx_backend/
pool.rs1use crate::error::{DatabaseError as _, TernResult};
7use crate::migration::{
8 AppliedMigration, Executor as MigrationExecutor, Query, QueryRepository,
9};
10
11use chrono::{DateTime, Utc};
12use sqlx::pool::PoolOptions;
13use sqlx::{
14 Acquire, Connection, Database, Encode, Executor, FromRow, IntoArguments,
15 Pool, Type,
16};
17use std::marker::PhantomData;
18
19pub struct SqlxExecutor<Db, Q>
21where
22 Db: Database,
23 Q: QueryRepository,
24{
25 pool: Pool<Db>,
26 _q: PhantomData<Q>,
27}
28
29impl<Db, Q> SqlxExecutor<Db, Q>
30where
31 Db: Database,
32 Q: QueryRepository,
33{
34 pub async fn new(db_url: &str) -> TernResult<Self> {
36 let pool = Pool::connect(db_url).await.tern_result()?;
37
38 Ok(Self { pool, _q: PhantomData })
39 }
40
41 pub async fn new_with(
43 pool_opts: PoolOptions<Db>,
44 conn_opts: <Db::Connection as Connection>::Options,
45 ) -> TernResult<Self> {
46 let pool = pool_opts.connect_with(conn_opts).await.tern_result()?;
47
48 Ok(Self { pool, _q: PhantomData })
49 }
50
51 pub fn pool(&self) -> Pool<Db> {
54 self.pool.clone()
55 }
56}
57
58impl<Db, Q> MigrationExecutor for SqlxExecutor<Db, Q>
63where
64 Self: Send + Sync + 'static,
65 Q: QueryRepository,
66 Db: Database,
67 for<'c> &'c mut <Db as Database>::Connection: Executor<'c, Database = Db>,
68 for<'q> <Db as Database>::Arguments<'q>: IntoArguments<'q, Db>,
69 for<'r> AppliedMigration: FromRow<'r, <Db as Database>::Row>,
70 String: Type<Db> + for<'a> Encode<'a, Db>,
71 i64: Type<Db> + for<'a> Encode<'a, Db>,
72 DateTime<Utc>: Type<Db> + for<'a> Encode<'a, Db>,
73{
74 type Queries = Q;
75
76 async fn apply_tx(&mut self, query: &Query) -> TernResult<()> {
77 let mut tx = self.pool.begin().await.tern_result()?;
78 let conn = tx.acquire().await.tern_result()?;
79 conn.execute(sqlx::raw_sql(query.sql())).await.void_tern_result()?;
80 tx.commit().await.void_tern_result()?;
81
82 Ok(())
83 }
84
85 async fn apply_no_tx(&mut self, query: &Query) -> TernResult<()> {
86 let statements = query.split_statements()?;
87 for statement in statements.iter() {
88 self.pool
89 .execute(sqlx::raw_sql(statement.as_ref()))
90 .await
91 .void_tern_result()?;
92 }
93
94 Ok(())
95 }
96
97 async fn create_history_if_not_exists(
98 &mut self,
99 history_table: &str,
100 ) -> TernResult<()> {
101 let query = Q::create_history_if_not_exists_query(history_table);
102 self.pool.execute(sqlx::raw_sql(query.sql())).await.void_tern_result()
103 }
104
105 async fn drop_history(&mut self, history_table: &str) -> TernResult<()> {
106 let query = Q::drop_history_query(history_table);
107 self.pool.execute(sqlx::raw_sql(query.sql())).await.void_tern_result()
108 }
109
110 async fn get_all_applied(
111 &mut self,
112 history_table: &str,
113 ) -> TernResult<Vec<AppliedMigration>> {
114 let query = Q::select_star_from_history_query(history_table);
115 let applied = sqlx::query_as::<Db, AppliedMigration>(query.sql())
116 .fetch_all(&self.pool)
117 .await
118 .tern_result()?;
119
120 Ok(applied)
121 }
122
123 async fn insert_applied_migration(
130 &mut self,
131 history_table: &str,
132 applied: &AppliedMigration,
133 ) -> TernResult<()> {
134 let query = Q::insert_into_history_query(history_table, applied);
135 sqlx::query::<Db>(query.sql())
136 .bind(applied.version)
137 .bind(applied.description.clone())
138 .bind(applied.content.clone())
139 .bind(applied.duration_ms)
140 .bind(applied.applied_at)
141 .execute(&self.pool)
142 .await
143 .void_tern_result()?;
144
145 Ok(())
146 }
147
148 async fn upsert_applied_migration(
154 &mut self,
155 history_table: &str,
156 applied: &AppliedMigration,
157 ) -> TernResult<()> {
158 let query = Q::upsert_history_query(history_table, applied);
159 sqlx::query::<Db>(query.sql())
160 .bind(applied.version)
161 .bind(applied.description.clone())
162 .bind(applied.content.clone())
163 .bind(applied.duration_ms)
164 .bind(applied.applied_at)
165 .execute(&self.pool)
166 .await
167 .void_tern_result()?;
168
169 Ok(())
170 }
171}