1use async_trait::async_trait;
13use futures::StreamExt;
14use sqlx::{Column, Executor, Row, TypeInfo};
15use std::collections::HashMap;
16use std::future::Future;
17use std::pin::Pin;
18use std::str::FromStr;
19use std::sync::Arc;
20use sz_orm_core::{ColType, Connection, ConnectionFactory, DbError, QueryRows, QueryValues, Value};
21
22use crate::error::map_sqlx_error;
23
24fn needs_raw_sql(sql: &str) -> bool {
27 let trimmed = sql.trim_start();
28 let upper = trimmed.to_uppercase();
29 upper.starts_with("BEGIN")
30 || upper.starts_with("COMMIT")
31 || upper.starts_with("ROLLBACK")
32 || upper.starts_with("SAVEPOINT")
33 || upper.starts_with("RELEASE")
34 || upper.starts_with("SET ")
35 || upper.starts_with("USE ")
36 || upper.starts_with("START TRANSACTION")
37}
38
39fn row_to_value_with_coltype_sqlite(
57 row: &sqlx::sqlite::SqliteRow,
58 ordinal: usize,
59 col_type: ColType,
60) -> Value {
61 match col_type {
62 ColType::Bool => match row.try_get::<Option<bool>, usize>(ordinal) {
63 Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
64 Err(_) => Value::Null,
65 },
66 ColType::I8 => match row.try_get::<Option<i8>, usize>(ordinal) {
67 Ok(v) => v.map(Value::I8).unwrap_or(Value::Null),
68 Err(_) => Value::Null,
69 },
70 ColType::I16 => match row.try_get::<Option<i16>, usize>(ordinal) {
71 Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
72 Err(_) => Value::Null,
73 },
74 ColType::I32 => match row.try_get::<Option<i32>, usize>(ordinal) {
75 Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
76 Err(_) => Value::Null,
77 },
78 ColType::I64 => match row.try_get::<Option<i64>, usize>(ordinal) {
79 Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
80 Err(_) => Value::Null,
81 },
82 ColType::U8 => match row.try_get::<Option<u8>, usize>(ordinal) {
83 Ok(v) => v.map(Value::U8).unwrap_or(Value::Null),
84 Err(_) => Value::Null,
85 },
86 ColType::U16 => match row.try_get::<Option<u16>, usize>(ordinal) {
87 Ok(v) => v.map(Value::U16).unwrap_or(Value::Null),
88 Err(_) => Value::Null,
89 },
90 ColType::U32 => match row.try_get::<Option<u32>, usize>(ordinal) {
91 Ok(v) => v.map(Value::U32).unwrap_or(Value::Null),
92 Err(_) => Value::Null,
93 },
94 ColType::U64 => match row.try_get::<Option<i64>, usize>(ordinal) {
96 Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
97 Err(_) => Value::Null,
98 },
99 ColType::F32 => match row.try_get::<Option<f32>, usize>(ordinal) {
100 Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
101 Err(_) => Value::Null,
102 },
103 ColType::F64 => match row.try_get::<Option<f64>, usize>(ordinal) {
104 Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
105 Err(_) => Value::Null,
106 },
107 ColType::Decimal => match row.try_get::<Option<String>, usize>(ordinal) {
108 Ok(v) => v.map(Value::Decimal).unwrap_or(Value::Null),
109 Err(_) => Value::Null,
110 },
111 ColType::String => match row.try_get::<Option<String>, usize>(ordinal) {
112 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
113 Err(_) => Value::Null,
114 },
115 ColType::Bytes => match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
116 Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
117 Err(_) => Value::Null,
118 },
119 ColType::Date | ColType::DateTime | ColType::Time | ColType::Json | ColType::Uuid => {
121 match row.try_get::<Option<String>, usize>(ordinal) {
122 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
123 Err(_) => Value::Null,
124 }
125 }
126 ColType::Unknown => {
127 if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
134 return v.map(Value::I64).unwrap_or(Value::Null);
135 }
136 if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
137 return v.map(Value::F64).unwrap_or(Value::Null);
138 }
139 if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
140 return v.map(Value::Bool).unwrap_or(Value::Null);
141 }
142 if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
143 return v.map(Value::String).unwrap_or(Value::Null);
144 }
145 Value::Null
146 }
147 _ => Value::Null,
149 }
150}
151
152pub struct SqlitePoolHandle {
153 pool: sqlx::SqlitePool,
154}
155
156impl SqlitePoolHandle {
157 pub async fn connect(url: &str) -> Result<Self, DbError> {
158 let opts = sqlx::sqlite::SqliteConnectOptions::from_str(url)
164 .map_err(map_sqlx_error)?
165 .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
166 .synchronous(sqlx::sqlite::SqliteSynchronous::Normal)
167 .busy_timeout(std::time::Duration::from_secs(5))
168 .pragma("mmap_size", "268435456");
169 let pool = sqlx::sqlite::SqlitePoolOptions::new()
170 .max_connections(10)
171 .acquire_timeout(std::time::Duration::from_secs(30))
172 .idle_timeout(Some(std::time::Duration::from_secs(600)))
173 .max_lifetime(Some(std::time::Duration::from_secs(1800)))
174 .connect_with(opts)
175 .await
176 .map_err(map_sqlx_error)?;
177 Ok(Self { pool })
178 }
179
180 pub fn from_pool(pool: sqlx::SqlitePool) -> Self {
181 Self { pool }
182 }
183
184 pub fn pool(&self) -> &sqlx::SqlitePool {
185 &self.pool
186 }
187}
188
189pub struct SqlxSqliteConnectionFactory {
190 pool: Arc<SqlitePoolHandle>,
191}
192
193impl SqlxSqliteConnectionFactory {
194 pub fn new(pool: Arc<SqlitePoolHandle>) -> Self {
195 Self { pool }
196 }
197}
198
199#[async_trait]
200impl ConnectionFactory for SqlxSqliteConnectionFactory {
201 async fn create(&self) -> Result<Box<dyn Connection>, DbError> {
202 let conn = self.pool.pool.acquire().await.map_err(map_sqlx_error)?;
203 Ok(Box::new(SqlxSqliteConnection {
204 conn: Some(conn),
205 connected: true,
206 in_transaction: false,
207 }))
208 }
209}
210
211pub struct SqlxSqliteConnection {
212 conn: Option<sqlx::pool::PoolConnection<sqlx::Sqlite>>,
213 connected: bool,
214 in_transaction: bool,
215}
216
217impl Connection for SqlxSqliteConnection {
218 fn execute<'a>(
219 &'a mut self,
220 sql: &'a str,
221 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
222 Box::pin(async move {
223 let mut pool_conn = self
224 .conn
225 .take()
226 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
227 let result = if needs_raw_sql(sql) {
230 (&mut *pool_conn)
231 .execute(sqlx::raw_sql(sqlx::AssertSqlSafe(sql)))
232 .await
233 } else {
234 (&mut *pool_conn).execute(sqlx::AssertSqlSafe(sql)).await
235 };
236 self.conn = Some(pool_conn);
237
238 match result {
239 Ok(r) => Ok(r.rows_affected()),
240 Err(e) => {
241 let db_err = map_sqlx_error(e);
242 if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
243 self.connected = false;
244 }
245 Err(db_err)
246 }
247 }
248 })
249 }
250
251 fn query<'a>(
252 &'a mut self,
253 sql: &'a str,
254 ) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>, DbError>> + Send + 'a>>
255 {
256 Box::pin(async move {
257 let mut pool_conn = self
258 .conn
259 .take()
260 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
261 let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
264 self.conn = Some(pool_conn);
265
266 let rows = rows_result.map_err(map_sqlx_error)?;
267 if rows.is_empty() {
268 return Ok(Vec::new());
269 }
270 let col_types: Vec<ColType> = rows[0]
273 .columns()
274 .iter()
275 .map(|col| ColType::parse_sqlite(col.type_info().name()))
276 .collect();
277 let col_names: Vec<String> = rows[0]
278 .columns()
279 .iter()
280 .map(|col| col.name().to_string())
281 .collect();
282 let mut result = Vec::with_capacity(rows.len());
283 for row in &rows {
284 let mut record = HashMap::with_capacity(col_types.len());
285 for (i, name) in col_names.iter().enumerate() {
286 let value = row_to_value_with_coltype_sqlite(row, i, col_types[i]);
287 record.insert(name.clone(), value);
288 }
289 result.push(record);
290 }
291 Ok(result)
292 })
293 }
294
295 fn begin_transaction<'a>(
296 &'a mut self,
297 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
298 Box::pin(async move {
299 if self.in_transaction {
300 return Err(DbError::Internal("transaction already started".to_string()));
301 }
302 self.execute("BEGIN").await?;
303 self.in_transaction = true;
304 Ok(())
305 })
306 }
307
308 fn commit<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
309 Box::pin(async move {
310 if self.in_transaction {
311 self.execute("COMMIT").await?;
312 self.in_transaction = false;
313 }
314 Ok(())
315 })
316 }
317
318 fn rollback<'a>(
319 &'a mut self,
320 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
321 Box::pin(async move {
322 if self.in_transaction {
323 let result = self.execute("ROLLBACK").await;
324 self.in_transaction = false;
325 result.map(|_| ())
326 } else {
327 Ok(())
328 }
329 })
330 }
331
332 fn is_connected(&self) -> bool {
333 self.connected && self.conn.is_some()
337 }
338
339 fn in_transaction(&self) -> bool {
340 self.in_transaction
341 }
342
343 fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
344 Box::pin(async move {
345 match self.execute("SELECT 1").await {
346 Ok(_) => true,
347 Err(_) => {
348 self.connected = false;
349 false
350 }
351 }
352 })
353 }
354
355 fn close<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
356 Box::pin(async move {
357 if let Some(conn) = self.conn.take() {
358 drop(conn);
359 }
360 self.connected = false;
361 self.in_transaction = false;
362 Ok(())
363 })
364 }
365
366 fn execute_with_params<'a>(
372 &'a mut self,
373 sql: &'a str,
374 params: &'a [Value],
375 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
376 Box::pin(async move {
377 if needs_raw_sql(sql) || params.is_empty() {
378 return self.execute(sql).await;
379 }
380 let mut pool_conn = self
381 .conn
382 .take()
383 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
384 let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
385 for v in params {
386 q = match v {
387 Value::Null => q.bind(None::<i64>),
388 Value::Bool(b) => q.bind(*b),
389 Value::I8(n) => q.bind(*n),
390 Value::I16(n) => q.bind(*n),
391 Value::I32(n) => q.bind(*n),
392 Value::I64(n) => q.bind(*n),
393 Value::U8(n) => q.bind(*n),
394 Value::U16(n) => q.bind(*n),
395 Value::U32(n) => q.bind(*n),
396 Value::U64(n) => q.bind(*n as i64),
397 Value::F32(f) => q.bind(*f),
398 Value::F64(f) => q.bind(*f),
399 Value::String(s) => q.bind(s.as_str()),
400 Value::Decimal(s) => q.bind(s.as_str()),
401 Value::Bytes(b) => q.bind(b.as_slice()),
402 other => q.bind(other.to_string()),
403 };
404 }
405 let result = q.execute(&mut *pool_conn).await;
406 self.conn = Some(pool_conn);
407 match result {
408 Ok(r) => Ok(r.rows_affected()),
409 Err(e) => {
410 let db_err = map_sqlx_error(e);
411 if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
412 self.connected = false;
413 }
414 Err(db_err)
415 }
416 }
417 })
418 }
419
420 fn query_with_params<'a>(
425 &'a mut self,
426 sql: &'a str,
427 params: &'a [Value],
428 ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
429 Box::pin(async move {
430 if params.is_empty() {
431 return self.query(sql).await;
432 }
433 let mut pool_conn = self
434 .conn
435 .take()
436 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
437 let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
438 for v in params {
439 q = match v {
440 Value::Null => q.bind(None::<i64>),
441 Value::Bool(b) => q.bind(*b),
442 Value::I8(n) => q.bind(*n),
443 Value::I16(n) => q.bind(*n),
444 Value::I32(n) => q.bind(*n),
445 Value::I64(n) => q.bind(*n),
446 Value::U8(n) => q.bind(*n),
447 Value::U16(n) => q.bind(*n),
448 Value::U32(n) => q.bind(*n),
449 Value::U64(n) => q.bind(*n as i64),
450 Value::F32(f) => q.bind(*f),
451 Value::F64(f) => q.bind(*f),
452 Value::String(s) => q.bind(s.as_str()),
453 Value::Decimal(s) => q.bind(s.as_str()),
454 Value::Bytes(b) => q.bind(b.as_slice()),
455 other => q.bind(other.to_string()),
456 };
457 }
458 let rows_result = q.fetch_all(&mut *pool_conn).await;
459 self.conn = Some(pool_conn);
460 let rows = rows_result.map_err(map_sqlx_error)?;
461 if rows.is_empty() {
462 return Ok(Vec::new());
463 }
464 let col_types: Vec<ColType> = rows[0]
467 .columns()
468 .iter()
469 .map(|col| ColType::parse_sqlite(col.type_info().name()))
470 .collect();
471 let col_names: Vec<String> = rows[0]
472 .columns()
473 .iter()
474 .map(|col| col.name().to_string())
475 .collect();
476 let mut result = Vec::with_capacity(rows.len());
477 for row in &rows {
478 let mut record = HashMap::with_capacity(col_types.len());
479 for (i, name) in col_names.iter().enumerate() {
480 let value = row_to_value_with_coltype_sqlite(row, i, col_types[i]);
481 record.insert(name.clone(), value);
482 }
483 result.push(record);
484 }
485 Ok(result)
486 })
487 }
488
489 fn query_values<'a>(
495 &'a mut self,
496 sql: &'a str,
497 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
498 Box::pin(async move {
499 let mut pool_conn = self
500 .conn
501 .take()
502 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
503 let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
504 self.conn = Some(pool_conn);
505 let rows = rows_result.map_err(map_sqlx_error)?;
506 if rows.is_empty() {
507 return Ok((Vec::new(), Vec::new()));
508 }
509 let cols = rows[0].columns();
510 let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
511 let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
512 for col in cols {
513 col_names.push(col.name().to_string());
514 col_types.push(ColType::parse_sqlite(col.type_info().name()));
515 }
516 let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
517 for row in &rows {
518 let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
519 for (idx, _) in col_names.iter().enumerate() {
520 row_values.push(row_to_value_with_coltype_sqlite(row, idx, col_types[idx]));
521 }
522 result_rows.push(row_values);
523 }
524 Ok((col_names, result_rows))
525 })
526 }
527
528 fn query_values_with_params<'a>(
532 &'a mut self,
533 sql: &'a str,
534 params: &'a [Value],
535 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
536 Box::pin(async move {
537 if params.is_empty() {
538 return self.query_values(sql).await;
539 }
540 let mut pool_conn = self
541 .conn
542 .take()
543 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
544 let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
545 for v in params {
546 q = match v {
547 Value::Null => q.bind(None::<i64>),
548 Value::Bool(b) => q.bind(*b),
549 Value::I8(n) => q.bind(*n),
550 Value::I16(n) => q.bind(*n),
551 Value::I32(n) => q.bind(*n),
552 Value::I64(n) => q.bind(*n),
553 Value::U8(n) => q.bind(*n),
554 Value::U16(n) => q.bind(*n),
555 Value::U32(n) => q.bind(*n),
556 Value::U64(n) => q.bind(*n as i64),
557 Value::F32(f) => q.bind(*f),
558 Value::F64(f) => q.bind(*f),
559 Value::String(s) => q.bind(s.as_str()),
560 Value::Decimal(s) => q.bind(s.as_str()),
561 Value::Bytes(b) => q.bind(b.as_slice()),
562 other => q.bind(other.to_string()),
563 };
564 }
565 let rows_result = q.fetch_all(&mut *pool_conn).await;
566 self.conn = Some(pool_conn);
567 let rows = rows_result.map_err(map_sqlx_error)?;
568 if rows.is_empty() {
569 return Ok((Vec::new(), Vec::new()));
570 }
571 let cols = rows[0].columns();
572 let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
573 let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
574 for col in cols {
575 col_names.push(col.name().to_string());
576 col_types.push(ColType::parse_sqlite(col.type_info().name()));
577 }
578 let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
579 for row in &rows {
580 let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
581 for (idx, _) in col_names.iter().enumerate() {
582 row_values.push(row_to_value_with_coltype_sqlite(row, idx, col_types[idx]));
583 }
584 result_rows.push(row_values);
585 }
586 Ok((col_names, result_rows))
587 })
588 }
589
590 fn query_stream<'a>(
596 &'a mut self,
597 sql: &'a str,
598 ) -> Pin<Box<dyn futures::Stream<Item = Result<HashMap<String, Value>, DbError>> + Send + 'a>>
599 {
600 Box::pin(async_stream::try_stream! {
601 let mut pool_conn = self
602 .conn
603 .take()
604 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
605 let mut row_stream = sqlx::query(sqlx::AssertSqlSafe(sql)).fetch(&mut *pool_conn);
606 let mut col_types: Vec<ColType> = Vec::new();
607 let mut col_names: Vec<String> = Vec::new();
608 let mut first_row = true;
609 while let Some(row_result) = row_stream.next().await {
610 let row = row_result.map_err(map_sqlx_error)?;
611 if first_row {
612 for col in row.columns() {
613 col_names.push(col.name().to_string());
614 col_types.push(ColType::parse_sqlite(col.type_info().name()));
615 }
616 first_row = false;
617 }
618 let mut record = HashMap::with_capacity(col_names.len());
619 for (i, name) in col_names.iter().enumerate() {
620 let value = row_to_value_with_coltype_sqlite(&row, i, col_types[i]);
621 record.insert(name.clone(), value);
622 }
623 yield record;
624 }
625 drop(row_stream);
628 self.conn = Some(pool_conn);
629 })
630 }
631}
632
633impl Drop for SqlxSqliteConnection {
634 fn drop(&mut self) {
635 if let Some(conn) = self.conn.take() {
636 drop(conn);
637 }
638 }
639}
640
641pub async fn sqlite_backup(
646 conn: &mut SqlxSqliteConnection,
647 dest_path: &str,
648) -> Result<(), DbError> {
649 let escaped_path = dest_path.replace('\'', "''");
650 let sql = format!("VACUUM INTO '{}'", escaped_path);
651 conn.execute(&sql).await?;
652 Ok(())
653}
654
655fn row_to_value_mysql(row: &sqlx::mysql::MySqlRow, ordinal: usize) -> Value {
661 use sqlx::TypeInfo;
662 let type_name = row.columns()[ordinal].type_info().name();
663 match type_name {
664 "BOOLEAN" => match row.try_get::<Option<bool>, usize>(ordinal) {
665 Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
666 Err(_) => Value::Null,
667 },
668 "TINYINT" | "TINYINT UNSIGNED" => match row.try_get::<Option<i8>, usize>(ordinal) {
669 Ok(v) => v.map(Value::I8).unwrap_or(Value::Null),
670 Err(_) => match row.try_get::<Option<u8>, usize>(ordinal) {
671 Ok(v) => v.map(Value::U8).unwrap_or(Value::Null),
672 Err(_) => Value::Null,
673 },
674 },
675 "SMALLINT" | "SMALLINT UNSIGNED" => match row.try_get::<Option<i16>, usize>(ordinal) {
676 Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
677 Err(_) => match row.try_get::<Option<u16>, usize>(ordinal) {
678 Ok(v) => v.map(Value::U16).unwrap_or(Value::Null),
679 Err(_) => Value::Null,
680 },
681 },
682 "INT" | "INT UNSIGNED" | "MEDIUMINT" | "MEDIUMINT UNSIGNED" => {
683 match row.try_get::<Option<i32>, usize>(ordinal) {
684 Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
685 Err(_) => match row.try_get::<Option<u32>, usize>(ordinal) {
686 Ok(v) => v.map(Value::U32).unwrap_or(Value::Null),
687 Err(_) => Value::Null,
688 },
689 }
690 }
691 "BIGINT" | "BIGINT UNSIGNED" => match row.try_get::<Option<i64>, usize>(ordinal) {
692 Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
693 Err(_) => match row.try_get::<Option<u64>, usize>(ordinal) {
694 Ok(v) => v.map(Value::U64).unwrap_or(Value::Null),
695 Err(_) => Value::Null,
696 },
697 },
698 "FLOAT" => match row.try_get::<Option<f32>, usize>(ordinal) {
699 Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
700 Err(_) => Value::Null,
701 },
702 "DOUBLE" => match row.try_get::<Option<f64>, usize>(ordinal) {
703 Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
704 Err(_) => Value::Null,
705 },
706 "VARCHAR" | "TEXT" | "CHAR" | "TINYTEXT" | "MEDIUMTEXT" | "LONGTEXT" | "ENUM" => {
707 match row.try_get::<Option<String>, usize>(ordinal) {
708 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
709 Err(_) => Value::Null,
710 }
711 }
712 "BLOB" | "TINYBLOB" | "MEDIUMBLOB" | "LONGBLOB" | "BINARY" | "VARBINARY" => {
713 match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
714 Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
715 Err(_) => Value::Null,
716 }
717 }
718 "DECIMAL" | "NUMERIC" | "NEWDECIMAL" => {
720 match row.try_get::<Option<rust_decimal::Decimal>, usize>(ordinal) {
721 Ok(Some(v)) => Value::Decimal(v.to_string()),
722 Ok(None) => Value::Null,
723 Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
724 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
725 Err(_) => Value::Null,
726 },
727 }
728 }
729 _ => {
730 if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
732 return v.map(Value::I64).unwrap_or(Value::Null);
733 }
734 if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
735 return v.map(Value::F64).unwrap_or(Value::Null);
736 }
737 if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
738 return v.map(Value::Bool).unwrap_or(Value::Null);
739 }
740 if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
741 return v.map(Value::String).unwrap_or(Value::Null);
742 }
743 Value::Null
744 }
745 }
746}
747
748fn row_to_value_with_coltype_mysql(
756 row: &sqlx::mysql::MySqlRow,
757 ordinal: usize,
758 col_type: ColType,
759) -> Value {
760 match col_type {
761 ColType::Bool => match row.try_get::<Option<bool>, usize>(ordinal) {
762 Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
763 Err(_) => Value::Null,
764 },
765 ColType::I8 => match row.try_get::<Option<i8>, usize>(ordinal) {
766 Ok(v) => v.map(Value::I8).unwrap_or(Value::Null),
767 Err(_) => match row.try_get::<Option<u8>, usize>(ordinal) {
768 Ok(v) => v.map(Value::U8).unwrap_or(Value::Null),
769 Err(_) => Value::Null,
770 },
771 },
772 ColType::I16 => match row.try_get::<Option<i16>, usize>(ordinal) {
773 Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
774 Err(_) => match row.try_get::<Option<u16>, usize>(ordinal) {
775 Ok(v) => v.map(Value::U16).unwrap_or(Value::Null),
776 Err(_) => Value::Null,
777 },
778 },
779 ColType::I32 => match row.try_get::<Option<i32>, usize>(ordinal) {
780 Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
781 Err(_) => match row.try_get::<Option<u32>, usize>(ordinal) {
782 Ok(v) => v.map(Value::U32).unwrap_or(Value::Null),
783 Err(_) => Value::Null,
784 },
785 },
786 ColType::I64 => match row.try_get::<Option<i64>, usize>(ordinal) {
787 Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
788 Err(_) => match row.try_get::<Option<u64>, usize>(ordinal) {
789 Ok(v) => v.map(Value::U64).unwrap_or(Value::Null),
790 Err(_) => Value::Null,
791 },
792 },
793 ColType::U8 => match row.try_get::<Option<u8>, usize>(ordinal) {
794 Ok(v) => v.map(Value::U8).unwrap_or(Value::Null),
795 Err(_) => Value::Null,
796 },
797 ColType::U16 => match row.try_get::<Option<u16>, usize>(ordinal) {
798 Ok(v) => v.map(Value::U16).unwrap_or(Value::Null),
799 Err(_) => Value::Null,
800 },
801 ColType::U32 => match row.try_get::<Option<u32>, usize>(ordinal) {
802 Ok(v) => v.map(Value::U32).unwrap_or(Value::Null),
803 Err(_) => Value::Null,
804 },
805 ColType::U64 => match row.try_get::<Option<u64>, usize>(ordinal) {
806 Ok(v) => v.map(Value::U64).unwrap_or(Value::Null),
807 Err(_) => Value::Null,
808 },
809 ColType::F32 => match row.try_get::<Option<f32>, usize>(ordinal) {
810 Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
811 Err(_) => Value::Null,
812 },
813 ColType::F64 => match row.try_get::<Option<f64>, usize>(ordinal) {
814 Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
815 Err(_) => Value::Null,
816 },
817 ColType::Decimal => match row.try_get::<Option<rust_decimal::Decimal>, usize>(ordinal) {
819 Ok(Some(v)) => Value::Decimal(v.to_string()),
820 Ok(None) => Value::Null,
821 Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
822 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
823 Err(_) => Value::Null,
824 },
825 },
826 ColType::String => match row.try_get::<Option<String>, usize>(ordinal) {
827 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
828 Err(_) => Value::Null,
829 },
830 ColType::Bytes => match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
831 Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
832 Err(_) => Value::Null,
833 },
834 ColType::Date | ColType::DateTime | ColType::Time | ColType::Json | ColType::Uuid => {
836 match row.try_get::<Option<String>, usize>(ordinal) {
837 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
838 Err(_) => Value::Null,
839 }
840 }
841 ColType::Unknown => {
842 if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
844 return v.map(Value::I64).unwrap_or(Value::Null);
845 }
846 if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
847 return v.map(Value::F64).unwrap_or(Value::Null);
848 }
849 if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
850 return v.map(Value::Bool).unwrap_or(Value::Null);
851 }
852 if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
853 return v.map(Value::String).unwrap_or(Value::Null);
854 }
855 Value::Null
856 }
857 _ => Value::Null,
859 }
860}
861
862pub struct MySqlPoolHandle {
863 pool: sqlx::MySqlPool,
864}
865
866impl MySqlPoolHandle {
867 pub async fn connect(url: &str) -> Result<Self, DbError> {
868 let pool = sqlx::pool::PoolOptions::<sqlx::MySql>::new()
869 .max_connections(10)
870 .acquire_timeout(std::time::Duration::from_secs(30))
871 .idle_timeout(Some(std::time::Duration::from_secs(600)))
872 .max_lifetime(Some(std::time::Duration::from_secs(1800)))
873 .connect(url)
874 .await
875 .map_err(map_sqlx_error)?;
876 Ok(Self { pool })
877 }
878
879 pub fn from_pool(pool: sqlx::MySqlPool) -> Self {
880 Self { pool }
881 }
882
883 pub fn pool(&self) -> &sqlx::MySqlPool {
884 &self.pool
885 }
886}
887
888pub struct SqlxMySqlConnectionFactory {
889 pool: Arc<MySqlPoolHandle>,
890}
891
892impl SqlxMySqlConnectionFactory {
893 pub fn new(pool: Arc<MySqlPoolHandle>) -> Self {
894 Self { pool }
895 }
896}
897
898#[async_trait]
899impl ConnectionFactory for SqlxMySqlConnectionFactory {
900 async fn create(&self) -> Result<Box<dyn Connection>, DbError> {
901 let conn = self.pool.pool.acquire().await.map_err(map_sqlx_error)?;
902 Ok(Box::new(SqlxMySqlConnection {
903 conn: Some(conn),
904 connected: true,
905 in_transaction: false,
906 }))
907 }
908}
909
910pub struct SqlxMySqlConnection {
911 conn: Option<sqlx::pool::PoolConnection<sqlx::MySql>>,
912 connected: bool,
913 in_transaction: bool,
914}
915
916impl Connection for SqlxMySqlConnection {
917 fn execute<'a>(
918 &'a mut self,
919 sql: &'a str,
920 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
921 Box::pin(async move {
922 let mut pool_conn = self
923 .conn
924 .take()
925 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
926 let result = if needs_raw_sql(sql) {
929 (&mut *pool_conn)
930 .execute(sqlx::raw_sql(sqlx::AssertSqlSafe(sql)))
931 .await
932 } else {
933 (&mut *pool_conn).execute(sqlx::AssertSqlSafe(sql)).await
934 };
935 self.conn = Some(pool_conn);
936
937 match result {
938 Ok(r) => Ok(r.rows_affected()),
939 Err(e) => {
940 let db_err = map_sqlx_error(e);
941 if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
942 self.connected = false;
943 }
944 Err(db_err)
945 }
946 }
947 })
948 }
949
950 fn query<'a>(
951 &'a mut self,
952 sql: &'a str,
953 ) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>, DbError>> + Send + 'a>>
954 {
955 Box::pin(async move {
956 let mut pool_conn = self
957 .conn
958 .take()
959 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
960 let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
963 self.conn = Some(pool_conn);
964
965 let rows = rows_result.map_err(map_sqlx_error)?;
966 if rows.is_empty() {
967 return Ok(Vec::new());
968 }
969 let col_types: Vec<ColType> = rows[0]
972 .columns()
973 .iter()
974 .map(|col| ColType::parse_mysql(col.type_info().name()))
975 .collect();
976 let col_names: Vec<String> = rows[0]
977 .columns()
978 .iter()
979 .map(|col| col.name().to_string())
980 .collect();
981 let mut result = Vec::with_capacity(rows.len());
982 for row in &rows {
983 let mut record = HashMap::with_capacity(col_types.len());
984 for (i, name) in col_names.iter().enumerate() {
985 let value = row_to_value_with_coltype_mysql(row, i, col_types[i]);
986 record.insert(name.clone(), value);
987 }
988 result.push(record);
989 }
990 Ok(result)
991 })
992 }
993
994 fn begin_transaction<'a>(
995 &'a mut self,
996 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
997 Box::pin(async move {
998 if self.in_transaction {
999 return Err(DbError::Internal("transaction already started".to_string()));
1000 }
1001 self.execute("BEGIN").await?;
1002 self.in_transaction = true;
1003 Ok(())
1004 })
1005 }
1006
1007 fn commit<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1008 Box::pin(async move {
1009 if self.in_transaction {
1010 self.execute("COMMIT").await?;
1011 self.in_transaction = false;
1012 }
1013 Ok(())
1014 })
1015 }
1016
1017 fn rollback<'a>(
1018 &'a mut self,
1019 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1020 Box::pin(async move {
1021 if self.in_transaction {
1022 let result = self.execute("ROLLBACK").await;
1023 self.in_transaction = false;
1024 result.map(|_| ())
1025 } else {
1026 Ok(())
1027 }
1028 })
1029 }
1030
1031 fn is_connected(&self) -> bool {
1032 self.connected && self.conn.is_some()
1036 }
1037
1038 fn in_transaction(&self) -> bool {
1039 self.in_transaction
1040 }
1041
1042 fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
1043 Box::pin(async move {
1044 match self.execute("SELECT 1").await {
1045 Ok(_) => true,
1046 Err(_) => {
1047 self.connected = false;
1048 false
1049 }
1050 }
1051 })
1052 }
1053
1054 fn close<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1055 Box::pin(async move {
1056 if let Some(conn) = self.conn.take() {
1057 drop(conn);
1058 }
1059 self.connected = false;
1060 self.in_transaction = false;
1061 Ok(())
1062 })
1063 }
1064
1065 fn execute_with_params<'a>(
1071 &'a mut self,
1072 sql: &'a str,
1073 params: &'a [Value],
1074 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
1075 Box::pin(async move {
1076 if needs_raw_sql(sql) || params.is_empty() {
1077 return self.execute(sql).await;
1078 }
1079 let mut pool_conn = self
1080 .conn
1081 .take()
1082 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1083 let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1084 for v in params {
1085 q = match v {
1086 Value::Null => q.bind(None::<i64>),
1087 Value::Bool(b) => q.bind(*b),
1088 Value::I8(n) => q.bind(*n),
1089 Value::I16(n) => q.bind(*n),
1090 Value::I32(n) => q.bind(*n),
1091 Value::I64(n) => q.bind(*n),
1092 Value::U8(n) => q.bind(*n),
1093 Value::U16(n) => q.bind(*n),
1094 Value::U32(n) => q.bind(*n),
1095 Value::U64(n) => q.bind(*n as i64),
1096 Value::F32(f) => q.bind(*f),
1097 Value::F64(f) => q.bind(*f),
1098 Value::String(s) => q.bind(s.as_str()),
1099 Value::Decimal(s) => q.bind(s.as_str()),
1100 Value::Bytes(b) => q.bind(b.as_slice()),
1101 other => q.bind(other.to_string()),
1102 };
1103 }
1104 let result = q.execute(&mut *pool_conn).await;
1105 self.conn = Some(pool_conn);
1106 match result {
1107 Ok(r) => Ok(r.rows_affected()),
1108 Err(e) => {
1109 let db_err = map_sqlx_error(e);
1110 if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
1111 self.connected = false;
1112 }
1113 Err(db_err)
1114 }
1115 }
1116 })
1117 }
1118
1119 fn query_with_params<'a>(
1121 &'a mut self,
1122 sql: &'a str,
1123 params: &'a [Value],
1124 ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
1125 Box::pin(async move {
1126 if params.is_empty() {
1127 return self.query(sql).await;
1128 }
1129 let mut pool_conn = self
1130 .conn
1131 .take()
1132 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1133 let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1134 for v in params {
1135 q = match v {
1136 Value::Null => q.bind(None::<i64>),
1137 Value::Bool(b) => q.bind(*b),
1138 Value::I8(n) => q.bind(*n),
1139 Value::I16(n) => q.bind(*n),
1140 Value::I32(n) => q.bind(*n),
1141 Value::I64(n) => q.bind(*n),
1142 Value::U8(n) => q.bind(*n),
1143 Value::U16(n) => q.bind(*n),
1144 Value::U32(n) => q.bind(*n),
1145 Value::U64(n) => q.bind(*n as i64),
1146 Value::F32(f) => q.bind(*f),
1147 Value::F64(f) => q.bind(*f),
1148 Value::String(s) => q.bind(s.as_str()),
1149 Value::Decimal(s) => q.bind(s.as_str()),
1150 Value::Bytes(b) => q.bind(b.as_slice()),
1151 other => q.bind(other.to_string()),
1152 };
1153 }
1154 let rows_result = q.fetch_all(&mut *pool_conn).await;
1155 self.conn = Some(pool_conn);
1156 let rows = rows_result.map_err(map_sqlx_error)?;
1157 if rows.is_empty() {
1158 return Ok(Vec::new());
1159 }
1160 let col_names: Vec<String> = rows[0]
1161 .columns()
1162 .iter()
1163 .map(|col| col.name().to_string())
1164 .collect();
1165 let mut result = Vec::with_capacity(rows.len());
1166 for row in &rows {
1167 let mut record = HashMap::with_capacity(col_names.len());
1168 for (i, name) in col_names.iter().enumerate() {
1169 let value = row_to_value_mysql(row, i);
1170 record.insert(name.clone(), value);
1171 }
1172 result.push(record);
1173 }
1174 Ok(result)
1175 })
1176 }
1177
1178 fn query_values<'a>(
1182 &'a mut self,
1183 sql: &'a str,
1184 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
1185 Box::pin(async move {
1186 let mut pool_conn = self
1187 .conn
1188 .take()
1189 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1190 let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
1191 self.conn = Some(pool_conn);
1192 let rows = rows_result.map_err(map_sqlx_error)?;
1193 if rows.is_empty() {
1194 return Ok((Vec::new(), Vec::new()));
1195 }
1196 let cols = rows[0].columns();
1197 let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
1198 let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
1199 for col in cols {
1200 col_names.push(col.name().to_string());
1201 col_types.push(ColType::parse_mysql(col.type_info().name()));
1202 }
1203 let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1204 for row in &rows {
1205 let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
1206 for (idx, _) in col_names.iter().enumerate() {
1207 row_values.push(row_to_value_with_coltype_mysql(row, idx, col_types[idx]));
1208 }
1209 result_rows.push(row_values);
1210 }
1211 Ok((col_names, result_rows))
1212 })
1213 }
1214
1215 fn query_values_with_params<'a>(
1217 &'a mut self,
1218 sql: &'a str,
1219 params: &'a [Value],
1220 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
1221 Box::pin(async move {
1222 if params.is_empty() {
1223 return self.query_values(sql).await;
1224 }
1225 let mut pool_conn = self
1226 .conn
1227 .take()
1228 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1229 let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1230 for v in params {
1231 q = match v {
1232 Value::Null => q.bind(None::<i64>),
1233 Value::Bool(b) => q.bind(*b),
1234 Value::I8(n) => q.bind(*n),
1235 Value::I16(n) => q.bind(*n),
1236 Value::I32(n) => q.bind(*n),
1237 Value::I64(n) => q.bind(*n),
1238 Value::U8(n) => q.bind(*n),
1239 Value::U16(n) => q.bind(*n),
1240 Value::U32(n) => q.bind(*n),
1241 Value::U64(n) => q.bind(*n as i64),
1242 Value::F32(f) => q.bind(*f),
1243 Value::F64(f) => q.bind(*f),
1244 Value::String(s) => q.bind(s.as_str()),
1245 Value::Decimal(s) => q.bind(s.as_str()),
1246 Value::Bytes(b) => q.bind(b.as_slice()),
1247 other => q.bind(other.to_string()),
1248 };
1249 }
1250 let rows_result = q.fetch_all(&mut *pool_conn).await;
1251 self.conn = Some(pool_conn);
1252 let rows = rows_result.map_err(map_sqlx_error)?;
1253 if rows.is_empty() {
1254 return Ok((Vec::new(), Vec::new()));
1255 }
1256 let cols = rows[0].columns();
1257 let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
1258 let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
1259 for col in cols {
1260 col_names.push(col.name().to_string());
1261 col_types.push(ColType::parse_mysql(col.type_info().name()));
1262 }
1263 let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1264 for row in &rows {
1265 let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
1266 for (idx, _) in col_names.iter().enumerate() {
1267 row_values.push(row_to_value_with_coltype_mysql(row, idx, col_types[idx]));
1268 }
1269 result_rows.push(row_values);
1270 }
1271 Ok((col_names, result_rows))
1272 })
1273 }
1274
1275 fn query_stream<'a>(
1281 &'a mut self,
1282 sql: &'a str,
1283 ) -> Pin<Box<dyn futures::Stream<Item = Result<HashMap<String, Value>, DbError>> + Send + 'a>>
1284 {
1285 Box::pin(async_stream::try_stream! {
1286 let mut pool_conn = self
1287 .conn
1288 .take()
1289 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1290 let mut row_stream = sqlx::query(sqlx::AssertSqlSafe(sql)).fetch(&mut *pool_conn);
1291 let mut col_types: Vec<ColType> = Vec::new();
1292 let mut col_names: Vec<String> = Vec::new();
1293 let mut first_row = true;
1294 while let Some(row_result) = row_stream.next().await {
1295 let row = row_result.map_err(map_sqlx_error)?;
1296 if first_row {
1297 for col in row.columns() {
1298 col_names.push(col.name().to_string());
1299 col_types.push(ColType::parse_mysql(col.type_info().name()));
1300 }
1301 first_row = false;
1302 }
1303 let mut record = HashMap::with_capacity(col_names.len());
1304 for (i, name) in col_names.iter().enumerate() {
1305 let value = row_to_value_with_coltype_mysql(&row, i, col_types[i]);
1306 record.insert(name.clone(), value);
1307 }
1308 yield record;
1309 }
1310 drop(row_stream);
1313 self.conn = Some(pool_conn);
1314 })
1315 }
1316}
1317
1318impl Drop for SqlxMySqlConnection {
1319 fn drop(&mut self) {
1320 if let Some(conn) = self.conn.take() {
1321 drop(conn);
1322 }
1323 }
1324}
1325
1326pub async fn mysql_bulk_insert(
1333 conn: &mut SqlxMySqlConnection,
1334 table: &str,
1335 columns: &[&str],
1336 rows: &[Vec<Value>],
1337) -> Result<u64, DbError> {
1338 if rows.is_empty() {
1339 return Ok(0);
1340 }
1341 let col_list = columns.join(", ");
1342 let cols_per_row = columns.len();
1343 let row_placeholder = format!("({})", vec!["?"; cols_per_row].join(", "));
1345 let placeholders = vec![row_placeholder; rows.len()].join(", ");
1346 let sql = format!(
1347 "INSERT INTO {} ({}) VALUES {}",
1348 table, col_list, placeholders
1349 );
1350 let mut params: Vec<Value> = Vec::with_capacity(rows.len() * cols_per_row);
1352 for row in rows {
1353 for v in row {
1354 params.push(v.clone());
1355 }
1356 }
1357 conn.execute_with_params(&sql, ¶ms).await
1358}
1359
1360fn row_to_value_pg(row: &sqlx::postgres::PgRow, ordinal: usize) -> Value {
1366 use sqlx::TypeInfo;
1367 let type_name = row.columns()[ordinal].type_info().name();
1368 match type_name {
1369 "BOOL" => match row.try_get::<Option<bool>, usize>(ordinal) {
1370 Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
1371 Err(_) => Value::Null,
1372 },
1373 "INT2" => match row.try_get::<Option<i16>, usize>(ordinal) {
1374 Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
1375 Err(_) => Value::Null,
1376 },
1377 "INT4" | "OID" => match row.try_get::<Option<i32>, usize>(ordinal) {
1378 Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
1379 Err(_) => Value::Null,
1380 },
1381 "INT8" => match row.try_get::<Option<i64>, usize>(ordinal) {
1382 Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
1383 Err(_) => Value::Null,
1384 },
1385 "FLOAT4" => match row.try_get::<Option<f32>, usize>(ordinal) {
1386 Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
1387 Err(_) => Value::Null,
1388 },
1389 "FLOAT8" => match row.try_get::<Option<f64>, usize>(ordinal) {
1390 Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
1391 Err(_) => Value::Null,
1392 },
1393 "TEXT" | "VARCHAR" | "CHAR" | "NAME" => match row.try_get::<Option<String>, usize>(ordinal)
1394 {
1395 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1396 Err(_) => Value::Null,
1397 },
1398 "BYTEA" => match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
1399 Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
1400 Err(_) => Value::Null,
1401 },
1402 "NUMERIC" => match row.try_get::<Option<rust_decimal::Decimal>, usize>(ordinal) {
1403 Ok(Some(v)) => Value::Decimal(v.to_string()),
1404 Ok(None) => Value::Null,
1405 Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
1406 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1407 Err(_) => Value::Null,
1408 },
1409 },
1410 "UUID" => match row.try_get::<Option<sqlx::types::Uuid>, usize>(ordinal) {
1412 Ok(v) => v
1413 .map(|uuid| Value::String(uuid.to_string()))
1414 .unwrap_or(Value::Null),
1415 Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
1416 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1417 Err(_) => Value::Null,
1418 },
1419 },
1420 _ => {
1421 if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
1423 return v.map(Value::I64).unwrap_or(Value::Null);
1424 }
1425 if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
1426 return v.map(Value::F64).unwrap_or(Value::Null);
1427 }
1428 if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
1429 return v.map(Value::Bool).unwrap_or(Value::Null);
1430 }
1431 if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
1432 return v.map(Value::String).unwrap_or(Value::Null);
1433 }
1434 Value::Null
1435 }
1436 }
1437}
1438
1439fn row_to_value_with_coltype_pg(
1447 row: &sqlx::postgres::PgRow,
1448 ordinal: usize,
1449 col_type: ColType,
1450) -> Value {
1451 match col_type {
1452 ColType::Bool => match row.try_get::<Option<bool>, usize>(ordinal) {
1453 Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
1454 Err(_) => Value::Null,
1455 },
1456 ColType::I8 => match row.try_get::<Option<i8>, usize>(ordinal) {
1457 Ok(v) => v.map(Value::I8).unwrap_or(Value::Null),
1458 Err(_) => Value::Null,
1459 },
1460 ColType::I16 => match row.try_get::<Option<i16>, usize>(ordinal) {
1461 Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
1462 Err(_) => Value::Null,
1463 },
1464 ColType::I32 => match row.try_get::<Option<i32>, usize>(ordinal) {
1466 Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
1467 Err(_) => Value::Null,
1468 },
1469 ColType::I64 => match row.try_get::<Option<i64>, usize>(ordinal) {
1470 Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
1471 Err(_) => Value::Null,
1472 },
1473 ColType::U8 => match row.try_get::<Option<i16>, usize>(ordinal) {
1478 Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
1479 Err(_) => Value::Null,
1480 },
1481 ColType::U16 => match row.try_get::<Option<i32>, usize>(ordinal) {
1482 Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
1483 Err(_) => Value::Null,
1484 },
1485 ColType::U32 => match row.try_get::<Option<i64>, usize>(ordinal) {
1486 Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
1487 Err(_) => Value::Null,
1488 },
1489 ColType::U64 => match row.try_get::<Option<i64>, usize>(ordinal) {
1491 Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
1492 Err(_) => Value::Null,
1493 },
1494 ColType::F32 => match row.try_get::<Option<f32>, usize>(ordinal) {
1495 Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
1496 Err(_) => Value::Null,
1497 },
1498 ColType::F64 => match row.try_get::<Option<f64>, usize>(ordinal) {
1499 Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
1500 Err(_) => Value::Null,
1501 },
1502 ColType::Decimal => match row.try_get::<Option<rust_decimal::Decimal>, usize>(ordinal) {
1504 Ok(Some(v)) => Value::Decimal(v.to_string()),
1505 Ok(None) => Value::Null,
1506 Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
1507 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1508 Err(_) => Value::Null,
1509 },
1510 },
1511 ColType::String => match row.try_get::<Option<String>, usize>(ordinal) {
1512 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1513 Err(_) => Value::Null,
1514 },
1515 ColType::Bytes => match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
1516 Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
1517 Err(_) => Value::Null,
1518 },
1519 ColType::Date | ColType::DateTime | ColType::Time | ColType::Json => {
1521 match row.try_get::<Option<String>, usize>(ordinal) {
1522 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1523 Err(_) => Value::Null,
1524 }
1525 }
1526 ColType::Uuid => match row.try_get::<Option<sqlx::types::Uuid>, usize>(ordinal) {
1528 Ok(v) => v
1529 .map(|uuid| Value::String(uuid.to_string()))
1530 .unwrap_or(Value::Null),
1531 Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
1532 Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1533 Err(_) => Value::Null,
1534 },
1535 },
1536 ColType::Unknown => {
1537 if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
1539 return v.map(Value::I64).unwrap_or(Value::Null);
1540 }
1541 if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
1542 return v.map(Value::F64).unwrap_or(Value::Null);
1543 }
1544 if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
1545 return v.map(Value::Bool).unwrap_or(Value::Null);
1546 }
1547 if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
1548 return v.map(Value::String).unwrap_or(Value::Null);
1549 }
1550 Value::Null
1551 }
1552 _ => Value::Null,
1554 }
1555}
1556
1557pub struct PgPoolHandle {
1558 pool: sqlx::PgPool,
1559}
1560
1561impl PgPoolHandle {
1562 pub async fn connect(url: &str) -> Result<Self, DbError> {
1563 let pool = sqlx::pool::PoolOptions::<sqlx::Postgres>::new()
1564 .max_connections(10)
1565 .acquire_timeout(std::time::Duration::from_secs(30))
1566 .idle_timeout(Some(std::time::Duration::from_secs(600)))
1567 .max_lifetime(Some(std::time::Duration::from_secs(1800)))
1568 .connect(url)
1569 .await
1570 .map_err(map_sqlx_error)?;
1571 Ok(Self { pool })
1572 }
1573
1574 pub fn from_pool(pool: sqlx::PgPool) -> Self {
1575 Self { pool }
1576 }
1577
1578 pub fn pool(&self) -> &sqlx::PgPool {
1579 &self.pool
1580 }
1581}
1582
1583pub struct SqlxPgConnectionFactory {
1584 pool: Arc<PgPoolHandle>,
1585}
1586
1587impl SqlxPgConnectionFactory {
1588 pub fn new(pool: Arc<PgPoolHandle>) -> Self {
1589 Self { pool }
1590 }
1591}
1592
1593#[async_trait]
1594impl ConnectionFactory for SqlxPgConnectionFactory {
1595 async fn create(&self) -> Result<Box<dyn Connection>, DbError> {
1596 let conn = self.pool.pool.acquire().await.map_err(map_sqlx_error)?;
1597 Ok(Box::new(SqlxPgConnection {
1598 conn: Some(conn),
1599 connected: true,
1600 in_transaction: false,
1601 }))
1602 }
1603}
1604
1605pub struct SqlxPgConnection {
1606 conn: Option<sqlx::pool::PoolConnection<sqlx::Postgres>>,
1607 connected: bool,
1608 in_transaction: bool,
1609}
1610
1611impl Connection for SqlxPgConnection {
1612 fn execute<'a>(
1613 &'a mut self,
1614 sql: &'a str,
1615 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
1616 Box::pin(async move {
1617 let mut pool_conn = self
1618 .conn
1619 .take()
1620 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1621 let result = if needs_raw_sql(sql) {
1624 (&mut *pool_conn)
1625 .execute(sqlx::raw_sql(sqlx::AssertSqlSafe(sql)))
1626 .await
1627 } else {
1628 (&mut *pool_conn).execute(sqlx::AssertSqlSafe(sql)).await
1629 };
1630 self.conn = Some(pool_conn);
1631
1632 match result {
1633 Ok(r) => Ok(r.rows_affected()),
1634 Err(e) => {
1635 let db_err = map_sqlx_error(e);
1636 if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
1637 self.connected = false;
1638 }
1639 Err(db_err)
1640 }
1641 }
1642 })
1643 }
1644
1645 fn query<'a>(
1646 &'a mut self,
1647 sql: &'a str,
1648 ) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>, DbError>> + Send + 'a>>
1649 {
1650 Box::pin(async move {
1651 let mut pool_conn = self
1652 .conn
1653 .take()
1654 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1655 let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
1658 self.conn = Some(pool_conn);
1659
1660 let rows = rows_result.map_err(map_sqlx_error)?;
1661 if rows.is_empty() {
1662 return Ok(Vec::new());
1663 }
1664 let col_types: Vec<ColType> = rows[0]
1667 .columns()
1668 .iter()
1669 .map(|col| ColType::parse_postgres(col.type_info().name()))
1670 .collect();
1671 let col_names: Vec<String> = rows[0]
1672 .columns()
1673 .iter()
1674 .map(|col| col.name().to_string())
1675 .collect();
1676 let mut result = Vec::with_capacity(rows.len());
1677 for row in &rows {
1678 let mut record = HashMap::with_capacity(col_types.len());
1679 for (i, name) in col_names.iter().enumerate() {
1680 let value = row_to_value_with_coltype_pg(row, i, col_types[i]);
1681 record.insert(name.clone(), value);
1682 }
1683 result.push(record);
1684 }
1685 Ok(result)
1686 })
1687 }
1688
1689 fn begin_transaction<'a>(
1690 &'a mut self,
1691 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1692 Box::pin(async move {
1693 if self.in_transaction {
1694 return Err(DbError::Internal("transaction already started".to_string()));
1695 }
1696 self.execute("BEGIN").await?;
1697 self.in_transaction = true;
1698 Ok(())
1699 })
1700 }
1701
1702 fn commit<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1703 Box::pin(async move {
1704 if self.in_transaction {
1705 self.execute("COMMIT").await?;
1706 self.in_transaction = false;
1707 }
1708 Ok(())
1709 })
1710 }
1711
1712 fn rollback<'a>(
1713 &'a mut self,
1714 ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1715 Box::pin(async move {
1716 if self.in_transaction {
1717 let result = self.execute("ROLLBACK").await;
1718 self.in_transaction = false;
1719 result.map(|_| ())
1720 } else {
1721 Ok(())
1722 }
1723 })
1724 }
1725
1726 fn is_connected(&self) -> bool {
1727 self.connected && self.conn.is_some()
1731 }
1732
1733 fn in_transaction(&self) -> bool {
1734 self.in_transaction
1735 }
1736
1737 fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
1738 Box::pin(async move {
1739 match self.execute("SELECT 1").await {
1740 Ok(_) => true,
1741 Err(_) => {
1742 self.connected = false;
1743 false
1744 }
1745 }
1746 })
1747 }
1748
1749 fn close<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1750 Box::pin(async move {
1751 if let Some(conn) = self.conn.take() {
1752 drop(conn);
1753 }
1754 self.connected = false;
1755 self.in_transaction = false;
1756 Ok(())
1757 })
1758 }
1759
1760 fn execute_with_params<'a>(
1775 &'a mut self,
1776 sql: &'a str,
1777 params: &'a [Value],
1778 ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
1779 Box::pin(async move {
1780 if needs_raw_sql(sql) || params.is_empty() {
1781 return self.execute(sql).await;
1782 }
1783 let mut pool_conn = self
1784 .conn
1785 .take()
1786 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1787 let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1788 for v in params {
1789 q = match v {
1790 Value::Null => q.bind(None::<i64>),
1791 Value::Bool(b) => q.bind(*b),
1792 Value::I8(n) => q.bind(*n),
1793 Value::I16(n) => q.bind(*n),
1794 Value::I32(n) => q.bind(*n),
1795 Value::I64(n) => q.bind(*n),
1796 Value::U8(n) => q.bind(*n as i16),
1798 Value::U16(n) => q.bind(*n as i32),
1799 Value::U32(n) => q.bind(*n as i64),
1800 Value::U64(n) => q.bind(*n as i64),
1801 Value::F32(f) => q.bind(*f),
1802 Value::F64(f) => q.bind(*f),
1803 Value::String(s) => q.bind(s.as_str()),
1804 Value::Decimal(s) => q.bind(s.as_str()),
1805 Value::Bytes(b) => q.bind(b.as_slice()),
1806 other => q.bind(other.to_string()),
1807 };
1808 }
1809 let result = q.execute(&mut *pool_conn).await;
1810 self.conn = Some(pool_conn);
1811 match result {
1812 Ok(r) => Ok(r.rows_affected()),
1813 Err(e) => {
1814 let db_err = map_sqlx_error(e);
1815 if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
1816 self.connected = false;
1817 }
1818 Err(db_err)
1819 }
1820 }
1821 })
1822 }
1823
1824 fn query_with_params<'a>(
1828 &'a mut self,
1829 sql: &'a str,
1830 params: &'a [Value],
1831 ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
1832 Box::pin(async move {
1833 if params.is_empty() {
1834 return self.query(sql).await;
1835 }
1836 let mut pool_conn = self
1837 .conn
1838 .take()
1839 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1840 let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1841 for v in params {
1842 q = match v {
1843 Value::Null => q.bind(None::<i64>),
1844 Value::Bool(b) => q.bind(*b),
1845 Value::I8(n) => q.bind(*n),
1846 Value::I16(n) => q.bind(*n),
1847 Value::I32(n) => q.bind(*n),
1848 Value::I64(n) => q.bind(*n),
1849 Value::U8(n) => q.bind(*n as i16),
1850 Value::U16(n) => q.bind(*n as i32),
1851 Value::U32(n) => q.bind(*n as i64),
1852 Value::U64(n) => q.bind(*n as i64),
1853 Value::F32(f) => q.bind(*f),
1854 Value::F64(f) => q.bind(*f),
1855 Value::String(s) => q.bind(s.as_str()),
1856 Value::Decimal(s) => q.bind(s.as_str()),
1857 Value::Bytes(b) => q.bind(b.as_slice()),
1858 other => q.bind(other.to_string()),
1859 };
1860 }
1861 let rows_result = q.fetch_all(&mut *pool_conn).await;
1862 self.conn = Some(pool_conn);
1863 let rows = rows_result.map_err(map_sqlx_error)?;
1864 if rows.is_empty() {
1865 return Ok(Vec::new());
1866 }
1867 let col_types: Vec<ColType> = rows[0]
1869 .columns()
1870 .iter()
1871 .map(|col| ColType::parse_postgres(col.type_info().name()))
1872 .collect();
1873 let col_names: Vec<String> = rows[0]
1874 .columns()
1875 .iter()
1876 .map(|col| col.name().to_string())
1877 .collect();
1878 let mut result = Vec::with_capacity(rows.len());
1879 for row in &rows {
1880 let mut record = HashMap::with_capacity(col_types.len());
1881 for (i, name) in col_names.iter().enumerate() {
1882 let value = row_to_value_with_coltype_pg(row, i, col_types[i]);
1883 record.insert(name.clone(), value);
1884 }
1885 result.push(record);
1886 }
1887 Ok(result)
1888 })
1889 }
1890
1891 fn query_values<'a>(
1895 &'a mut self,
1896 sql: &'a str,
1897 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
1898 Box::pin(async move {
1899 let mut pool_conn = self
1900 .conn
1901 .take()
1902 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1903 let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
1904 self.conn = Some(pool_conn);
1905 let rows = rows_result.map_err(map_sqlx_error)?;
1906 if rows.is_empty() {
1907 return Ok((Vec::new(), Vec::new()));
1908 }
1909 let cols = rows[0].columns();
1910 let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
1911 let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
1912 for col in cols {
1913 col_names.push(col.name().to_string());
1914 col_types.push(ColType::parse_postgres(col.type_info().name()));
1915 }
1916 let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1917 for row in &rows {
1918 let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
1919 for (idx, _) in col_names.iter().enumerate() {
1920 row_values.push(row_to_value_with_coltype_pg(row, idx, col_types[idx]));
1921 }
1922 result_rows.push(row_values);
1923 }
1924 Ok((col_names, result_rows))
1925 })
1926 }
1927
1928 fn query_values_with_params<'a>(
1932 &'a mut self,
1933 sql: &'a str,
1934 params: &'a [Value],
1935 ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
1936 Box::pin(async move {
1937 if params.is_empty() {
1938 return self.query_values(sql).await;
1939 }
1940 let mut pool_conn = self
1941 .conn
1942 .take()
1943 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1944 let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1945 for v in params {
1946 q = match v {
1947 Value::Null => q.bind(None::<i64>),
1948 Value::Bool(b) => q.bind(*b),
1949 Value::I8(n) => q.bind(*n),
1950 Value::I16(n) => q.bind(*n),
1951 Value::I32(n) => q.bind(*n),
1952 Value::I64(n) => q.bind(*n),
1953 Value::U8(n) => q.bind(*n as i16),
1954 Value::U16(n) => q.bind(*n as i32),
1955 Value::U32(n) => q.bind(*n as i64),
1956 Value::U64(n) => q.bind(*n as i64),
1957 Value::F32(f) => q.bind(*f),
1958 Value::F64(f) => q.bind(*f),
1959 Value::String(s) => q.bind(s.as_str()),
1960 Value::Decimal(s) => q.bind(s.as_str()),
1961 Value::Bytes(b) => q.bind(b.as_slice()),
1962 other => q.bind(other.to_string()),
1963 };
1964 }
1965 let rows_result = q.fetch_all(&mut *pool_conn).await;
1966 self.conn = Some(pool_conn);
1967 let rows = rows_result.map_err(map_sqlx_error)?;
1968 if rows.is_empty() {
1969 return Ok((Vec::new(), Vec::new()));
1970 }
1971 let cols = rows[0].columns();
1972 let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
1973 for col in cols {
1974 col_names.push(col.name().to_string());
1975 }
1976 let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1977 for row in rows {
1978 let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
1979 for (idx, _) in col_names.iter().enumerate() {
1980 let ordinal = row.columns()[idx].ordinal();
1981 row_values.push(row_to_value_pg(&row, ordinal));
1982 }
1983 result_rows.push(row_values);
1984 }
1985 Ok((col_names, result_rows))
1986 })
1987 }
1988
1989 fn query_stream<'a>(
1999 &'a mut self,
2000 sql: &'a str,
2001 ) -> Pin<Box<dyn futures::Stream<Item = Result<HashMap<String, Value>, DbError>> + Send + 'a>>
2002 {
2003 Box::pin(async_stream::try_stream! {
2004 let mut pool_conn = self
2005 .conn
2006 .take()
2007 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
2008 let mut row_stream = sqlx::query(sqlx::AssertSqlSafe(sql)).fetch(&mut *pool_conn);
2009 while let Some(row_result) = row_stream.next().await {
2012 let row = row_result.map_err(map_sqlx_error)?;
2013 let cols = row.columns();
2014 let mut record = HashMap::with_capacity(cols.len());
2015 for (i, col) in cols.iter().enumerate() {
2016 let name = col.name().to_string();
2017 let value = row_to_value_pg(&row, i);
2019 record.insert(name, value);
2020 }
2021 yield record;
2022 }
2023 drop(row_stream);
2026 self.conn = Some(pool_conn);
2027 })
2028 }
2029}
2030
2031impl Drop for SqlxPgConnection {
2032 fn drop(&mut self) {
2033 if let Some(conn) = self.conn.take() {
2034 drop(conn);
2035 }
2036 }
2037}
2038
2039#[async_trait]
2060pub trait PgExtensions: Send + Sync {
2061 async fn listen(&mut self, channel: &str) -> Result<(), DbError>;
2072
2073 async fn notify(&mut self, channel: &str, payload: &str) -> Result<(), DbError>;
2084
2085 async fn copy_from_stdin(&mut self, sql: &str, data: &[u8]) -> Result<u64, DbError>;
2100}
2101
2102fn validate_pg_channel_name(channel: &str) -> Result<(), DbError> {
2107 if channel.is_empty() {
2108 return Err(DbError::Internal(
2109 "PG channel name must not be empty".to_string(),
2110 ));
2111 }
2112 if !channel
2113 .chars()
2114 .all(|c| c.is_ascii_alphanumeric() || c == '_')
2115 {
2116 return Err(DbError::Internal(format!(
2117 "invalid PG channel name: {} (only alphanumeric and underscore allowed)",
2118 channel
2119 )));
2120 }
2121 Ok(())
2122}
2123
2124#[async_trait]
2125impl PgExtensions for SqlxPgConnection {
2126 async fn listen(&mut self, channel: &str) -> Result<(), DbError> {
2127 validate_pg_channel_name(channel)?;
2128 self.execute(&format!("LISTEN {}", channel)).await?;
2130 Ok(())
2131 }
2132
2133 async fn notify(&mut self, channel: &str, payload: &str) -> Result<(), DbError> {
2134 validate_pg_channel_name(channel)?;
2135 let escaped_payload = payload.replace('\'', "''");
2137 self.execute(&format!("NOTIFY {}, '{}'", channel, escaped_payload))
2138 .await?;
2139 Ok(())
2140 }
2141
2142 async fn copy_from_stdin(&mut self, sql: &str, data: &[u8]) -> Result<u64, DbError> {
2143 let mut pool_conn = self
2144 .conn
2145 .take()
2146 .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
2147 let mut copy = (*pool_conn)
2151 .copy_in_raw(sql)
2152 .await
2153 .map_err(map_sqlx_error)?;
2154 copy.send(data).await.map_err(map_sqlx_error)?;
2156 let result = copy.finish().await.map_err(map_sqlx_error)?;
2158 self.conn = Some(pool_conn);
2159 Ok(result)
2160 }
2161}
2162
2163pub async fn pg_bulk_insert(
2171 conn: &mut SqlxPgConnection,
2172 table: &str,
2173 columns: &[&str],
2174 rows: &[Vec<Value>],
2175) -> Result<u64, DbError> {
2176 if rows.is_empty() {
2177 return Ok(0);
2178 }
2179 let col_list = columns.join(", ");
2180 let cols_per_row = columns.len();
2181 let placeholders: Vec<String> = rows
2183 .iter()
2184 .enumerate()
2185 .map(|(row_idx, _)| {
2186 let base = row_idx * cols_per_row;
2187 let ph: Vec<String> = (0..cols_per_row)
2188 .map(|i| format!("${}", base + i + 1))
2189 .collect();
2190 format!("({})", ph.join(", "))
2191 })
2192 .collect();
2193 let sql = format!(
2194 "INSERT INTO {} ({}) VALUES {}",
2195 table,
2196 col_list,
2197 placeholders.join(", ")
2198 );
2199 let mut params: Vec<Value> = Vec::with_capacity(rows.len() * cols_per_row);
2201 for row in rows {
2202 for v in row {
2203 params.push(v.clone());
2204 }
2205 }
2206 conn.execute_with_params(&sql, ¶ms).await
2207}
2208#[cfg(test)]
2209mod tests {
2210 use super::*;
2211
2212 #[test]
2213 fn test_needs_raw_sql_begin() {
2214 assert!(needs_raw_sql("BEGIN"));
2215 assert!(needs_raw_sql("begin"));
2216 assert!(needs_raw_sql(" BEGIN"));
2217 assert!(needs_raw_sql("\tbegin"));
2218 }
2219
2220 #[test]
2221 fn test_needs_raw_sql_commit() {
2222 assert!(needs_raw_sql("COMMIT"));
2223 assert!(needs_raw_sql("commit"));
2224 assert!(needs_raw_sql(" COMMIT WORK"));
2225 }
2226
2227 #[test]
2228 fn test_needs_raw_sql_rollback() {
2229 assert!(needs_raw_sql("ROLLBACK"));
2230 assert!(needs_raw_sql("rollback"));
2231 assert!(needs_raw_sql(" ROLLBACK TO SAVEPOINT sp1"));
2232 }
2233
2234 #[test]
2235 fn test_needs_raw_sql_savepoint() {
2236 assert!(needs_raw_sql("SAVEPOINT sp1"));
2237 assert!(needs_raw_sql("savepoint s1"));
2238 }
2239
2240 #[test]
2241 fn test_needs_raw_sql_release() {
2242 assert!(needs_raw_sql("RELEASE sp1"));
2243 assert!(needs_raw_sql("release savepoint s1"));
2244 }
2245
2246 #[test]
2247 fn test_needs_raw_sql_set() {
2248 assert!(needs_raw_sql("SET autocommit = 0"));
2249 assert!(needs_raw_sql("set names utf8"));
2250 }
2251
2252 #[test]
2253 fn test_needs_raw_sql_use() {
2254 assert!(needs_raw_sql("USE mydb"));
2255 assert!(needs_raw_sql("use mydb"));
2256 }
2257
2258 #[test]
2259 fn test_needs_raw_sql_start_transaction() {
2260 assert!(needs_raw_sql("START TRANSACTION"));
2261 assert!(needs_raw_sql("start transaction"));
2262 assert!(needs_raw_sql(" START TRANSACTION READ ONLY"));
2263 }
2264
2265 #[test]
2266 fn test_needs_raw_sql_normal_queries() {
2267 assert!(!needs_raw_sql("SELECT * FROM users"));
2268 assert!(!needs_raw_sql("INSERT INTO t VALUES (1)"));
2269 assert!(!needs_raw_sql("UPDATE t SET x = 1"));
2270 assert!(!needs_raw_sql("DELETE FROM t"));
2271 assert!(!needs_raw_sql("CREATE TABLE t (id INT)"));
2272 assert!(!needs_raw_sql("DROP TABLE t"));
2273 assert!(!needs_raw_sql("ALTER TABLE t ADD COLUMN x INT"));
2274 }
2275
2276 #[test]
2277 fn test_needs_raw_sql_edge_cases() {
2278 assert!(!needs_raw_sql(""));
2279 assert!(!needs_raw_sql(" "));
2280 assert!(!needs_raw_sql("SELECT"));
2281 assert!(needs_raw_sql("BEGINNING"));
2282 assert!(needs_raw_sql("COMMITTEE"));
2283 assert!(!needs_raw_sql("INSERT"));
2284 assert!(!needs_raw_sql("DELETE"));
2285 }
2286}