1pub use crate::error::TryGetError;
2use crate::{
3 SelectGetableValue, SelectorRaw, Statement,
4 error::{DbErr, type_err},
5};
6use std::{
7 collections::{BTreeMap, HashMap},
8 fmt::Debug,
9 hash::Hash,
10 marker::PhantomData,
11 sync::Arc,
12};
13
14#[cfg(any(feature = "mock", feature = "proxy"))]
15use crate::debug_print;
16
17#[cfg(feature = "sqlx-dep")]
18use crate::driver::*;
19#[cfg(feature = "sqlx-dep")]
20use sqlx::Row;
21#[cfg(feature = "sqlx-postgres")]
22use sqlx::{TypeInfo, ValueRef};
23
24#[derive(Debug)]
31pub struct QueryResult {
32 pub(crate) row: QueryResultRow,
33}
34
35#[allow(clippy::enum_variant_names)]
36pub(crate) enum QueryResultRow {
37 #[cfg(feature = "sqlx-mysql")]
38 SqlxMySql(sqlx::mysql::MySqlRow),
39 #[cfg(feature = "sqlx-postgres")]
40 SqlxPostgres(sqlx::postgres::PgRow),
41 #[cfg(feature = "sqlx-sqlite")]
42 SqlxSqlite(sqlx::sqlite::SqliteRow),
43 #[cfg(feature = "rusqlite")]
44 Rusqlite(crate::driver::rusqlite::RusqliteRow),
45 #[cfg(feature = "mock")]
46 Mock(crate::MockRow),
47 #[cfg(feature = "proxy")]
48 Proxy(crate::ProxyRow),
49}
50
51pub trait TryGetable: Sized {
58 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError>;
60
61 fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
64 if pre.is_empty() {
65 Self::try_get_by(res, col)
66 } else {
67 Self::try_get_by(res, format!("{pre}{col}").as_str())
68 }
69 }
70
71 fn try_get_by_index(res: &QueryResult, index: usize) -> Result<Self, TryGetError> {
73 Self::try_get_by(res, index)
74 }
75}
76
77impl From<TryGetError> for DbErr {
78 fn from(e: TryGetError) -> DbErr {
79 match e {
80 TryGetError::DbErr(e) => e,
81 TryGetError::Null(s) => {
82 type_err(format!("A null value was encountered while decoding {s}"))
83 }
84 }
85 }
86}
87
88impl From<DbErr> for TryGetError {
89 fn from(e: DbErr) -> TryGetError {
90 Self::DbErr(e)
91 }
92}
93
94impl QueryResult {
97 #[doc(hidden)]
98 #[cfg(feature = "sqlx-postgres")]
99 pub fn try_get_from_sqlx_postgres<T, I>(&self, idx: I) -> Option<Result<T, TryGetError>>
100 where
101 T: sqlx::Type<sqlx::Postgres> + for<'r> sqlx::Decode<'r, sqlx::Postgres>,
102 I: ColIdx,
103 {
104 match &self.row {
105 QueryResultRow::SqlxPostgres(row) => {
106 let value = match row.try_get_raw(idx.as_sqlx_postgres_index()) {
107 Ok(value) => value,
108 Err(err) => return Some(Err(sqlx_error_to_query_err(err).into())),
109 };
110
111 if !value.is_null() {
112 let ty = value.type_info();
113 if !ty.is_null() && !T::compatible(&ty) {
114 return None;
115 }
116 }
117
118 Some(
119 row.try_get::<Option<T>, _>(idx.as_sqlx_postgres_index())
120 .map_err(|e| sqlx_error_to_query_err(e).into())
121 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
122 )
123 }
124 _ => None,
125 }
126 }
127
128 pub fn try_get_by<T, I>(&self, index: I) -> Result<T, DbErr>
130 where
131 T: TryGetable,
132 I: ColIdx,
133 {
134 Ok(T::try_get_by(self, index)?)
135 }
136
137 pub fn try_get_by_nullable<T, I>(&self, index: I) -> Result<T, TryGetError>
139 where
140 T: TryGetable,
141 I: ColIdx,
142 {
143 T::try_get_by(self, index)
144 }
145
146 pub fn try_get<T>(&self, pre: &str, col: &str) -> Result<T, DbErr>
148 where
149 T: TryGetable,
150 {
151 Ok(T::try_get(self, pre, col)?)
152 }
153
154 pub fn try_get_nullable<T>(&self, pre: &str, col: &str) -> Result<T, TryGetError>
156 where
157 T: TryGetable,
158 {
159 T::try_get(self, pre, col)
160 }
161
162 pub fn try_get_by_index<T>(&self, idx: usize) -> Result<T, DbErr>
164 where
165 T: TryGetable,
166 {
167 Ok(T::try_get_by_index(self, idx)?)
168 }
169
170 pub fn try_get_by_index_nullable<T>(&self, idx: usize) -> Result<T, TryGetError>
172 where
173 T: TryGetable,
174 {
175 T::try_get_by_index(self, idx)
176 }
177
178 pub fn try_get_many<T>(&self, pre: &str, cols: &[String]) -> Result<T, DbErr>
180 where
181 T: TryGetableMany,
182 {
183 Ok(T::try_get_many(self, pre, cols)?)
184 }
185
186 pub fn try_get_many_by_index<T>(&self) -> Result<T, DbErr>
188 where
189 T: TryGetableMany,
190 {
191 Ok(T::try_get_many_by_index(self)?)
192 }
193
194 pub fn column_names(&self) -> Vec<String> {
196 #[cfg(feature = "sqlx-dep")]
197 use sqlx::Column;
198
199 match &self.row {
200 #[cfg(feature = "sqlx-mysql")]
201 QueryResultRow::SqlxMySql(row) => {
202 row.columns().iter().map(|c| c.name().to_string()).collect()
203 }
204 #[cfg(feature = "sqlx-postgres")]
205 QueryResultRow::SqlxPostgres(row) => {
206 row.columns().iter().map(|c| c.name().to_string()).collect()
207 }
208 #[cfg(feature = "sqlx-sqlite")]
209 QueryResultRow::SqlxSqlite(row) => {
210 row.columns().iter().map(|c| c.name().to_string()).collect()
211 }
212 #[cfg(feature = "rusqlite")]
213 QueryResultRow::Rusqlite(row) => row.columns().iter().map(|c| c.to_string()).collect(),
214 #[cfg(feature = "mock")]
215 QueryResultRow::Mock(row) => row
216 .clone()
217 .into_column_value_tuples()
218 .map(|(c, _)| c.to_string())
219 .collect(),
220 #[cfg(feature = "proxy")]
221 QueryResultRow::Proxy(row) => row
222 .clone()
223 .into_column_value_tuples()
224 .map(|(c, _)| c.to_string())
225 .collect(),
226 #[allow(unreachable_patterns)]
227 _ => unreachable!(),
228 }
229 }
230
231 #[cfg(feature = "sqlx-mysql")]
233 pub fn try_as_mysql_row(&self) -> Option<&sqlx::mysql::MySqlRow> {
234 match &self.row {
235 QueryResultRow::SqlxMySql(mysql_row) => Some(mysql_row),
236 #[allow(unreachable_patterns)]
237 _ => None,
238 }
239 }
240
241 #[cfg(feature = "sqlx-postgres")]
243 pub fn try_as_pg_row(&self) -> Option<&sqlx::postgres::PgRow> {
244 match &self.row {
245 QueryResultRow::SqlxPostgres(pg_row) => Some(pg_row),
246 #[allow(unreachable_patterns)]
247 _ => None,
248 }
249 }
250
251 #[cfg(feature = "sqlx-sqlite")]
253 pub fn try_as_sqlite_row(&self) -> Option<&sqlx::sqlite::SqliteRow> {
254 match &self.row {
255 QueryResultRow::SqlxSqlite(sqlite_row) => Some(sqlite_row),
256 #[allow(unreachable_patterns)]
257 _ => None,
258 }
259 }
260
261 #[cfg(feature = "mock")]
263 pub fn try_as_mock_row(&self) -> Option<&crate::MockRow> {
264 match &self.row {
265 QueryResultRow::Mock(mock_row) => Some(mock_row),
266 #[allow(unreachable_patterns)]
267 _ => None,
268 }
269 }
270
271 #[cfg(feature = "proxy")]
273 pub fn try_as_proxy_row(&self) -> Option<&crate::ProxyRow> {
274 match &self.row {
275 QueryResultRow::Proxy(proxy_row) => Some(proxy_row),
276 #[allow(unreachable_patterns)]
277 _ => None,
278 }
279 }
280}
281
282#[allow(unused_variables)]
283impl Debug for QueryResultRow {
284 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
285 match self {
286 #[cfg(feature = "sqlx-mysql")]
287 Self::SqlxMySql(row) => write!(f, "{row:?}"),
288 #[cfg(feature = "sqlx-postgres")]
289 Self::SqlxPostgres(_) => write!(f, "QueryResultRow::SqlxPostgres cannot be inspected"),
290 #[cfg(feature = "sqlx-sqlite")]
291 Self::SqlxSqlite(_) => write!(f, "QueryResultRow::SqlxSqlite cannot be inspected"),
292 #[cfg(feature = "rusqlite")]
293 Self::Rusqlite(row) => write!(f, "{row:?}"),
294 #[cfg(feature = "mock")]
295 Self::Mock(row) => write!(f, "{row:?}"),
296 #[cfg(feature = "proxy")]
297 Self::Proxy(row) => write!(f, "{row:?}"),
298 #[allow(unreachable_patterns)]
299 _ => unreachable!(),
300 }
301 }
302}
303
304impl<T: TryGetable> TryGetable for Option<T> {
307 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
308 match T::try_get_by(res, index) {
309 Ok(v) => Ok(Some(v)),
310 Err(TryGetError::Null(_)) => Ok(None),
311 #[cfg(feature = "sqlx-dep")]
312 Err(TryGetError::DbErr(DbErr::Query(crate::RuntimeErr::SqlxError(err)))) => {
313 use std::ops::Deref;
314 match err.deref() {
315 sqlx::Error::ColumnNotFound(_) => Ok(None),
316 _ => Err(TryGetError::DbErr(DbErr::Query(
317 crate::RuntimeErr::SqlxError(err),
318 ))),
319 }
320 }
321 Err(e) => Err(e),
322 }
323 }
324}
325
326pub trait ColIdx: Debug + Copy {
328 #[cfg(feature = "sqlx-mysql")]
329 type SqlxMySqlIndex: sqlx::ColumnIndex<sqlx::mysql::MySqlRow>;
331 #[cfg(feature = "sqlx-postgres")]
332 type SqlxPostgresIndex: sqlx::ColumnIndex<sqlx::postgres::PgRow>;
334 #[cfg(feature = "sqlx-sqlite")]
335 type SqlxSqliteIndex: sqlx::ColumnIndex<sqlx::sqlite::SqliteRow>;
337
338 #[cfg(feature = "sqlx-mysql")]
339 fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex;
341 #[cfg(feature = "sqlx-postgres")]
342 fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex;
344 #[cfg(feature = "sqlx-sqlite")]
345 fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex;
347
348 fn as_str(&self) -> Option<&str>;
350 fn as_usize(&self) -> Option<&usize>;
352}
353
354impl ColIdx for &str {
355 #[cfg(feature = "sqlx-mysql")]
356 type SqlxMySqlIndex = Self;
357 #[cfg(feature = "sqlx-postgres")]
358 type SqlxPostgresIndex = Self;
359 #[cfg(feature = "sqlx-sqlite")]
360 type SqlxSqliteIndex = Self;
361
362 #[cfg(feature = "sqlx-mysql")]
363 #[inline]
364 fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
365 self
366 }
367 #[cfg(feature = "sqlx-postgres")]
368 #[inline]
369 fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
370 self
371 }
372 #[cfg(feature = "sqlx-sqlite")]
373 #[inline]
374 fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
375 self
376 }
377
378 #[inline]
379 fn as_str(&self) -> Option<&str> {
380 Some(self)
381 }
382 #[inline]
383 fn as_usize(&self) -> Option<&usize> {
384 None
385 }
386}
387
388impl ColIdx for usize {
389 #[cfg(feature = "sqlx-mysql")]
390 type SqlxMySqlIndex = Self;
391 #[cfg(feature = "sqlx-postgres")]
392 type SqlxPostgresIndex = Self;
393 #[cfg(feature = "sqlx-sqlite")]
394 type SqlxSqliteIndex = Self;
395
396 #[cfg(feature = "sqlx-mysql")]
397 #[inline]
398 fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
399 *self
400 }
401 #[cfg(feature = "sqlx-postgres")]
402 #[inline]
403 fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
404 *self
405 }
406 #[cfg(feature = "sqlx-sqlite")]
407 #[inline]
408 fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
409 *self
410 }
411
412 #[inline]
413 fn as_str(&self) -> Option<&str> {
414 None
415 }
416 #[inline]
417 fn as_usize(&self) -> Option<&usize> {
418 Some(self)
419 }
420}
421
422macro_rules! try_getable_all {
423 ( $type: ty ) => {
424 impl TryGetable for $type {
425 #[allow(unused_variables)]
426 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
427 match &res.row {
428 #[cfg(feature = "sqlx-mysql")]
429 QueryResultRow::SqlxMySql(row) => row
430 .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
431 .map_err(|e| sqlx_error_to_query_err(e).into())
432 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
433 #[cfg(feature = "sqlx-postgres")]
434 QueryResultRow::SqlxPostgres(row) => row
435 .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
436 .map_err(|e| sqlx_error_to_query_err(e).into())
437 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
438 #[cfg(feature = "sqlx-sqlite")]
439 QueryResultRow::SqlxSqlite(row) => row
440 .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
441 .map_err(|e| sqlx_error_to_query_err(e).into())
442 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
443 #[cfg(feature = "rusqlite")]
444 QueryResultRow::Rusqlite(row) => row
445 .try_get::<Option<$type>, _>(idx)
446 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
447 #[cfg(feature = "mock")]
448 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
449 debug_print!("{:#?}", e.to_string());
450 err_null_idx_col(idx)
451 }),
452 #[cfg(feature = "proxy")]
453 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
454 debug_print!("{:#?}", e.to_string());
455 err_null_idx_col(idx)
456 }),
457 #[allow(unreachable_patterns)]
458 _ => unreachable!(),
459 }
460 }
461 }
462 };
463}
464
465macro_rules! try_getable_unsigned {
466 ( $type: ty ) => {
467 impl TryGetable for $type {
468 #[allow(unused_variables)]
469 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
470 match &res.row {
471 #[cfg(feature = "sqlx-mysql")]
472 QueryResultRow::SqlxMySql(row) => row
473 .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
474 .map_err(|e| sqlx_error_to_query_err(e).into())
475 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
476 #[cfg(feature = "sqlx-postgres")]
477 QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
478 "{} unsupported by sqlx-postgres",
479 stringify!($type)
480 ))
481 .into()),
482 #[cfg(feature = "sqlx-sqlite")]
483 QueryResultRow::SqlxSqlite(row) => row
484 .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
485 .map_err(|e| sqlx_error_to_query_err(e).into())
486 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
487 #[cfg(feature = "rusqlite")]
488 QueryResultRow::Rusqlite(row) => row
489 .try_get::<Option<$type>, _>(idx)
490 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
491 #[cfg(feature = "mock")]
492 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
493 debug_print!("{:#?}", e.to_string());
494 err_null_idx_col(idx)
495 }),
496 #[cfg(feature = "proxy")]
497 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
498 debug_print!("{:#?}", e.to_string());
499 err_null_idx_col(idx)
500 }),
501 #[allow(unreachable_patterns)]
502 _ => unreachable!(),
503 }
504 }
505 }
506 };
507}
508
509macro_rules! try_getable_mysql {
510 ( $type: ty ) => {
511 impl TryGetable for $type {
512 #[allow(unused_variables)]
513 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
514 match &res.row {
515 #[cfg(feature = "sqlx-mysql")]
516 QueryResultRow::SqlxMySql(row) => row
517 .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
518 .map_err(|e| sqlx_error_to_query_err(e).into())
519 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
520 #[cfg(feature = "sqlx-postgres")]
521 QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
522 "{} unsupported by sqlx-postgres",
523 stringify!($type)
524 ))
525 .into()),
526 #[cfg(feature = "sqlx-sqlite")]
527 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
528 "{} unsupported by sqlx-sqlite",
529 stringify!($type)
530 ))
531 .into()),
532 #[cfg(feature = "rusqlite")]
533 QueryResultRow::Rusqlite(row) => Err(type_err(format!(
534 "{} unsupported by rusqlite",
535 stringify!($type)
536 ))
537 .into()),
538 #[cfg(feature = "mock")]
539 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
540 debug_print!("{:#?}", e.to_string());
541 err_null_idx_col(idx)
542 }),
543 #[cfg(feature = "proxy")]
544 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
545 debug_print!("{:#?}", e.to_string());
546 err_null_idx_col(idx)
547 }),
548 #[allow(unreachable_patterns)]
549 _ => unreachable!(),
550 }
551 }
552 }
553 };
554}
555
556#[allow(unused_macros)]
557macro_rules! try_getable_postgres {
558 ( $type: ty ) => {
559 impl TryGetable for $type {
560 #[allow(unused_variables)]
561 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
562 match &res.row {
563 #[cfg(feature = "sqlx-mysql")]
564 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
565 "{} unsupported by sqlx-mysql",
566 stringify!($type)
567 ))
568 .into()),
569 #[cfg(feature = "sqlx-postgres")]
570 QueryResultRow::SqlxPostgres(row) => row
571 .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
572 .map_err(|e| sqlx_error_to_query_err(e).into())
573 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
574 #[cfg(feature = "sqlx-sqlite")]
575 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
576 "{} unsupported by sqlx-sqlite",
577 stringify!($type)
578 ))
579 .into()),
580 #[cfg(feature = "rusqlite")]
581 QueryResultRow::Rusqlite(row) => Err(type_err(format!(
582 "{} unsupported by rusqlite",
583 stringify!($type)
584 ))
585 .into()),
586 #[cfg(feature = "mock")]
587 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
588 debug_print!("{:#?}", e.to_string());
589 err_null_idx_col(idx)
590 }),
591 #[cfg(feature = "proxy")]
592 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
593 debug_print!("{:#?}", e.to_string());
594 err_null_idx_col(idx)
595 }),
596 #[allow(unreachable_patterns)]
597 _ => unreachable!(),
598 }
599 }
600 }
601 };
602}
603
604#[allow(unused_macros)]
605macro_rules! try_getable_date_time {
606 ( $type: ty ) => {
607 impl TryGetable for $type {
608 #[allow(unused_variables)]
609 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
610 match &res.row {
611 #[cfg(feature = "sqlx-mysql")]
612 QueryResultRow::SqlxMySql(row) => {
613 use chrono::{DateTime, Utc};
614 row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_mysql_index())
615 .map_err(|e| sqlx_error_to_query_err(e).into())
616 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
617 .map(|v| v.into())
618 }
619 #[cfg(feature = "sqlx-postgres")]
620 QueryResultRow::SqlxPostgres(row) => row
621 .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
622 .map_err(|e| sqlx_error_to_query_err(e).into())
623 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
624 #[cfg(feature = "sqlx-sqlite")]
625 QueryResultRow::SqlxSqlite(row) => row
626 .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
627 .map_err(|e| sqlx_error_to_query_err(e).into())
628 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
629 #[cfg(feature = "rusqlite")]
630 QueryResultRow::Rusqlite(row) => row
631 .try_get::<Option<$type>, _>(idx)
632 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
633 .map(|v| v.into()),
634 #[cfg(feature = "mock")]
635 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
636 debug_print!("{:#?}", e.to_string());
637 err_null_idx_col(idx)
638 }),
639 #[cfg(feature = "proxy")]
640 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
641 debug_print!("{:#?}", e.to_string());
642 err_null_idx_col(idx)
643 }),
644 #[allow(unreachable_patterns)]
645 _ => unreachable!(),
646 }
647 }
648 }
649 };
650}
651
652try_getable_all!(bool);
653try_getable_all!(i8);
654try_getable_all!(i16);
655try_getable_all!(i32);
656try_getable_all!(i64);
657try_getable_unsigned!(u8);
658try_getable_unsigned!(u16);
659try_getable_mysql!(u64);
660try_getable_all!(f32);
661try_getable_all!(f64);
662try_getable_all!(Vec<u8>);
663
664#[cfg(feature = "with-json")]
665try_getable_all!(serde_json::Value);
666
667#[cfg(feature = "with-chrono")]
668try_getable_all!(chrono::NaiveDate);
669
670#[cfg(feature = "with-chrono")]
671try_getable_all!(chrono::NaiveTime);
672
673#[cfg(feature = "with-chrono")]
674try_getable_all!(chrono::NaiveDateTime);
675
676#[cfg(feature = "with-chrono")]
677try_getable_date_time!(chrono::DateTime<chrono::FixedOffset>);
678
679#[cfg(feature = "with-chrono")]
680try_getable_all!(chrono::DateTime<chrono::Utc>);
681
682#[cfg(feature = "with-chrono")]
683try_getable_all!(chrono::DateTime<chrono::Local>);
684
685#[cfg(feature = "with-time")]
686try_getable_all!(time::Date);
687
688#[cfg(feature = "with-time")]
689try_getable_all!(time::Time);
690
691#[cfg(feature = "with-time")]
692try_getable_all!(time::PrimitiveDateTime);
693
694#[cfg(feature = "with-time")]
695try_getable_all!(time::OffsetDateTime);
696
697#[cfg(feature = "with-rust_decimal")]
698use rust_decimal::Decimal;
699
700#[cfg(feature = "with-rust_decimal")]
701impl TryGetable for Decimal {
702 #[allow(unused_variables)]
703 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
704 match &res.row {
705 #[cfg(feature = "sqlx-mysql")]
706 QueryResultRow::SqlxMySql(row) => row
707 .try_get::<Option<Decimal>, _>(idx.as_sqlx_mysql_index())
708 .map_err(|e| sqlx_error_to_query_err(e).into())
709 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
710 #[cfg(feature = "sqlx-postgres")]
711 QueryResultRow::SqlxPostgres(row) => row
712 .try_get::<Option<Decimal>, _>(idx.as_sqlx_postgres_index())
713 .map_err(|e| sqlx_error_to_query_err(e).into())
714 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
715 #[cfg(feature = "sqlx-sqlite")]
716 QueryResultRow::SqlxSqlite(row) => {
717 let val: Option<f64> = row
718 .try_get(idx.as_sqlx_sqlite_index())
719 .map_err(sqlx_error_to_query_err)?;
720 match val {
721 Some(v) => Decimal::try_from(v).map_err(|e| {
722 DbErr::TryIntoErr {
723 from: "f64",
724 into: "Decimal",
725 source: Arc::new(e),
726 }
727 .into()
728 }),
729 None => Err(err_null_idx_col(idx)),
730 }
731 }
732 #[cfg(feature = "rusqlite")]
733 QueryResultRow::Rusqlite(row) => {
734 let val: Option<f64> = row.try_get(idx)?;
735 match val {
736 Some(v) => Decimal::try_from(v).map_err(|e| {
737 DbErr::TryIntoErr {
738 from: "f64",
739 into: "Decimal",
740 source: Arc::new(e),
741 }
742 .into()
743 }),
744 None => Err(err_null_idx_col(idx)),
745 }
746 }
747 #[cfg(feature = "mock")]
748 #[allow(unused_variables)]
749 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
750 debug_print!("{:#?}", e.to_string());
751 err_null_idx_col(idx)
752 }),
753 #[cfg(feature = "proxy")]
754 #[allow(unused_variables)]
755 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
756 debug_print!("{:#?}", e.to_string());
757 err_null_idx_col(idx)
758 }),
759 #[allow(unreachable_patterns)]
760 _ => unreachable!(),
761 }
762 }
763}
764
765#[cfg(feature = "with-bigdecimal")]
766use bigdecimal::BigDecimal;
767
768#[cfg(feature = "with-bigdecimal")]
769impl TryGetable for BigDecimal {
770 #[allow(unused_variables)]
771 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
772 match &res.row {
773 #[cfg(feature = "sqlx-mysql")]
774 QueryResultRow::SqlxMySql(row) => row
775 .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_mysql_index())
776 .map_err(|e| sqlx_error_to_query_err(e).into())
777 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
778 #[cfg(feature = "sqlx-postgres")]
779 QueryResultRow::SqlxPostgres(row) => row
780 .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_postgres_index())
781 .map_err(|e| sqlx_error_to_query_err(e).into())
782 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
783 #[cfg(feature = "sqlx-sqlite")]
784 QueryResultRow::SqlxSqlite(row) => {
785 let val: Option<f64> = row
786 .try_get(idx.as_sqlx_sqlite_index())
787 .map_err(sqlx_error_to_query_err)?;
788 match val {
789 Some(v) => BigDecimal::try_from(v).map_err(|e| {
790 DbErr::TryIntoErr {
791 from: "f64",
792 into: "BigDecimal",
793 source: Arc::new(e),
794 }
795 .into()
796 }),
797 None => Err(err_null_idx_col(idx)),
798 }
799 }
800 #[cfg(feature = "rusqlite")]
801 QueryResultRow::Rusqlite(row) => {
802 let val: Option<f64> = row.try_get(idx)?;
803 match val {
804 Some(v) => BigDecimal::try_from(v).map_err(|e| {
805 DbErr::TryIntoErr {
806 from: "f64",
807 into: "BigDecimal",
808 source: Arc::new(e),
809 }
810 .into()
811 }),
812 None => Err(err_null_idx_col(idx)),
813 }
814 }
815 #[cfg(feature = "mock")]
816 #[allow(unused_variables)]
817 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
818 debug_print!("{:#?}", e.to_string());
819 err_null_idx_col(idx)
820 }),
821 #[cfg(feature = "proxy")]
822 #[allow(unused_variables)]
823 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
824 debug_print!("{:#?}", e.to_string());
825 err_null_idx_col(idx)
826 }),
827 #[allow(unreachable_patterns)]
828 _ => unreachable!(),
829 }
830 }
831}
832
833#[allow(unused_macros)]
834macro_rules! try_getable_uuid {
835 ( $type: ty, $conversion_fn: expr ) => {
836 #[allow(unused_variables, unreachable_code)]
837 impl TryGetable for $type {
838 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
839 let res: Result<uuid::Uuid, TryGetError> = match &res.row {
840 #[cfg(feature = "sqlx-mysql")]
841 QueryResultRow::SqlxMySql(row) => row
842 .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_mysql_index())
843 .map_err(|e| sqlx_error_to_query_err(e).into())
844 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
845 .or_else(|_| {
846 row.try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
849 .map_err(|e| sqlx_error_to_query_err(e).into())
850 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
851 .map(|bytes| {
852 String::from_utf8(bytes).map_err(|e| {
853 DbErr::TryIntoErr {
854 from: "Vec<u8>",
855 into: "String",
856 source: Arc::new(e),
857 }
858 .into()
859 })
860 })?
861 .and_then(|s| {
862 uuid::Uuid::parse_str(&s).map_err(|e| {
863 DbErr::TryIntoErr {
864 from: "String",
865 into: "uuid::Uuid",
866 source: Arc::new(e),
867 }
868 .into()
869 })
870 })
871 }),
872 #[cfg(feature = "sqlx-postgres")]
873 QueryResultRow::SqlxPostgres(row) => row
874 .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_postgres_index())
875 .map_err(|e| sqlx_error_to_query_err(e).into())
876 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
877 #[cfg(feature = "sqlx-sqlite")]
878 QueryResultRow::SqlxSqlite(row) => row
879 .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_sqlite_index())
880 .map_err(|e| sqlx_error_to_query_err(e).into())
881 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
882 #[cfg(feature = "rusqlite")]
883 QueryResultRow::Rusqlite(row) => row
884 .try_get::<Option<uuid::Uuid>, _>(idx)
885 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
886 #[cfg(feature = "mock")]
887 #[allow(unused_variables)]
888 QueryResultRow::Mock(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
889 debug_print!("{:#?}", e.to_string());
890 err_null_idx_col(idx)
891 }),
892 #[cfg(feature = "proxy")]
893 #[allow(unused_variables)]
894 QueryResultRow::Proxy(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
895 debug_print!("{:#?}", e.to_string());
896 err_null_idx_col(idx)
897 }),
898 #[allow(unreachable_patterns)]
899 _ => unreachable!(),
900 };
901 res.map($conversion_fn)
902 }
903 }
904 };
905}
906
907#[cfg(feature = "with-uuid")]
908try_getable_uuid!(uuid::Uuid, Into::into);
909
910#[cfg(feature = "with-uuid")]
911try_getable_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
912
913#[cfg(feature = "with-uuid")]
914try_getable_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
915
916#[cfg(feature = "with-uuid")]
917try_getable_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
918
919#[cfg(feature = "with-uuid")]
920try_getable_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
921
922#[cfg(feature = "with-ipnetwork")]
923try_getable_postgres!(ipnetwork::IpNetwork);
924
925#[cfg(feature = "with-mac_address")]
926try_getable_postgres!(mac_address::MacAddress);
927
928impl TryGetable for u32 {
929 #[allow(unused_variables)]
930 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
931 match &res.row {
932 #[cfg(feature = "sqlx-mysql")]
933 QueryResultRow::SqlxMySql(row) => row
934 .try_get::<Option<u32>, _>(idx.as_sqlx_mysql_index())
935 .map_err(|e| sqlx_error_to_query_err(e).into())
936 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
937 #[cfg(feature = "sqlx-postgres")]
938 QueryResultRow::SqlxPostgres(row) => {
939 use sqlx::postgres::types::Oid;
940 match row.try_get::<Option<Oid>, _>(idx.as_sqlx_postgres_index()) {
943 Ok(opt) => opt.ok_or_else(|| err_null_idx_col(idx)).map(|oid| oid.0),
944 Err(_) => row
945 .try_get::<i32, _>(idx.as_sqlx_postgres_index())
947 .map_err(|e| sqlx_error_to_query_err(e).into())
948 .map(|v| {
949 v.try_into().map_err(|e| {
950 DbErr::TryIntoErr {
951 from: "i32",
952 into: "u32",
953 source: Arc::new(e),
954 }
955 .into()
956 })
957 })
958 .and_then(|r| r),
959 }
960 }
961 #[cfg(feature = "sqlx-sqlite")]
962 QueryResultRow::SqlxSqlite(row) => row
963 .try_get::<Option<u32>, _>(idx.as_sqlx_sqlite_index())
964 .map_err(|e| sqlx_error_to_query_err(e).into())
965 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
966 #[cfg(feature = "rusqlite")]
967 QueryResultRow::Rusqlite(row) => row
968 .try_get::<Option<u32>, _>(idx)
969 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
970 #[cfg(feature = "mock")]
971 #[allow(unused_variables)]
972 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
973 debug_print!("{:#?}", e.to_string());
974 err_null_idx_col(idx)
975 }),
976 #[cfg(feature = "proxy")]
977 #[allow(unused_variables)]
978 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
979 debug_print!("{:#?}", e.to_string());
980 err_null_idx_col(idx)
981 }),
982 #[allow(unreachable_patterns)]
983 _ => unreachable!(),
984 }
985 }
986}
987
988impl TryGetable for String {
989 #[allow(unused_variables)]
990 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
991 match &res.row {
992 #[cfg(feature = "sqlx-mysql")]
993 QueryResultRow::SqlxMySql(row) => row
994 .try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
995 .map_err(|e| sqlx_error_to_query_err(e).into())
996 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
997 .map(|bytes| {
998 String::from_utf8(bytes).map_err(|e| {
999 DbErr::TryIntoErr {
1000 from: "Vec<u8>",
1001 into: "String",
1002 source: Arc::new(e),
1003 }
1004 .into()
1005 })
1006 })?,
1007 #[cfg(feature = "sqlx-postgres")]
1008 QueryResultRow::SqlxPostgres(row) => row
1009 .try_get::<Option<String>, _>(idx.as_sqlx_postgres_index())
1010 .map_err(|e| sqlx_error_to_query_err(e).into())
1011 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1012 #[cfg(feature = "sqlx-sqlite")]
1013 QueryResultRow::SqlxSqlite(row) => row
1014 .try_get::<Option<String>, _>(idx.as_sqlx_sqlite_index())
1015 .map_err(|e| sqlx_error_to_query_err(e).into())
1016 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1017 #[cfg(feature = "rusqlite")]
1018 QueryResultRow::Rusqlite(row) => row
1019 .try_get::<Option<String>, _>(idx)
1020 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1021 #[cfg(feature = "mock")]
1022 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1023 debug_print!("{:#?}", e.to_string());
1024 err_null_idx_col(idx)
1025 }),
1026 #[cfg(feature = "proxy")]
1027 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
1028 debug_print!("{:#?}", e.to_string());
1029 err_null_idx_col(idx)
1030 }),
1031 #[allow(unreachable_patterns)]
1032 _ => unreachable!(),
1033 }
1034 }
1035}
1036
1037#[allow(dead_code)]
1038fn err_null_idx_col<I: ColIdx>(idx: I) -> TryGetError {
1039 TryGetError::Null(format!("{idx:?}"))
1040}
1041
1042#[cfg(feature = "postgres-array")]
1043mod postgres_array {
1044 use super::*;
1045
1046 #[allow(unused_macros)]
1047 macro_rules! try_getable_postgres_array {
1048 ( $type: ty ) => {
1049 #[allow(unused_variables)]
1050 impl TryGetable for Vec<$type> {
1051 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1052 match &res.row {
1053 #[cfg(feature = "sqlx-mysql")]
1054 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
1055 "{} unsupported by sqlx-mysql",
1056 stringify!($type)
1057 ))
1058 .into()),
1059 #[cfg(feature = "sqlx-postgres")]
1060 QueryResultRow::SqlxPostgres(row) => row
1061 .try_get::<Option<Vec<$type>>, _>(idx.as_sqlx_postgres_index())
1062 .map_err(|e| sqlx_error_to_query_err(e).into())
1063 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1064 #[cfg(feature = "sqlx-sqlite")]
1065 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1066 "{} unsupported by sqlx-sqlite",
1067 stringify!($type)
1068 ))
1069 .into()),
1070 #[cfg(feature = "rusqlite")]
1071 QueryResultRow::Rusqlite(_) => Err(type_err(format!(
1072 "{} unsupported by rusqlite",
1073 stringify!($type)
1074 ))
1075 .into()),
1076 #[cfg(feature = "mock")]
1077 #[allow(unused_variables)]
1078 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1079 debug_print!("{:#?}", e.to_string());
1080 err_null_idx_col(idx)
1081 }),
1082 #[cfg(feature = "proxy")]
1083 #[allow(unused_variables)]
1084 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
1085 debug_print!("{:#?}", e.to_string());
1086 err_null_idx_col(idx)
1087 }),
1088 #[allow(unreachable_patterns)]
1089 _ => unreachable!(),
1090 }
1091 }
1092 }
1093 };
1094 }
1095
1096 try_getable_postgres_array!(bool);
1097 try_getable_postgres_array!(i8);
1098 try_getable_postgres_array!(i16);
1099 try_getable_postgres_array!(i32);
1100 try_getable_postgres_array!(i64);
1101 try_getable_postgres_array!(f32);
1102 try_getable_postgres_array!(f64);
1103 try_getable_postgres_array!(String);
1104 try_getable_postgres_array!(Vec<u8>);
1105
1106 #[cfg(feature = "with-json")]
1107 try_getable_postgres_array!(serde_json::Value);
1108
1109 #[cfg(feature = "with-chrono")]
1110 try_getable_postgres_array!(chrono::NaiveDate);
1111
1112 #[cfg(feature = "with-chrono")]
1113 try_getable_postgres_array!(chrono::NaiveTime);
1114
1115 #[cfg(feature = "with-chrono")]
1116 try_getable_postgres_array!(chrono::NaiveDateTime);
1117
1118 #[cfg(feature = "with-chrono")]
1119 try_getable_postgres_array!(chrono::DateTime<chrono::FixedOffset>);
1120
1121 #[cfg(feature = "with-chrono")]
1122 try_getable_postgres_array!(chrono::DateTime<chrono::Utc>);
1123
1124 #[cfg(feature = "with-chrono")]
1125 try_getable_postgres_array!(chrono::DateTime<chrono::Local>);
1126
1127 #[cfg(feature = "with-time")]
1128 try_getable_postgres_array!(time::Date);
1129
1130 #[cfg(feature = "with-time")]
1131 try_getable_postgres_array!(time::Time);
1132
1133 #[cfg(feature = "with-time")]
1134 try_getable_postgres_array!(time::PrimitiveDateTime);
1135
1136 #[cfg(feature = "with-time")]
1137 try_getable_postgres_array!(time::OffsetDateTime);
1138
1139 #[cfg(feature = "with-rust_decimal")]
1140 try_getable_postgres_array!(rust_decimal::Decimal);
1141
1142 #[cfg(feature = "with-bigdecimal")]
1143 try_getable_postgres_array!(bigdecimal::BigDecimal);
1144
1145 #[cfg(feature = "with-ipnetwork")]
1146 try_getable_postgres_array!(ipnetwork::IpNetwork);
1147
1148 #[cfg(feature = "with-mac_address")]
1149 try_getable_postgres_array!(mac_address::MacAddress);
1150
1151 #[allow(unused_macros)]
1152 macro_rules! try_getable_postgres_array_uuid {
1153 ( $type: ty, $conversion_fn: expr ) => {
1154 #[allow(unused_variables, unreachable_code)]
1155 impl TryGetable for Vec<$type> {
1156 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1157 let res: Result<Vec<uuid::Uuid>, TryGetError> = match &res.row {
1158 #[cfg(feature = "sqlx-mysql")]
1159 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
1160 "{} unsupported by sqlx-mysql",
1161 stringify!($type)
1162 ))
1163 .into()),
1164 #[cfg(feature = "sqlx-postgres")]
1165 QueryResultRow::SqlxPostgres(row) => row
1166 .try_get::<Option<Vec<uuid::Uuid>>, _>(idx.as_sqlx_postgres_index())
1167 .map_err(|e| sqlx_error_to_query_err(e).into())
1168 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1169 #[cfg(feature = "sqlx-sqlite")]
1170 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1171 "{} unsupported by sqlx-sqlite",
1172 stringify!($type)
1173 ))
1174 .into()),
1175 #[cfg(feature = "rusqlite")]
1176 QueryResultRow::Rusqlite(_) => Err(type_err(format!(
1177 "{} unsupported by rusqlite",
1178 stringify!($type)
1179 ))
1180 .into()),
1181 #[cfg(feature = "mock")]
1182 QueryResultRow::Mock(row) => {
1183 row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1184 debug_print!("{:#?}", e.to_string());
1185 err_null_idx_col(idx)
1186 })
1187 }
1188 #[cfg(feature = "proxy")]
1189 QueryResultRow::Proxy(row) => {
1190 row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1191 debug_print!("{:#?}", e.to_string());
1192 err_null_idx_col(idx)
1193 })
1194 }
1195 #[allow(unreachable_patterns)]
1196 _ => unreachable!(),
1197 };
1198 res.map(|vec| vec.into_iter().map($conversion_fn).collect())
1199 }
1200 }
1201 };
1202 }
1203
1204 #[cfg(feature = "with-uuid")]
1205 try_getable_postgres_array_uuid!(uuid::Uuid, Into::into);
1206
1207 #[cfg(feature = "with-uuid")]
1208 try_getable_postgres_array_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
1209
1210 #[cfg(feature = "with-uuid")]
1211 try_getable_postgres_array_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
1212
1213 #[cfg(feature = "with-uuid")]
1214 try_getable_postgres_array_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
1215
1216 #[cfg(feature = "with-uuid")]
1217 try_getable_postgres_array_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
1218
1219 impl TryGetable for Vec<u32> {
1220 #[allow(unused_variables)]
1221 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1222 match &res.row {
1223 #[cfg(feature = "sqlx-mysql")]
1224 QueryResultRow::SqlxMySql(_) => {
1225 Err(type_err(format!("{} unsupported by sqlx-mysql", stringify!($type))).into())
1226 }
1227 #[cfg(feature = "sqlx-postgres")]
1228 QueryResultRow::SqlxPostgres(row) => {
1229 use sqlx::postgres::types::Oid;
1230 row.try_get::<Option<Vec<Oid>>, _>(idx.as_sqlx_postgres_index())
1233 .map_err(|e| sqlx_error_to_query_err(e).into())
1234 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
1235 .map(|oids| oids.into_iter().map(|oid| oid.0).collect())
1236 }
1237 #[cfg(feature = "sqlx-sqlite")]
1238 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1239 "{} unsupported by sqlx-sqlite",
1240 stringify!($type)
1241 ))
1242 .into()),
1243 #[cfg(feature = "rusqlite")]
1244 QueryResultRow::Rusqlite(_) => {
1245 Err(type_err(format!("{} unsupported by rusqlite", stringify!($type))).into())
1246 }
1247 #[cfg(feature = "mock")]
1248 #[allow(unused_variables)]
1249 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1250 debug_print!("{:#?}", e.to_string());
1251 err_null_idx_col(idx)
1252 }),
1253 #[cfg(feature = "proxy")]
1254 #[allow(unused_variables)]
1255 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
1256 debug_print!("{:#?}", e.to_string());
1257 err_null_idx_col(idx)
1258 }),
1259 #[allow(unreachable_patterns)]
1260 _ => unreachable!(),
1261 }
1262 }
1263 }
1264}
1265
1266#[cfg(feature = "postgres-vector")]
1267impl TryGetable for pgvector::Vector {
1268 #[allow(unused_variables)]
1269 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1270 match &res.row {
1271 #[cfg(feature = "sqlx-mysql")]
1272 QueryResultRow::SqlxMySql(_) => {
1273 Err(type_err("Vector unsupported by sqlx-mysql").into())
1274 }
1275 #[cfg(feature = "sqlx-postgres")]
1276 QueryResultRow::SqlxPostgres(row) => row
1277 .try_get::<Option<pgvector::Vector>, _>(idx.as_sqlx_postgres_index())
1278 .map_err(|e| sqlx_error_to_query_err(e).into())
1279 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1280 #[cfg(feature = "sqlx-sqlite")]
1281 QueryResultRow::SqlxSqlite(_) => {
1282 Err(type_err("Vector unsupported by sqlx-sqlite").into())
1283 }
1284 #[cfg(feature = "rusqlite")]
1285 QueryResultRow::Rusqlite(_) => Err(type_err("Vector unsupported by rusqlite").into()),
1286 #[cfg(feature = "mock")]
1287 QueryResultRow::Mock(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1288 debug_print!("{:#?}", e.to_string());
1289 err_null_idx_col(idx)
1290 }),
1291 #[cfg(feature = "proxy")]
1292 QueryResultRow::Proxy(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1293 debug_print!("{:#?}", e.to_string());
1294 err_null_idx_col(idx)
1295 }),
1296 #[allow(unreachable_patterns)]
1297 _ => unreachable!(),
1298 }
1299 }
1300}
1301
1302pub trait TryGetableMany: Sized {
1310 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>;
1313
1314 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError>;
1316
1317 fn find_by_statement<C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<Self, C>>
1375 where
1376 C: strum::IntoEnumIterator + sea_query::Iden,
1377 {
1378 SelectorRaw {
1379 stmt,
1380 selector: PhantomData,
1381 }
1382 }
1383}
1384
1385impl<T> TryGetableMany for T
1386where
1387 T: TryGetable,
1388{
1389 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1390 try_get_many_with_slice_len_of(1, cols)?;
1391 T::try_get(res, pre, &cols[0])
1392 }
1393
1394 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1395 T::try_get_by_index(res, 0)
1396 }
1397}
1398
1399impl<T> TryGetableMany for (T,)
1400where
1401 T: TryGetableMany,
1402{
1403 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1404 T::try_get_many(res, pre, cols).map(|r| (r,))
1405 }
1406
1407 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1408 T::try_get_many_by_index(res).map(|r| (r,))
1409 }
1410}
1411
1412macro_rules! impl_try_get_many {
1413 ( $LEN:expr, $($T:ident : $N:expr),+ $(,)? ) => {
1414 impl< $($T),+ > TryGetableMany for ( $($T),+ )
1415 where
1416 $($T: TryGetable),+
1417 {
1418 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1419 try_get_many_with_slice_len_of($LEN, cols)?;
1420 Ok((
1421 $($T::try_get(res, pre, &cols[$N])?),+
1422 ))
1423 }
1424
1425 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1426 Ok((
1427 $($T::try_get_by_index(res, $N)?),+
1428 ))
1429 }
1430 }
1431 };
1432}
1433
1434#[rustfmt::skip]
1435mod impl_try_get_many {
1436 use super::*;
1437
1438 impl_try_get_many!( 2, T0:0, T1:1);
1439 impl_try_get_many!( 3, T0:0, T1:1, T2:2);
1440 impl_try_get_many!( 4, T0:0, T1:1, T2:2, T3:3);
1441 impl_try_get_many!( 5, T0:0, T1:1, T2:2, T3:3, T4:4);
1442 impl_try_get_many!( 6, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
1443 impl_try_get_many!( 7, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
1444 impl_try_get_many!( 8, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
1445 impl_try_get_many!( 9, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
1446 impl_try_get_many!(10, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9);
1447 impl_try_get_many!(11, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9, T10:10);
1448 impl_try_get_many!(12, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9, T10:10, T11:11);
1449}
1450
1451fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
1452 if cols.len() < len {
1453 Err(type_err(format!(
1454 "Expect {} column names supplied but got slice of length {}",
1455 len,
1456 cols.len()
1457 ))
1458 .into())
1459 } else {
1460 Ok(())
1461 }
1462}
1463
1464pub trait TryGetableArray: Sized {
1470 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
1472}
1473
1474impl<T> TryGetable for Vec<T>
1475where
1476 T: TryGetableArray,
1477{
1478 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1479 T::try_get_by(res, index)
1480 }
1481}
1482
1483#[cfg(feature = "with-json")]
1487pub trait TryGetableFromJson: Sized
1488where
1489 for<'de> Self: serde::Deserialize<'de>,
1490{
1491 #[allow(unused_variables, unreachable_code)]
1493 fn try_get_from_json<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1494 match &res.row {
1495 #[cfg(feature = "sqlx-mysql")]
1496 QueryResultRow::SqlxMySql(row) => row
1497 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
1498 .map_err(|e| sqlx_error_to_query_err(e).into())
1499 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1500 #[cfg(feature = "sqlx-postgres")]
1501 QueryResultRow::SqlxPostgres(row) => row
1502 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
1503 .map_err(|e| sqlx_error_to_query_err(e).into())
1504 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1505 #[cfg(feature = "sqlx-sqlite")]
1506 QueryResultRow::SqlxSqlite(row) => row
1507 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
1508 .map_err(|e| sqlx_error_to_query_err(e).into())
1509 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1510 #[cfg(feature = "rusqlite")]
1511 QueryResultRow::Rusqlite(row) => row
1512 .try_get::<Option<serde_json::Value>, _>(idx)?
1513 .ok_or_else(|| err_null_idx_col(idx))
1514 .and_then(|json| {
1515 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1516 }),
1517 #[cfg(feature = "mock")]
1518 QueryResultRow::Mock(row) => row
1519 .try_get::<serde_json::Value, I>(idx)
1520 .map_err(|e| {
1521 debug_print!("{:#?}", e.to_string());
1522 err_null_idx_col(idx)
1523 })
1524 .and_then(|json| {
1525 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1526 }),
1527 #[cfg(feature = "proxy")]
1528 QueryResultRow::Proxy(row) => row
1529 .try_get::<serde_json::Value, I>(idx)
1530 .map_err(|e| {
1531 debug_print!("{:#?}", e.to_string());
1532 err_null_idx_col(idx)
1533 })
1534 .and_then(|json| {
1535 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1536 }),
1537 #[allow(unreachable_patterns)]
1538 _ => unreachable!(),
1539 }
1540 }
1541
1542 fn from_json_vec(value: serde_json::Value) -> Result<Vec<Self>, TryGetError> {
1544 match value {
1545 serde_json::Value::Array(values) => {
1546 let mut res = Vec::new();
1547 for item in values {
1548 res.push(serde_json::from_value(item).map_err(crate::error::json_err)?);
1549 }
1550 Ok(res)
1551 }
1552 _ => Err(TryGetError::DbErr(DbErr::Json(
1553 "Value is not an Array".to_owned(),
1554 ))),
1555 }
1556 }
1557}
1558
1559#[cfg(feature = "with-json")]
1560impl<T> TryGetable for T
1561where
1562 T: TryGetableFromJson,
1563{
1564 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1565 T::try_get_from_json(res, index)
1566 }
1567}
1568
1569#[cfg(feature = "with-json")]
1570impl<T> TryGetableArray for T
1571where
1572 T: TryGetableFromJson,
1573{
1574 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<T>, TryGetError> {
1575 T::from_json_vec(serde_json::Value::try_get_by(res, index)?)
1576 }
1577}
1578
1579pub trait TryFromU64: Sized {
1586 fn try_from_u64(n: u64) -> Result<Self, DbErr>;
1588}
1589
1590#[cfg(feature = "with-json")]
1591use serde::de::DeserializeOwned;
1592
1593#[cfg(feature = "with-json")]
1594impl<K, V> TryGetableFromJson for HashMap<K, V>
1595where
1596 K: DeserializeOwned + Eq + Hash,
1597 V: DeserializeOwned,
1598{
1599}
1600
1601#[cfg(feature = "with-json")]
1602impl<K, V> TryGetableFromJson for BTreeMap<K, V>
1603where
1604 K: DeserializeOwned + Ord,
1605 V: DeserializeOwned,
1606{
1607}
1608
1609macro_rules! try_from_u64_err {
1610 ( $type: ty ) => {
1611 impl TryFromU64 for $type {
1612 fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1613 Err(DbErr::ConvertFromU64(stringify!($type)))
1614 }
1615 }
1616 };
1617
1618 ( $($gen_type: ident),* ) => {
1619 impl<$( $gen_type, )*> TryFromU64 for ($( $gen_type, )*)
1620 where
1621 $( $gen_type: TryFromU64, )*
1622 {
1623 fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1624 Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*)))
1625 }
1626 }
1627 };
1628}
1629
1630#[rustfmt::skip]
1631mod try_from_u64_err {
1632 use super::*;
1633
1634 try_from_u64_err!(T0, T1);
1635 try_from_u64_err!(T0, T1, T2);
1636 try_from_u64_err!(T0, T1, T2, T3);
1637 try_from_u64_err!(T0, T1, T2, T3, T4);
1638 try_from_u64_err!(T0, T1, T2, T3, T4, T5);
1639 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6);
1640 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7);
1641 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
1642 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
1643 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
1644 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
1645}
1646
1647macro_rules! try_from_u64_numeric {
1648 ( $type: ty ) => {
1649 impl TryFromU64 for $type {
1650 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1651 use std::convert::TryInto;
1652 n.try_into().map_err(|e| DbErr::TryIntoErr {
1653 from: stringify!(u64),
1654 into: stringify!($type),
1655 source: Arc::new(e),
1656 })
1657 }
1658 }
1659 };
1660}
1661
1662try_from_u64_numeric!(i8);
1663try_from_u64_numeric!(i16);
1664try_from_u64_numeric!(i32);
1665try_from_u64_numeric!(i64);
1666try_from_u64_numeric!(u8);
1667try_from_u64_numeric!(u16);
1668try_from_u64_numeric!(u32);
1669try_from_u64_numeric!(u64);
1670
1671macro_rules! try_from_u64_string {
1672 ( $type: ty ) => {
1673 impl TryFromU64 for $type {
1674 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1675 Ok(n.to_string())
1676 }
1677 }
1678 };
1679}
1680
1681try_from_u64_string!(String);
1682
1683try_from_u64_err!(bool);
1684try_from_u64_err!(f32);
1685try_from_u64_err!(f64);
1686try_from_u64_err!(Vec<u8>);
1687
1688#[cfg(feature = "with-json")]
1689try_from_u64_err!(serde_json::Value);
1690
1691#[cfg(feature = "with-chrono")]
1692try_from_u64_err!(chrono::NaiveDate);
1693
1694#[cfg(feature = "with-chrono")]
1695try_from_u64_err!(chrono::NaiveTime);
1696
1697#[cfg(feature = "with-chrono")]
1698try_from_u64_err!(chrono::NaiveDateTime);
1699
1700#[cfg(feature = "with-chrono")]
1701try_from_u64_err!(chrono::DateTime<chrono::FixedOffset>);
1702
1703#[cfg(feature = "with-chrono")]
1704try_from_u64_err!(chrono::DateTime<chrono::Utc>);
1705
1706#[cfg(feature = "with-chrono")]
1707try_from_u64_err!(chrono::DateTime<chrono::Local>);
1708
1709#[cfg(feature = "with-time")]
1710try_from_u64_err!(time::Date);
1711
1712#[cfg(feature = "with-time")]
1713try_from_u64_err!(time::Time);
1714
1715#[cfg(feature = "with-time")]
1716try_from_u64_err!(time::PrimitiveDateTime);
1717
1718#[cfg(feature = "with-time")]
1719try_from_u64_err!(time::OffsetDateTime);
1720
1721#[cfg(feature = "with-rust_decimal")]
1722try_from_u64_err!(rust_decimal::Decimal);
1723
1724#[cfg(feature = "with-uuid")]
1725try_from_u64_err!(uuid::Uuid);
1726
1727#[cfg(feature = "with-ipnetwork")]
1728try_from_u64_err!(ipnetwork::IpNetwork);
1729
1730#[cfg(feature = "with-mac_address")]
1731try_from_u64_err!(mac_address::MacAddress);
1732
1733#[cfg(test)]
1734mod tests {
1735 use super::*;
1736 use crate::{MockRow, RuntimeErr};
1737 use sea_query::Value;
1738
1739 use crate::{QueryResult, TryGetable};
1740 use serde::{Deserialize, Serialize};
1741 use std::collections::{BTreeMap, HashMap};
1742
1743 #[test]
1744 fn from_try_get_error() {
1745 let try_get_error = TryGetError::DbErr(DbErr::Query(RuntimeErr::Internal(
1747 "expected error message".to_owned(),
1748 )));
1749 assert_eq!(
1750 DbErr::from(try_get_error),
1751 DbErr::Query(RuntimeErr::Internal("expected error message".to_owned()))
1752 );
1753
1754 let try_get_error = TryGetError::Null("column".to_owned());
1756 let expected = "A null value was encountered while decoding column".to_owned();
1757 assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected));
1758 }
1759
1760 #[test]
1761 fn build_with_query() {
1762 use sea_orm::{DbBackend, Statement};
1763 use sea_query::{
1764 ColumnRef, CommonTableExpression, Cycle, Expr, ExprTrait, JoinType, SelectStatement,
1765 UnionType, WithClause,
1766 };
1767
1768 let base_query = SelectStatement::new()
1769 .column("id")
1770 .expr(1i32)
1771 .column("next")
1772 .column("value")
1773 .from("table")
1774 .to_owned();
1775
1776 let cte_referencing = SelectStatement::new()
1777 .column("id")
1778 .expr(Expr::col("depth").add(1i32))
1779 .column("next")
1780 .column("value")
1781 .from("table")
1782 .join(
1783 JoinType::InnerJoin,
1784 "cte_traversal",
1785 Expr::col(("cte_traversal", "next")).equals(("table", "id")),
1786 )
1787 .to_owned();
1788
1789 let common_table_expression = CommonTableExpression::new()
1790 .query(
1791 base_query
1792 .clone()
1793 .union(UnionType::All, cte_referencing)
1794 .to_owned(),
1795 )
1796 .columns(["id", "depth", "next", "value"])
1797 .table_name("cte_traversal")
1798 .to_owned();
1799
1800 let select = SelectStatement::new()
1801 .column(ColumnRef::Asterisk(None))
1802 .from("cte_traversal")
1803 .to_owned();
1804
1805 let with_clause = WithClause::new()
1806 .recursive(true)
1807 .cte(common_table_expression)
1808 .cycle(Cycle::new_from_expr_set_using(
1809 Expr::column("id"),
1810 "looped",
1811 "traversal_path",
1812 ))
1813 .to_owned();
1814
1815 let with_query = select.with(with_clause).to_owned();
1816
1817 assert_eq!(
1818 DbBackend::MySql.build(&with_query),
1819 Statement::from_sql_and_values(
1820 DbBackend::MySql,
1821 r"WITH RECURSIVE `cte_traversal` (`id`, `depth`, `next`, `value`) AS (SELECT `id`, ?, `next`, `value` FROM `table` UNION ALL (SELECT `id`, `depth` + ?, `next`, `value` FROM `table` INNER JOIN `cte_traversal` ON `cte_traversal`.`next` = `table`.`id`)) SELECT * FROM `cte_traversal`",
1822 [1.into(), 1.into()]
1823 )
1824 );
1825 }
1826
1827 #[test]
1828 fn column_names_from_query_result() {
1829 let mut values = BTreeMap::new();
1830 values.insert("id".to_string(), Value::Int(Some(1)));
1831 values.insert("name".to_string(), Value::String(Some("Abc".to_owned())));
1832 let query_result = QueryResult {
1833 row: QueryResultRow::Mock(MockRow { values }),
1834 };
1835 assert_eq!(
1836 query_result.column_names(),
1837 vec!["id".to_owned(), "name".to_owned()]
1838 );
1839 }
1840
1841 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1842 struct Component {
1843 base_price: i32,
1844 component_type: String,
1845 }
1846
1847 #[test]
1848 fn json_deserialize_to_btreemap() {
1849 let json_value = serde_json::json!({
1850 "engine": {
1851 "base_price": 100,
1852 "component_type": "metal"
1853 }
1854 });
1855
1856 let values = BTreeMap::from([(
1857 "components".to_string(),
1858 Value::Json(Some(Box::new(json_value))),
1859 )]);
1860
1861 let row = QueryResultRow::Mock(MockRow { values });
1862
1863 let result: BTreeMap<String, Component> =
1864 TryGetable::try_get_by(&QueryResult { row }, "components").unwrap();
1865
1866 assert_eq!(
1867 result.get("engine"),
1868 Some(&Component {
1869 base_price: 100,
1870 component_type: "metal".to_owned(),
1871 })
1872 );
1873 }
1874
1875 #[test]
1876 fn json_deserialize_to_hashmap() {
1877 let json_value = serde_json::json!({
1878 "engine": {
1879 "base_price": 100,
1880 "component_type": "metal"
1881 }
1882 });
1883
1884 let values = BTreeMap::from([(
1885 "components".to_string(),
1886 Value::Json(Some(Box::new(json_value))),
1887 )]);
1888
1889 let row = QueryResultRow::Mock(MockRow { values });
1890
1891 let result: HashMap<String, Component> =
1892 TryGetable::try_get_by(&QueryResult { row }, "components").unwrap();
1893
1894 assert_eq!(
1895 result.get("engine"),
1896 Some(&Component {
1897 base_price: 100,
1898 component_type: "metal".to_owned(),
1899 })
1900 );
1901 }
1902}