1use super::{Db, DbError, FetchedThen, RawRows, Tx};
4use crate::error::DatabaseError;
5use crate::row_json::{row_to_json, row_to_values};
6use postrust_sql::SqlParam;
7use sqlx::postgres::{PgArguments, PgPool, PgRow};
8use sqlx::query::Query;
9use sqlx::{AssertSqlSafe, Postgres, Row, ValueRef};
10
11pub struct SqlxTx {
18 tx: sqlx::Transaction<'static, Postgres>,
19 queued: Vec<(String, Vec<SqlParam>)>,
20}
21
22impl Db for PgPool {
23 type Tx<'a> = SqlxTx;
24
25 async fn begin(&self) -> Result<SqlxTx, DbError> {
26 Ok(SqlxTx {
27 tx: PgPool::begin(self).await?,
28 queued: Vec::new(),
29 })
30 }
31}
32
33impl SqlxTx {
34 async fn flush(&mut self) -> Result<(), DbError> {
36 for (sql, params) in std::mem::take(&mut self.queued) {
37 query(&sql, ¶ms)
38 .execute(&mut *self.tx)
39 .await
40 .map_err(|e| DbError::from(e).queued())?;
41 }
42 Ok(())
43 }
44}
45
46impl Tx for SqlxTx {
47 async fn execute(&mut self, sql: &str, params: &[SqlParam]) -> Result<u64, DbError> {
48 self.flush().await?;
49 Ok(query(sql, params)
50 .execute(&mut *self.tx)
51 .await?
52 .rows_affected())
53 }
54
55 async fn fetch_json(
56 &mut self,
57 sql: &str,
58 params: &[SqlParam],
59 ) -> Result<Vec<serde_json::Value>, DbError> {
60 self.flush().await?;
64 Ok(fetch_all(&mut self.tx, sql, params)
65 .await?
66 .into_iter()
67 .map(|row| row_to_json(&row))
68 .collect())
69 }
70
71 async fn fetch_values(
72 &mut self,
73 sql: &str,
74 params: &[SqlParam],
75 ) -> Result<Vec<Vec<serde_json::Value>>, DbError> {
76 self.flush().await?;
77 Ok(fetch_all(&mut self.tx, sql, params)
78 .await?
79 .into_iter()
80 .map(|row| row_to_values(&row))
81 .collect())
82 }
83
84 async fn fetch_raw(
85 &mut self,
86 sql: &str,
87 params: &[SqlParam],
88 count_column: &str,
89 ) -> Result<RawRows, DbError> {
90 self.flush().await?;
91 let rows = fetch_all(&mut self.tx, sql, params).await?;
92 let first = rows.first().and_then(|row| {
93 let value = row.try_get_raw(0).ok()?;
94 if value.is_null() {
95 return None;
96 }
97 Some(value.as_bytes().ok()?.to_vec())
98 });
99 let count = rows
100 .first()
101 .and_then(|row| row.try_get::<i64, _>(count_column).ok());
102 Ok(RawRows::new(rows.len(), first, count))
103 }
104
105 async fn commit(mut self) -> Result<(), DbError> {
106 self.flush().await?;
107 Ok(self.tx.commit().await?)
108 }
109
110 fn queue(&mut self, sql: &str, params: &[SqlParam]) {
111 self.queued.push((sql.to_string(), params.to_vec()));
112 }
113
114 async fn fetch_json_then(&mut self, sql: &str, params: &[SqlParam], then: &str) -> FetchedThen {
115 let rows = self.fetch_json(sql, params).await?;
116 let then = self.fetch_values(then, &[]).await?;
117 Ok((rows, then))
118 }
119
120 async fn fetch_json_and_commit(
121 mut self,
122 sql: &str,
123 params: &[SqlParam],
124 ) -> Result<Vec<serde_json::Value>, DbError> {
125 let rows = self.fetch_json(sql, params).await?;
126 self.commit().await?;
127 Ok(rows)
128 }
129}
130
131impl crate::session::Session<'_> {
132 pub async fn apply_sqlx(&self, conn: &mut sqlx::PgConnection) -> Result<(), DbError> {
145 for statement in self.statements() {
146 query(&statement.sql, &statement.params)
147 .execute(&mut *conn)
148 .await
149 .map_err(|e| DbError::from(e).queued())?;
150 }
151 Ok(())
152 }
153}
154
155async fn fetch_all(
156 tx: &mut sqlx::Transaction<'static, Postgres>,
157 sql: &str,
158 params: &[SqlParam],
159) -> Result<Vec<PgRow>, sqlx::Error> {
160 query(sql, params).fetch_all(&mut **tx).await
161}
162
163fn query(sql: &str, params: &[SqlParam]) -> Query<'static, Postgres, PgArguments> {
183 let mut query = sqlx::query(AssertSqlSafe(sql));
184 for param in params {
185 query = match param {
186 SqlParam::Null => query.bind(None::<String>),
187 SqlParam::Bool(b) => query.bind(b),
188 SqlParam::Int(n) => query.bind(n),
189 SqlParam::Float(f) => query.bind(f),
190 SqlParam::Text(s) => query.bind(s),
191 SqlParam::Bytes(b) => query.bind(b),
192 SqlParam::Json(j) => query.bind(j),
193 SqlParam::Uuid(u) => query.bind(u),
194 SqlParam::Timestamp(t) => query.bind(t),
195 SqlParam::Array(arr) => {
196 let strings: Vec<String> = arr
198 .iter()
199 .map(|p| match p {
200 SqlParam::Text(s) => s.clone(),
201 SqlParam::Int(n) => n.to_string(),
202 SqlParam::Bool(b) => b.to_string(),
203 other => format!("{:?}", other),
204 })
205 .collect();
206 query.bind(strings)
207 }
208 };
209 }
210
211 query
212}
213
214impl From<sqlx::Error> for DbError {
215 fn from(e: sqlx::Error) -> Self {
216 let message = e.to_string();
217 match e {
218 sqlx::Error::Database(db_err) => {
219 let message = format!("error returned from database: {}", db_err.message());
227 let (details, hint) = db_err
229 .try_downcast_ref::<sqlx::postgres::PgDatabaseError>()
230 .map(|pg_err| {
231 (
232 pg_err.detail().map(String::from),
233 pg_err.hint().map(String::from),
234 )
235 })
236 .unwrap_or((None, None));
237
238 DbError::database(
239 message,
240 DatabaseError {
241 code: db_err.code().map(|c| c.to_string()).unwrap_or_default(),
242 message: db_err.message().to_string(),
243 details,
244 hint,
245 constraint: db_err.constraint().map(|s| s.to_string()),
246 table: db_err.table().map(|s| s.to_string()),
247 column: None,
248 },
249 )
250 }
251 sqlx::Error::Io(_)
254 | sqlx::Error::Tls(_)
255 | sqlx::Error::PoolTimedOut
256 | sqlx::Error::PoolClosed
257 | sqlx::Error::WorkerCrashed => DbError::connection(message),
258 _ => DbError::other(message),
259 }
260 }
261}