1use std::sync::Arc;
10
11use keelson_core::Value;
12use keelson_exec::{
13 Begin, BeginWith, Column, ExecError, ExecFuture, ExecResult, Executor, Family, RawConnection,
14 Row, RowStream, Statement, StreamExecutor, Transaction, TxConflict, TxConflictError, TxOptions,
15};
16use sqlx::mysql::{MySqlArguments, MySqlRow};
17use sqlx::{Column as _, MySql, Row as _, TypeInfo as _, ValueRef as _};
18
19use crate::common::{decode_err, unhandled};
20
21#[derive(Debug, Clone)]
24pub struct Pool {
25 inner: sqlx::MySqlPool,
26}
27
28impl Pool {
29 pub async fn connect(url: &str) -> Result<Self, ExecError> {
31 let inner = sqlx::mysql::MySqlPoolOptions::new()
32 .after_connect(|conn, _meta| {
33 Box::pin(async move {
34 sqlx::Executor::execute(&mut *conn, "SET time_zone = '+00:00'").await?;
37 Ok(())
38 })
39 })
40 .connect(url)
41 .await
42 .map_err(ExecError::driver)?;
43 Ok(Pool { inner })
44 }
45
46 pub fn from_pool(inner: sqlx::MySqlPool) -> Self {
53 Pool { inner }
54 }
55
56 pub fn inner(&self) -> &sqlx::MySqlPool {
58 &self.inner
59 }
60}
61
62impl Executor for Pool {
63 fn family(&self) -> Family {
64 Family::MySql
65 }
66
67 fn fetch(&self, stmt: Statement) -> ExecFuture<'_, Result<Vec<Row>, ExecError>> {
68 Box::pin(async move {
69 let Statement { sql, args, .. } = stmt;
70 do_fetch(&self.inner, &sql, args).await
71 })
72 }
73
74 fn execute(&self, stmt: Statement) -> ExecFuture<'_, Result<ExecResult, ExecError>> {
75 Box::pin(async move {
76 let Statement { sql, args, .. } = stmt;
77 do_execute(&self.inner, &sql, args).await
78 })
79 }
80}
81
82impl Begin for Pool {
83 fn begin(&self) -> ExecFuture<'_, Result<Transaction, ExecError>> {
84 Box::pin(async move {
85 let conn = self.inner.acquire().await.map_err(ExecError::driver)?;
86 Transaction::begin_on(Box::new(RawConn { conn })).await
87 })
88 }
89}
90
91impl BeginWith for Pool {
92 fn begin_with(&self, opts: TxOptions) -> ExecFuture<'_, Result<Transaction, ExecError>> {
93 Box::pin(async move {
94 opts.check(Family::MySql)?;
96 let conn = self.inner.acquire().await.map_err(ExecError::driver)?;
97 Transaction::begin_on_with(Box::new(RawConn { conn }), opts).await
98 })
99 }
100}
101
102impl StreamExecutor for Pool {
103 fn fetch_stream(&self, stmt: Statement) -> ExecFuture<'_, Result<RowStream, ExecError>> {
104 Box::pin(async move {
105 let pool = self.inner.clone();
106 let (tx, rx) = tokio::sync::mpsc::channel::<Result<Row, ExecError>>(64);
107 tokio::spawn(async move {
108 use futures_util::StreamExt as _;
109 let Statement { sql, args, .. } = stmt;
110 let q = match bind_args(&sql, args) {
111 Ok(q) => q,
112 Err(e) => {
113 let _ = tx.send(Err(e)).await;
114 return;
115 }
116 };
117 let mut native = q.fetch(&pool);
118 let mut header: Option<Arc<[Column]>> = None;
119 while let Some(next) = native.next().await {
120 let msg = match next {
121 Ok(row) => decode_row(&row, &mut header),
122 Err(e) => Err(ExecError::driver(e)),
123 };
124 let stop = msg.is_err();
125 if tx.send(msg).await.is_err() || stop {
126 return;
127 }
128 }
129 });
130 Ok(RowStream::new(rx))
131 })
132 }
133}
134
135#[derive(Debug)]
137struct RawConn {
138 conn: sqlx::pool::PoolConnection<MySql>,
139}
140
141impl RawConnection for RawConn {
142 fn family(&self) -> Family {
143 Family::MySql
144 }
145
146 fn fetch<'a>(
147 &'a mut self,
148 sql: &'a str,
149 args: Vec<Value>,
150 ) -> ExecFuture<'a, Result<Vec<Row>, ExecError>> {
151 Box::pin(async move { do_fetch(&mut *self.conn, sql, args).await })
152 }
153
154 fn execute<'a>(
155 &'a mut self,
156 sql: &'a str,
157 args: Vec<Value>,
158 ) -> ExecFuture<'a, Result<ExecResult, ExecError>> {
159 Box::pin(async move { do_execute(&mut *self.conn, sql, args).await })
160 }
161
162 fn abandon(self: Box<Self>) {
163 let _ = self.conn.detach();
164 }
165}
166
167fn driver_err(e: sqlx::Error) -> ExecError {
175 if let sqlx::Error::Database(db) = &e
176 && let Some(my) = db.try_downcast_ref::<sqlx::mysql::MySqlDatabaseError>()
177 {
178 let kind = match my.number() {
179 1213 => Some(TxConflict::Deadlock),
180 1205 => Some(TxConflict::LockTimeout),
181 _ => None,
182 };
183 if let Some(kind) = kind {
184 let code = my.number().to_string();
185 let message = my.message().to_owned();
186 return TxConflictError::new(kind, code, message)
187 .with_source(e)
188 .into_exec_error();
189 }
190 }
191 ExecError::driver(e)
192}
193
194async fn do_fetch<'e, E>(exec: E, sql: &str, args: Vec<Value>) -> Result<Vec<Row>, ExecError>
195where
196 E: sqlx::Executor<'e, Database = MySql>,
197{
198 let rows = bind_args(sql, args)?
199 .fetch_all(exec)
200 .await
201 .map_err(driver_err)?;
202 let mut header: Option<Arc<[Column]>> = None;
203 rows.iter().map(|r| decode_row(r, &mut header)).collect()
204}
205
206async fn do_execute<'e, E>(exec: E, sql: &str, args: Vec<Value>) -> Result<ExecResult, ExecError>
207where
208 E: sqlx::Executor<'e, Database = MySql>,
209{
210 let done = if args.is_empty() {
215 exec.execute(sql).await.map_err(driver_err)?
216 } else {
217 bind_args(sql, args)?
218 .execute(exec)
219 .await
220 .map_err(driver_err)?
221 };
222 let last = i64::try_from(done.last_insert_id())
223 .ok()
224 .filter(|id| *id != 0);
225 Ok(ExecResult::new(done.rows_affected(), last))
226}
227
228fn bind_args<'q>(
233 sql: &'q str,
234 args: Vec<Value>,
235) -> Result<sqlx::query::Query<'q, MySql, MySqlArguments>, ExecError> {
236 let mut q = sqlx::query(sql);
237 for v in args {
238 q = match v {
239 Value::Null => q.bind(Option::<String>::None),
240 Value::Bool(x) => q.bind(x),
241 Value::I8(x) => q.bind(x),
242 Value::I16(x) => q.bind(x),
243 Value::I32(x) => q.bind(x),
244 Value::I64(x) => q.bind(x),
245 Value::U8(x) => q.bind(x),
246 Value::U16(x) => q.bind(x),
247 Value::U32(x) => q.bind(x),
248 Value::U64(x) => q.bind(x),
249 Value::F32(x) => q.bind(x),
250 Value::F64(x) => q.bind(x),
251 Value::Text(x) => q.bind(x),
252 Value::Bytes(x) => q.bind(x),
253 #[cfg(feature = "chrono")]
254 Value::Date(x) => q.bind(x),
255 #[cfg(feature = "chrono")]
256 Value::Time(x) => q.bind(x),
257 #[cfg(feature = "chrono")]
258 Value::DateTime(x) => q.bind(x),
259 #[cfg(feature = "chrono")]
260 Value::TimestampTz(x) => q.bind(x),
261 #[cfg(feature = "uuid")]
262 Value::Uuid(x) => q.bind(x.hyphenated().to_string()),
263 #[cfg(feature = "decimal")]
264 Value::Decimal(x) => q.bind(x),
265 #[cfg(feature = "json")]
266 Value::Json(x) => q.bind(x),
267 other => {
268 return Err(ExecError::UnsupportedValue {
269 type_name: other.type_name(),
270 family: Family::MySql,
271 });
272 }
273 };
274 }
275 Ok(q)
276}
277
278fn decode_row(row: &MySqlRow, header: &mut Option<Arc<[Column]>>) -> Result<Row, ExecError> {
279 let columns = header
280 .get_or_insert_with(|| {
281 row.columns()
282 .iter()
283 .map(|c| Column::new(c.name()))
284 .collect::<Vec<_>>()
285 .into()
286 })
287 .clone();
288 let mut values = Vec::with_capacity(row.columns().len());
289 for i in 0..row.columns().len() {
290 values.push(decode_value(row, i)?);
291 }
292 Ok(Row::new(columns, values))
293}
294
295fn decode_value(row: &MySqlRow, i: usize) -> Result<Value, ExecError> {
296 let col = &row.columns()[i];
297 let name = col.name();
298 let raw = row.try_get_raw(i).map_err(|e| decode_err(name, e))?;
299 if raw.is_null() {
300 return Ok(Value::Null);
301 }
302 let ty = raw.type_info();
303 let ty = ty.name();
304
305 macro_rules! take {
306 ($t:ty) => {
307 row.try_get::<$t, _>(i).map_err(|e| decode_err(name, e))?
308 };
309 }
310
311 Ok(match ty {
312 "BOOLEAN" => Value::Bool(take!(bool)),
314 "TINYINT" => Value::I8(take!(i8)),
315 "SMALLINT" => Value::I16(take!(i16)),
316 "MEDIUMINT" | "INT" => Value::I32(take!(i32)),
317 "BIGINT" => Value::I64(take!(i64)),
318 "TINYINT UNSIGNED" => Value::U8(take!(u8)),
319 "SMALLINT UNSIGNED" => Value::U16(take!(u16)),
320 "MEDIUMINT UNSIGNED" | "INT UNSIGNED" => Value::U32(take!(u32)),
321 "BIGINT UNSIGNED" => Value::U64(take!(u64)),
322 "FLOAT" => Value::F32(take!(f32)),
323 "DOUBLE" => Value::F64(take!(f64)),
324 "CHAR" | "VARCHAR" | "TEXT" | "TINYTEXT" | "MEDIUMTEXT" | "LONGTEXT" | "ENUM" => {
325 Value::Text(take!(String))
326 }
327 "BINARY" | "VARBINARY" | "BLOB" | "TINYBLOB" | "MEDIUMBLOB" | "LONGBLOB" => {
328 Value::Bytes(take!(Vec<u8>))
329 }
330 #[cfg(feature = "chrono")]
331 "DATE" => Value::Date(take!(chrono::NaiveDate)),
332 #[cfg(feature = "chrono")]
333 "TIME" => Value::Time(take!(chrono::NaiveTime)),
334 #[cfg(feature = "chrono")]
335 "DATETIME" => Value::DateTime(take!(chrono::NaiveDateTime)),
336 #[cfg(feature = "chrono")]
339 "TIMESTAMP" => Value::TimestampTz(take!(chrono::DateTime<chrono::Utc>)),
340 #[cfg(not(feature = "chrono"))]
341 "DATE" | "TIME" | "DATETIME" | "TIMESTAMP" => {
342 return Err(crate::common::need_feature(name, ty, "chrono"));
343 }
344 #[cfg(feature = "decimal")]
345 "DECIMAL" => Value::Decimal(take!(rust_decimal::Decimal)),
346 #[cfg(not(feature = "decimal"))]
347 "DECIMAL" => return Err(crate::common::need_feature(name, ty, "decimal")),
348 #[cfg(feature = "json")]
349 "JSON" => Value::Json(take!(serde_json::Value)),
350 #[cfg(not(feature = "json"))]
351 "JSON" => return Err(crate::common::need_feature(name, ty, "json")),
352 other => return Err(unhandled(name, other)),
353 })
354}