sea_orm/executor/
query.rs

1use crate::{error::*, SelectGetableValue, SelectorRaw, Statement};
2use std::fmt;
3
4#[cfg(any(feature = "mock", feature = "proxy"))]
5use crate::debug_print;
6
7#[cfg(feature = "sqlx-dep")]
8use crate::driver::*;
9#[cfg(feature = "sqlx-dep")]
10use sqlx::Row;
11
12/// Defines the result of a query operation on a Model
13#[derive(Debug)]
14pub struct QueryResult {
15    pub(crate) row: QueryResultRow,
16}
17
18#[allow(clippy::enum_variant_names)]
19pub(crate) enum QueryResultRow {
20    #[cfg(feature = "sqlx-mysql")]
21    SqlxMySql(sqlx::mysql::MySqlRow),
22    #[cfg(feature = "sqlx-postgres")]
23    SqlxPostgres(sqlx::postgres::PgRow),
24    #[cfg(feature = "sqlx-sqlite")]
25    SqlxSqlite(sqlx::sqlite::SqliteRow),
26    #[cfg(feature = "mock")]
27    Mock(crate::MockRow),
28    #[cfg(feature = "proxy")]
29    Proxy(crate::ProxyRow),
30}
31
32/// An interface to get a value from the query result
33pub trait TryGetable: Sized {
34    /// Get a value from the query result with an ColIdx
35    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError>;
36
37    /// Get a value from the query result with prefixed column name
38    fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
39        if pre.is_empty() {
40            Self::try_get_by(res, col)
41        } else {
42            Self::try_get_by(res, format!("{pre}{col}").as_str())
43        }
44    }
45
46    /// Get a value from the query result based on the order in the select expressions
47    fn try_get_by_index(res: &QueryResult, index: usize) -> Result<Self, TryGetError> {
48        Self::try_get_by(res, index)
49    }
50}
51
52/// An error from trying to get a row from a Model
53#[derive(Debug)]
54pub enum TryGetError {
55    /// A database error was encountered as defined in [crate::DbErr]
56    DbErr(DbErr),
57    /// A null value was encountered
58    Null(String),
59}
60
61impl From<TryGetError> for DbErr {
62    fn from(e: TryGetError) -> DbErr {
63        match e {
64            TryGetError::DbErr(e) => e,
65            TryGetError::Null(s) => {
66                type_err(format!("A null value was encountered while decoding {s}"))
67            }
68        }
69    }
70}
71
72impl From<DbErr> for TryGetError {
73    fn from(e: DbErr) -> TryGetError {
74        Self::DbErr(e)
75    }
76}
77
78// QueryResult //
79
80impl QueryResult {
81    /// Get a value from the query result with an ColIdx
82    pub fn try_get_by<T, I>(&self, index: I) -> Result<T, DbErr>
83    where
84        T: TryGetable,
85        I: ColIdx,
86    {
87        Ok(T::try_get_by(self, index)?)
88    }
89
90    /// Get a value from the query result with an ColIdx
91    pub fn try_get_by_nullable<T, I>(&self, index: I) -> Result<T, TryGetError>
92    where
93        T: TryGetable,
94        I: ColIdx,
95    {
96        T::try_get_by(self, index)
97    }
98
99    /// Get a value from the query result with prefixed column name
100    pub fn try_get<T>(&self, pre: &str, col: &str) -> Result<T, DbErr>
101    where
102        T: TryGetable,
103    {
104        Ok(T::try_get(self, pre, col)?)
105    }
106
107    /// Get a value from the query result with prefixed column name
108    pub fn try_get_nullable<T>(&self, pre: &str, col: &str) -> Result<T, TryGetError>
109    where
110        T: TryGetable,
111    {
112        T::try_get(self, pre, col)
113    }
114
115    /// Get a value from the query result based on the order in the select expressions
116    pub fn try_get_by_index<T>(&self, idx: usize) -> Result<T, DbErr>
117    where
118        T: TryGetable,
119    {
120        Ok(T::try_get_by_index(self, idx)?)
121    }
122
123    /// Get a value from the query result based on the order in the select expressions
124    pub fn try_get_by_index_nullable<T>(&self, idx: usize) -> Result<T, TryGetError>
125    where
126        T: TryGetable,
127    {
128        T::try_get_by_index(self, idx)
129    }
130
131    /// Get a tuple value from the query result with prefixed column name
132    pub fn try_get_many<T>(&self, pre: &str, cols: &[String]) -> Result<T, DbErr>
133    where
134        T: TryGetableMany,
135    {
136        Ok(T::try_get_many(self, pre, cols)?)
137    }
138
139    /// Get a tuple value from the query result based on the order in the select expressions
140    pub fn try_get_many_by_index<T>(&self) -> Result<T, DbErr>
141    where
142        T: TryGetableMany,
143    {
144        Ok(T::try_get_many_by_index(self)?)
145    }
146
147    /// Retrieves the names of the columns in the result set
148    pub fn column_names(&self) -> Vec<String> {
149        #[cfg(feature = "sqlx-dep")]
150        use sqlx::Column;
151
152        match &self.row {
153            #[cfg(feature = "sqlx-mysql")]
154            QueryResultRow::SqlxMySql(row) => {
155                row.columns().iter().map(|c| c.name().to_string()).collect()
156            }
157            #[cfg(feature = "sqlx-postgres")]
158            QueryResultRow::SqlxPostgres(row) => {
159                row.columns().iter().map(|c| c.name().to_string()).collect()
160            }
161            #[cfg(feature = "sqlx-sqlite")]
162            QueryResultRow::SqlxSqlite(row) => {
163                row.columns().iter().map(|c| c.name().to_string()).collect()
164            }
165            #[cfg(feature = "mock")]
166            QueryResultRow::Mock(row) => row
167                .clone()
168                .into_column_value_tuples()
169                .map(|(c, _)| c.to_string())
170                .collect(),
171            #[cfg(feature = "proxy")]
172            QueryResultRow::Proxy(row) => row
173                .clone()
174                .into_column_value_tuples()
175                .map(|(c, _)| c.to_string())
176                .collect(),
177            #[allow(unreachable_patterns)]
178            _ => unreachable!(),
179        }
180    }
181
182    /// Access the underlying `MySqlRow` if we use the MySQL backend.
183    #[cfg(feature = "sqlx-mysql")]
184    pub fn try_as_mysql_row(&self) -> Option<&sqlx::mysql::MySqlRow> {
185        match &self.row {
186            QueryResultRow::SqlxMySql(mysql_row) => Some(mysql_row),
187            #[allow(unreachable_patterns)]
188            _ => None,
189        }
190    }
191
192    /// Access the underlying `PgRow` if we use the Postgres backend.
193    #[cfg(feature = "sqlx-postgres")]
194    pub fn try_as_pg_row(&self) -> Option<&sqlx::postgres::PgRow> {
195        match &self.row {
196            QueryResultRow::SqlxPostgres(pg_row) => Some(pg_row),
197            #[allow(unreachable_patterns)]
198            _ => None,
199        }
200    }
201
202    /// Access the underlying `SqliteRow` if we use the SQLite backend.
203    #[cfg(feature = "sqlx-sqlite")]
204    pub fn try_as_sqlite_row(&self) -> Option<&sqlx::sqlite::SqliteRow> {
205        match &self.row {
206            QueryResultRow::SqlxSqlite(sqlite_row) => Some(sqlite_row),
207            #[allow(unreachable_patterns)]
208            _ => None,
209        }
210    }
211
212    /// Access the underlying `MockRow` if we use a mock.
213    #[cfg(feature = "mock")]
214    pub fn try_as_mock_row(&self) -> Option<&crate::MockRow> {
215        match &self.row {
216            QueryResultRow::Mock(mock_row) => Some(mock_row),
217            #[allow(unreachable_patterns)]
218            _ => None,
219        }
220    }
221
222    /// Access the underlying `ProxyRow` if we use a proxy.
223    #[cfg(feature = "proxy")]
224    pub fn try_as_proxy_row(&self) -> Option<&crate::ProxyRow> {
225        match &self.row {
226            QueryResultRow::Proxy(proxy_row) => Some(proxy_row),
227            #[allow(unreachable_patterns)]
228            _ => None,
229        }
230    }
231}
232
233#[allow(unused_variables)]
234impl fmt::Debug for QueryResultRow {
235    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236        match self {
237            #[cfg(feature = "sqlx-mysql")]
238            Self::SqlxMySql(row) => write!(f, "{row:?}"),
239            #[cfg(feature = "sqlx-postgres")]
240            Self::SqlxPostgres(_) => write!(f, "QueryResultRow::SqlxPostgres cannot be inspected"),
241            #[cfg(feature = "sqlx-sqlite")]
242            Self::SqlxSqlite(_) => write!(f, "QueryResultRow::SqlxSqlite cannot be inspected"),
243            #[cfg(feature = "mock")]
244            Self::Mock(row) => write!(f, "{row:?}"),
245            #[cfg(feature = "proxy")]
246            Self::Proxy(row) => write!(f, "{row:?}"),
247            #[allow(unreachable_patterns)]
248            _ => unreachable!(),
249        }
250    }
251}
252
253// TryGetable //
254
255impl<T: TryGetable> TryGetable for Option<T> {
256    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
257        match T::try_get_by(res, index) {
258            Ok(v) => Ok(Some(v)),
259            Err(TryGetError::Null(_)) => Ok(None),
260            #[cfg(feature = "sqlx-dep")]
261            Err(TryGetError::DbErr(DbErr::Query(RuntimeErr::SqlxError(
262                sqlx::Error::ColumnNotFound(_),
263            )))) => Ok(None),
264            Err(e) => Err(e),
265        }
266    }
267}
268
269/// Column Index, used by [`TryGetable`]. Implemented for `&str` and `usize`
270pub trait ColIdx: std::fmt::Debug + Copy {
271    #[cfg(feature = "sqlx-mysql")]
272    /// Type surrogate
273    type SqlxMySqlIndex: sqlx::ColumnIndex<sqlx::mysql::MySqlRow>;
274    #[cfg(feature = "sqlx-postgres")]
275    /// Type surrogate
276    type SqlxPostgresIndex: sqlx::ColumnIndex<sqlx::postgres::PgRow>;
277    #[cfg(feature = "sqlx-sqlite")]
278    /// Type surrogate
279    type SqlxSqliteIndex: sqlx::ColumnIndex<sqlx::sqlite::SqliteRow>;
280
281    #[cfg(feature = "sqlx-mysql")]
282    /// Basically a no-op; only to satisfy trait bounds
283    fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex;
284    #[cfg(feature = "sqlx-postgres")]
285    /// Basically a no-op; only to satisfy trait bounds
286    fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex;
287    #[cfg(feature = "sqlx-sqlite")]
288    /// Basically a no-op; only to satisfy trait bounds
289    fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex;
290
291    /// Self must be `&str`, return `None` otherwise
292    fn as_str(&self) -> Option<&str>;
293    /// Self must be `usize`, return `None` otherwise
294    fn as_usize(&self) -> Option<&usize>;
295}
296
297impl ColIdx for &str {
298    #[cfg(feature = "sqlx-mysql")]
299    type SqlxMySqlIndex = Self;
300    #[cfg(feature = "sqlx-postgres")]
301    type SqlxPostgresIndex = Self;
302    #[cfg(feature = "sqlx-sqlite")]
303    type SqlxSqliteIndex = Self;
304
305    #[cfg(feature = "sqlx-mysql")]
306    #[inline]
307    fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
308        self
309    }
310    #[cfg(feature = "sqlx-postgres")]
311    #[inline]
312    fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
313        self
314    }
315    #[cfg(feature = "sqlx-sqlite")]
316    #[inline]
317    fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
318        self
319    }
320
321    #[inline]
322    fn as_str(&self) -> Option<&str> {
323        Some(self)
324    }
325    #[inline]
326    fn as_usize(&self) -> Option<&usize> {
327        None
328    }
329}
330
331impl ColIdx for usize {
332    #[cfg(feature = "sqlx-mysql")]
333    type SqlxMySqlIndex = Self;
334    #[cfg(feature = "sqlx-postgres")]
335    type SqlxPostgresIndex = Self;
336    #[cfg(feature = "sqlx-sqlite")]
337    type SqlxSqliteIndex = Self;
338
339    #[cfg(feature = "sqlx-mysql")]
340    #[inline]
341    fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
342        *self
343    }
344    #[cfg(feature = "sqlx-postgres")]
345    #[inline]
346    fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
347        *self
348    }
349    #[cfg(feature = "sqlx-sqlite")]
350    #[inline]
351    fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
352        *self
353    }
354
355    #[inline]
356    fn as_str(&self) -> Option<&str> {
357        None
358    }
359    #[inline]
360    fn as_usize(&self) -> Option<&usize> {
361        Some(self)
362    }
363}
364
365macro_rules! try_getable_all {
366    ( $type: ty ) => {
367        impl TryGetable for $type {
368            #[allow(unused_variables)]
369            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
370                match &res.row {
371                    #[cfg(feature = "sqlx-mysql")]
372                    QueryResultRow::SqlxMySql(row) => row
373                        .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
374                        .map_err(|e| sqlx_error_to_query_err(e).into())
375                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
376                    #[cfg(feature = "sqlx-postgres")]
377                    QueryResultRow::SqlxPostgres(row) => row
378                        .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
379                        .map_err(|e| sqlx_error_to_query_err(e).into())
380                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
381                    #[cfg(feature = "sqlx-sqlite")]
382                    QueryResultRow::SqlxSqlite(row) => row
383                        .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
384                        .map_err(|e| sqlx_error_to_query_err(e).into())
385                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
386                    #[cfg(feature = "mock")]
387                    QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
388                        debug_print!("{:#?}", e.to_string());
389                        err_null_idx_col(idx)
390                    }),
391                    #[cfg(feature = "proxy")]
392                    QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
393                        debug_print!("{:#?}", e.to_string());
394                        err_null_idx_col(idx)
395                    }),
396                    #[allow(unreachable_patterns)]
397                    _ => unreachable!(),
398                }
399            }
400        }
401    };
402}
403
404macro_rules! try_getable_unsigned {
405    ( $type: ty ) => {
406        impl TryGetable for $type {
407            #[allow(unused_variables)]
408            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
409                match &res.row {
410                    #[cfg(feature = "sqlx-mysql")]
411                    QueryResultRow::SqlxMySql(row) => row
412                        .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
413                        .map_err(|e| sqlx_error_to_query_err(e).into())
414                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
415                    #[cfg(feature = "sqlx-postgres")]
416                    QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
417                        "{} unsupported by sqlx-postgres",
418                        stringify!($type)
419                    ))
420                    .into()),
421                    #[cfg(feature = "sqlx-sqlite")]
422                    QueryResultRow::SqlxSqlite(row) => row
423                        .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
424                        .map_err(|e| sqlx_error_to_query_err(e).into())
425                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
426                    #[cfg(feature = "mock")]
427                    QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
428                        debug_print!("{:#?}", e.to_string());
429                        err_null_idx_col(idx)
430                    }),
431                    #[cfg(feature = "proxy")]
432                    QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
433                        debug_print!("{:#?}", e.to_string());
434                        err_null_idx_col(idx)
435                    }),
436                    #[allow(unreachable_patterns)]
437                    _ => unreachable!(),
438                }
439            }
440        }
441    };
442}
443
444macro_rules! try_getable_mysql {
445    ( $type: ty ) => {
446        impl TryGetable for $type {
447            #[allow(unused_variables)]
448            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
449                match &res.row {
450                    #[cfg(feature = "sqlx-mysql")]
451                    QueryResultRow::SqlxMySql(row) => row
452                        .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
453                        .map_err(|e| sqlx_error_to_query_err(e).into())
454                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
455                    #[cfg(feature = "sqlx-postgres")]
456                    QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
457                        "{} unsupported by sqlx-postgres",
458                        stringify!($type)
459                    ))
460                    .into()),
461                    #[cfg(feature = "sqlx-sqlite")]
462                    QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
463                        "{} unsupported by sqlx-sqlite",
464                        stringify!($type)
465                    ))
466                    .into()),
467                    #[cfg(feature = "mock")]
468                    QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
469                        debug_print!("{:#?}", e.to_string());
470                        err_null_idx_col(idx)
471                    }),
472                    #[cfg(feature = "proxy")]
473                    QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
474                        debug_print!("{:#?}", e.to_string());
475                        err_null_idx_col(idx)
476                    }),
477                    #[allow(unreachable_patterns)]
478                    _ => unreachable!(),
479                }
480            }
481        }
482    };
483}
484
485#[allow(unused_macros)]
486macro_rules! try_getable_date_time {
487    ( $type: ty ) => {
488        impl TryGetable for $type {
489            #[allow(unused_variables)]
490            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
491                match &res.row {
492                    #[cfg(feature = "sqlx-mysql")]
493                    QueryResultRow::SqlxMySql(row) => {
494                        use chrono::{DateTime, Utc};
495                        row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_mysql_index())
496                            .map_err(|e| sqlx_error_to_query_err(e).into())
497                            .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
498                            .map(|v| v.into())
499                    }
500                    #[cfg(feature = "sqlx-postgres")]
501                    QueryResultRow::SqlxPostgres(row) => row
502                        .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
503                        .map_err(|e| sqlx_error_to_query_err(e).into())
504                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
505                    #[cfg(feature = "sqlx-sqlite")]
506                    QueryResultRow::SqlxSqlite(row) => {
507                        use chrono::{DateTime, Utc};
508                        row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_sqlite_index())
509                            .map_err(|e| sqlx_error_to_query_err(e).into())
510                            .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
511                            .map(|v| v.into())
512                    }
513                    #[cfg(feature = "mock")]
514                    QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
515                        debug_print!("{:#?}", e.to_string());
516                        err_null_idx_col(idx)
517                    }),
518                    #[cfg(feature = "proxy")]
519                    QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
520                        debug_print!("{:#?}", e.to_string());
521                        err_null_idx_col(idx)
522                    }),
523                    #[allow(unreachable_patterns)]
524                    _ => unreachable!(),
525                }
526            }
527        }
528    };
529}
530
531try_getable_all!(bool);
532try_getable_all!(i8);
533try_getable_all!(i16);
534try_getable_all!(i32);
535try_getable_all!(i64);
536try_getable_unsigned!(u8);
537try_getable_unsigned!(u16);
538try_getable_mysql!(u64);
539try_getable_all!(f32);
540try_getable_all!(f64);
541try_getable_all!(Vec<u8>);
542
543#[cfg(feature = "with-json")]
544try_getable_all!(serde_json::Value);
545
546#[cfg(feature = "with-chrono")]
547try_getable_all!(chrono::NaiveDate);
548
549#[cfg(feature = "with-chrono")]
550try_getable_all!(chrono::NaiveTime);
551
552#[cfg(feature = "with-chrono")]
553try_getable_all!(chrono::NaiveDateTime);
554
555#[cfg(feature = "with-chrono")]
556try_getable_date_time!(chrono::DateTime<chrono::FixedOffset>);
557
558#[cfg(feature = "with-chrono")]
559try_getable_all!(chrono::DateTime<chrono::Utc>);
560
561#[cfg(feature = "with-chrono")]
562try_getable_all!(chrono::DateTime<chrono::Local>);
563
564#[cfg(feature = "with-time")]
565try_getable_all!(time::Date);
566
567#[cfg(feature = "with-time")]
568try_getable_all!(time::Time);
569
570#[cfg(feature = "with-time")]
571try_getable_all!(time::PrimitiveDateTime);
572
573#[cfg(feature = "with-time")]
574try_getable_all!(time::OffsetDateTime);
575
576#[cfg(feature = "with-rust_decimal")]
577use rust_decimal::Decimal;
578
579#[cfg(feature = "with-rust_decimal")]
580impl TryGetable for Decimal {
581    #[allow(unused_variables)]
582    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
583        match &res.row {
584            #[cfg(feature = "sqlx-mysql")]
585            QueryResultRow::SqlxMySql(row) => row
586                .try_get::<Option<Decimal>, _>(idx.as_sqlx_mysql_index())
587                .map_err(|e| sqlx_error_to_query_err(e).into())
588                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
589            #[cfg(feature = "sqlx-postgres")]
590            QueryResultRow::SqlxPostgres(row) => row
591                .try_get::<Option<Decimal>, _>(idx.as_sqlx_postgres_index())
592                .map_err(|e| sqlx_error_to_query_err(e).into())
593                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
594            #[cfg(feature = "sqlx-sqlite")]
595            QueryResultRow::SqlxSqlite(row) => {
596                let val: Option<f64> = row
597                    .try_get(idx.as_sqlx_sqlite_index())
598                    .map_err(sqlx_error_to_query_err)?;
599                match val {
600                    Some(v) => Decimal::try_from(v).map_err(|e| {
601                        DbErr::TryIntoErr {
602                            from: "f64",
603                            into: "Decimal",
604                            source: Box::new(e),
605                        }
606                        .into()
607                    }),
608                    None => Err(err_null_idx_col(idx)),
609                }
610            }
611            #[cfg(feature = "mock")]
612            #[allow(unused_variables)]
613            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
614                debug_print!("{:#?}", e.to_string());
615                err_null_idx_col(idx)
616            }),
617            #[cfg(feature = "proxy")]
618            #[allow(unused_variables)]
619            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
620                debug_print!("{:#?}", e.to_string());
621                err_null_idx_col(idx)
622            }),
623            #[allow(unreachable_patterns)]
624            _ => unreachable!(),
625        }
626    }
627}
628
629#[cfg(feature = "with-bigdecimal")]
630use bigdecimal::BigDecimal;
631
632#[cfg(feature = "with-bigdecimal")]
633impl TryGetable for BigDecimal {
634    #[allow(unused_variables)]
635    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
636        match &res.row {
637            #[cfg(feature = "sqlx-mysql")]
638            QueryResultRow::SqlxMySql(row) => row
639                .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_mysql_index())
640                .map_err(|e| sqlx_error_to_query_err(e).into())
641                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
642            #[cfg(feature = "sqlx-postgres")]
643            QueryResultRow::SqlxPostgres(row) => row
644                .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_postgres_index())
645                .map_err(|e| sqlx_error_to_query_err(e).into())
646                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
647            #[cfg(feature = "sqlx-sqlite")]
648            QueryResultRow::SqlxSqlite(row) => {
649                let val: Option<f64> = row
650                    .try_get(idx.as_sqlx_sqlite_index())
651                    .map_err(sqlx_error_to_query_err)?;
652                match val {
653                    Some(v) => BigDecimal::try_from(v).map_err(|e| {
654                        DbErr::TryIntoErr {
655                            from: "f64",
656                            into: "BigDecimal",
657                            source: Box::new(e),
658                        }
659                        .into()
660                    }),
661                    None => Err(err_null_idx_col(idx)),
662                }
663            }
664            #[cfg(feature = "mock")]
665            #[allow(unused_variables)]
666            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
667                debug_print!("{:#?}", e.to_string());
668                err_null_idx_col(idx)
669            }),
670            #[cfg(feature = "proxy")]
671            #[allow(unused_variables)]
672            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
673                debug_print!("{:#?}", e.to_string());
674                err_null_idx_col(idx)
675            }),
676            #[allow(unreachable_patterns)]
677            _ => unreachable!(),
678        }
679    }
680}
681
682#[allow(unused_macros)]
683macro_rules! try_getable_uuid {
684    ( $type: ty, $conversion_fn: expr ) => {
685        #[allow(unused_variables, unreachable_code)]
686        impl TryGetable for $type {
687            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
688                let res: Result<uuid::Uuid, TryGetError> = match &res.row {
689                    #[cfg(feature = "sqlx-mysql")]
690                    QueryResultRow::SqlxMySql(row) => row
691                        .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_mysql_index())
692                        .map_err(|e| sqlx_error_to_query_err(e).into())
693                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
694                        .or_else(|_| {
695                            // MariaDB's UUID type stores UUIDs as hyphenated strings.
696                            // reference: https://github.com/SeaQL/sea-orm/pull/2485
697                            row.try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
698                                .map_err(|e| sqlx_error_to_query_err(e).into())
699                                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
700                                .map(|bytes| {
701                                    String::from_utf8(bytes).map_err(|e| {
702                                        DbErr::TryIntoErr {
703                                            from: "Vec<u8>",
704                                            into: "String",
705                                            source: Box::new(e),
706                                        }
707                                        .into()
708                                    })
709                                })?
710                                .and_then(|s| {
711                                    uuid::Uuid::parse_str(&s).map_err(|e| {
712                                        DbErr::TryIntoErr {
713                                            from: "String",
714                                            into: "uuid::Uuid",
715                                            source: Box::new(e),
716                                        }
717                                        .into()
718                                    })
719                                })
720                        }),
721                    #[cfg(feature = "sqlx-postgres")]
722                    QueryResultRow::SqlxPostgres(row) => row
723                        .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_postgres_index())
724                        .map_err(|e| sqlx_error_to_query_err(e).into())
725                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
726                    #[cfg(feature = "sqlx-sqlite")]
727                    QueryResultRow::SqlxSqlite(row) => row
728                        .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_sqlite_index())
729                        .map_err(|e| sqlx_error_to_query_err(e).into())
730                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
731                    #[cfg(feature = "mock")]
732                    #[allow(unused_variables)]
733                    QueryResultRow::Mock(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
734                        debug_print!("{:#?}", e.to_string());
735                        err_null_idx_col(idx)
736                    }),
737                    #[cfg(feature = "proxy")]
738                    #[allow(unused_variables)]
739                    QueryResultRow::Proxy(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
740                        debug_print!("{:#?}", e.to_string());
741                        err_null_idx_col(idx)
742                    }),
743                    #[allow(unreachable_patterns)]
744                    _ => unreachable!(),
745                };
746                res.map($conversion_fn)
747            }
748        }
749    };
750}
751
752#[cfg(feature = "with-uuid")]
753try_getable_uuid!(uuid::Uuid, Into::into);
754
755#[cfg(feature = "with-uuid")]
756try_getable_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
757
758#[cfg(feature = "with-uuid")]
759try_getable_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
760
761#[cfg(feature = "with-uuid")]
762try_getable_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
763
764#[cfg(feature = "with-uuid")]
765try_getable_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
766
767impl TryGetable for u32 {
768    #[allow(unused_variables)]
769    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
770        match &res.row {
771            #[cfg(feature = "sqlx-mysql")]
772            QueryResultRow::SqlxMySql(row) => row
773                .try_get::<Option<u32>, _>(idx.as_sqlx_mysql_index())
774                .map_err(|e| sqlx_error_to_query_err(e).into())
775                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
776            #[cfg(feature = "sqlx-postgres")]
777            QueryResultRow::SqlxPostgres(row) => {
778                use sqlx::postgres::types::Oid;
779                // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
780                // Instead, `u32` was wrapped by a `sqlx::Oid`.
781                row.try_get::<Option<Oid>, _>(idx.as_sqlx_postgres_index())
782                    .map_err(|e| sqlx_error_to_query_err(e).into())
783                    .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
784                    .map(|oid| oid.0)
785            }
786            #[cfg(feature = "sqlx-sqlite")]
787            QueryResultRow::SqlxSqlite(row) => row
788                .try_get::<Option<u32>, _>(idx.as_sqlx_sqlite_index())
789                .map_err(|e| sqlx_error_to_query_err(e).into())
790                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
791            #[cfg(feature = "mock")]
792            #[allow(unused_variables)]
793            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
794                debug_print!("{:#?}", e.to_string());
795                err_null_idx_col(idx)
796            }),
797            #[cfg(feature = "proxy")]
798            #[allow(unused_variables)]
799            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
800                debug_print!("{:#?}", e.to_string());
801                err_null_idx_col(idx)
802            }),
803            #[allow(unreachable_patterns)]
804            _ => unreachable!(),
805        }
806    }
807}
808
809impl TryGetable for String {
810    #[allow(unused_variables)]
811    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
812        match &res.row {
813            #[cfg(feature = "sqlx-mysql")]
814            QueryResultRow::SqlxMySql(row) => row
815                .try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
816                .map_err(|e| sqlx_error_to_query_err(e).into())
817                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
818                .map(|bytes| {
819                    String::from_utf8(bytes).map_err(|e| {
820                        DbErr::TryIntoErr {
821                            from: "Vec<u8>",
822                            into: "String",
823                            source: Box::new(e),
824                        }
825                        .into()
826                    })
827                })?,
828            #[cfg(feature = "sqlx-postgres")]
829            QueryResultRow::SqlxPostgres(row) => row
830                .try_get::<Option<String>, _>(idx.as_sqlx_postgres_index())
831                .map_err(|e| sqlx_error_to_query_err(e).into())
832                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
833            #[cfg(feature = "sqlx-sqlite")]
834            QueryResultRow::SqlxSqlite(row) => row
835                .try_get::<Option<String>, _>(idx.as_sqlx_sqlite_index())
836                .map_err(|e| sqlx_error_to_query_err(e).into())
837                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
838            #[cfg(feature = "mock")]
839            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
840                debug_print!("{:#?}", e.to_string());
841                err_null_idx_col(idx)
842            }),
843            #[cfg(feature = "proxy")]
844            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
845                debug_print!("{:#?}", e.to_string());
846                err_null_idx_col(idx)
847            }),
848            #[allow(unreachable_patterns)]
849            _ => unreachable!(),
850        }
851    }
852}
853
854#[allow(dead_code)]
855fn err_null_idx_col<I: ColIdx>(idx: I) -> TryGetError {
856    TryGetError::Null(format!("{idx:?}"))
857}
858
859#[cfg(feature = "postgres-array")]
860mod postgres_array {
861    use super::*;
862
863    #[allow(unused_macros)]
864    macro_rules! try_getable_postgres_array {
865        ( $type: ty ) => {
866            #[allow(unused_variables)]
867            impl TryGetable for Vec<$type> {
868                fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
869                    match &res.row {
870                        #[cfg(feature = "sqlx-mysql")]
871                        QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
872                            "{} unsupported by sqlx-mysql",
873                            stringify!($type)
874                        ))
875                        .into()),
876                        #[cfg(feature = "sqlx-postgres")]
877                        QueryResultRow::SqlxPostgres(row) => row
878                            .try_get::<Option<Vec<$type>>, _>(idx.as_sqlx_postgres_index())
879                            .map_err(|e| sqlx_error_to_query_err(e).into())
880                            .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
881                        #[cfg(feature = "sqlx-sqlite")]
882                        QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
883                            "{} unsupported by sqlx-sqlite",
884                            stringify!($type)
885                        ))
886                        .into()),
887                        #[cfg(feature = "mock")]
888                        #[allow(unused_variables)]
889                        QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
890                            debug_print!("{:#?}", e.to_string());
891                            err_null_idx_col(idx)
892                        }),
893                        #[cfg(feature = "proxy")]
894                        #[allow(unused_variables)]
895                        QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
896                            debug_print!("{:#?}", e.to_string());
897                            err_null_idx_col(idx)
898                        }),
899                        #[allow(unreachable_patterns)]
900                        _ => unreachable!(),
901                    }
902                }
903            }
904        };
905    }
906
907    try_getable_postgres_array!(bool);
908    try_getable_postgres_array!(i8);
909    try_getable_postgres_array!(i16);
910    try_getable_postgres_array!(i32);
911    try_getable_postgres_array!(i64);
912    try_getable_postgres_array!(f32);
913    try_getable_postgres_array!(f64);
914    try_getable_postgres_array!(String);
915
916    #[cfg(feature = "with-json")]
917    try_getable_postgres_array!(serde_json::Value);
918
919    #[cfg(feature = "with-chrono")]
920    try_getable_postgres_array!(chrono::NaiveDate);
921
922    #[cfg(feature = "with-chrono")]
923    try_getable_postgres_array!(chrono::NaiveTime);
924
925    #[cfg(feature = "with-chrono")]
926    try_getable_postgres_array!(chrono::NaiveDateTime);
927
928    #[cfg(feature = "with-chrono")]
929    try_getable_postgres_array!(chrono::DateTime<chrono::FixedOffset>);
930
931    #[cfg(feature = "with-chrono")]
932    try_getable_postgres_array!(chrono::DateTime<chrono::Utc>);
933
934    #[cfg(feature = "with-chrono")]
935    try_getable_postgres_array!(chrono::DateTime<chrono::Local>);
936
937    #[cfg(feature = "with-time")]
938    try_getable_postgres_array!(time::Date);
939
940    #[cfg(feature = "with-time")]
941    try_getable_postgres_array!(time::Time);
942
943    #[cfg(feature = "with-time")]
944    try_getable_postgres_array!(time::PrimitiveDateTime);
945
946    #[cfg(feature = "with-time")]
947    try_getable_postgres_array!(time::OffsetDateTime);
948
949    #[cfg(feature = "with-rust_decimal")]
950    try_getable_postgres_array!(rust_decimal::Decimal);
951
952    #[cfg(feature = "with-bigdecimal")]
953    try_getable_postgres_array!(bigdecimal::BigDecimal);
954
955    #[allow(unused_macros)]
956    macro_rules! try_getable_postgres_array_uuid {
957        ( $type: ty, $conversion_fn: expr ) => {
958            #[allow(unused_variables, unreachable_code)]
959            impl TryGetable for Vec<$type> {
960                fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
961                    let res: Result<Vec<uuid::Uuid>, TryGetError> = match &res.row {
962                        #[cfg(feature = "sqlx-mysql")]
963                        QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
964                            "{} unsupported by sqlx-mysql",
965                            stringify!($type)
966                        ))
967                        .into()),
968                        #[cfg(feature = "sqlx-postgres")]
969                        QueryResultRow::SqlxPostgres(row) => row
970                            .try_get::<Option<Vec<uuid::Uuid>>, _>(idx.as_sqlx_postgres_index())
971                            .map_err(|e| sqlx_error_to_query_err(e).into())
972                            .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
973                        #[cfg(feature = "sqlx-sqlite")]
974                        QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
975                            "{} unsupported by sqlx-sqlite",
976                            stringify!($type)
977                        ))
978                        .into()),
979                        #[cfg(feature = "mock")]
980                        QueryResultRow::Mock(row) => {
981                            row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
982                                debug_print!("{:#?}", e.to_string());
983                                err_null_idx_col(idx)
984                            })
985                        }
986                        #[cfg(feature = "proxy")]
987                        QueryResultRow::Proxy(row) => {
988                            row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
989                                debug_print!("{:#?}", e.to_string());
990                                err_null_idx_col(idx)
991                            })
992                        }
993                        #[allow(unreachable_patterns)]
994                        _ => unreachable!(),
995                    };
996                    res.map(|vec| vec.into_iter().map($conversion_fn).collect())
997                }
998            }
999        };
1000    }
1001
1002    #[cfg(feature = "with-uuid")]
1003    try_getable_postgres_array_uuid!(uuid::Uuid, Into::into);
1004
1005    #[cfg(feature = "with-uuid")]
1006    try_getable_postgres_array_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
1007
1008    #[cfg(feature = "with-uuid")]
1009    try_getable_postgres_array_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
1010
1011    #[cfg(feature = "with-uuid")]
1012    try_getable_postgres_array_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
1013
1014    #[cfg(feature = "with-uuid")]
1015    try_getable_postgres_array_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
1016
1017    impl TryGetable for Vec<u32> {
1018        #[allow(unused_variables)]
1019        fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1020            match &res.row {
1021                #[cfg(feature = "sqlx-mysql")]
1022                QueryResultRow::SqlxMySql(_) => {
1023                    Err(type_err(format!("{} unsupported by sqlx-mysql", stringify!($type))).into())
1024                }
1025                #[cfg(feature = "sqlx-postgres")]
1026                QueryResultRow::SqlxPostgres(row) => {
1027                    use sqlx::postgres::types::Oid;
1028                    // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
1029                    // Instead, `u32` was wrapped by a `sqlx::Oid`.
1030                    row.try_get::<Option<Vec<Oid>>, _>(idx.as_sqlx_postgres_index())
1031                        .map_err(|e| sqlx_error_to_query_err(e).into())
1032                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
1033                        .map(|oids| oids.into_iter().map(|oid| oid.0).collect())
1034                }
1035                #[cfg(feature = "sqlx-sqlite")]
1036                QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1037                    "{} unsupported by sqlx-sqlite",
1038                    stringify!($type)
1039                ))
1040                .into()),
1041                #[cfg(feature = "mock")]
1042                #[allow(unused_variables)]
1043                QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1044                    debug_print!("{:#?}", e.to_string());
1045                    err_null_idx_col(idx)
1046                }),
1047                #[cfg(feature = "proxy")]
1048                #[allow(unused_variables)]
1049                QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
1050                    debug_print!("{:#?}", e.to_string());
1051                    err_null_idx_col(idx)
1052                }),
1053                #[allow(unreachable_patterns)]
1054                _ => unreachable!(),
1055            }
1056        }
1057    }
1058}
1059
1060#[cfg(feature = "postgres-vector")]
1061impl TryGetable for pgvector::Vector {
1062    #[allow(unused_variables)]
1063    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1064        match &res.row {
1065            #[cfg(feature = "sqlx-mysql")]
1066            QueryResultRow::SqlxMySql(_) => {
1067                Err(type_err("Vector unsupported by sqlx-mysql").into())
1068            }
1069            #[cfg(feature = "sqlx-postgres")]
1070            QueryResultRow::SqlxPostgres(row) => row
1071                .try_get::<Option<pgvector::Vector>, _>(idx.as_sqlx_postgres_index())
1072                .map_err(|e| sqlx_error_to_query_err(e).into())
1073                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1074            #[cfg(feature = "sqlx-sqlite")]
1075            QueryResultRow::SqlxSqlite(_) => {
1076                Err(type_err("Vector unsupported by sqlx-sqlite").into())
1077            }
1078            #[cfg(feature = "mock")]
1079            QueryResultRow::Mock(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1080                debug_print!("{:#?}", e.to_string());
1081                err_null_idx_col(idx)
1082            }),
1083            #[cfg(feature = "proxy")]
1084            QueryResultRow::Proxy(row) => row.try_get::<pgvector::Vector, _>(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// TryGetableMany //
1095
1096/// An interface to get a tuple value from the query result
1097pub trait TryGetableMany: Sized {
1098    /// Get a tuple value from the query result with prefixed column name
1099    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>;
1100
1101    /// Get a tuple value from the query result based on the order in the select expressions
1102    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError>;
1103
1104    /// ```
1105    /// # use sea_orm::{error::*, tests_cfg::*, *};
1106    /// #
1107    /// # #[smol_potat::main]
1108    /// # #[cfg(all(feature = "mock", feature = "macros"))]
1109    /// # pub async fn main() -> Result<(), DbErr> {
1110    /// #
1111    /// # let db = MockDatabase::new(DbBackend::Postgres)
1112    /// #     .append_query_results([[
1113    /// #         maplit::btreemap! {
1114    /// #             "name" => Into::<Value>::into("Chocolate Forest"),
1115    /// #             "num_of_cakes" => Into::<Value>::into(1),
1116    /// #         },
1117    /// #         maplit::btreemap! {
1118    /// #             "name" => Into::<Value>::into("New York Cheese"),
1119    /// #             "num_of_cakes" => Into::<Value>::into(1),
1120    /// #         },
1121    /// #     ]])
1122    /// #     .into_connection();
1123    /// #
1124    /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DeriveIden, EnumIter, TryGetableMany};
1125    ///
1126    /// #[derive(EnumIter, DeriveIden)]
1127    /// enum ResultCol {
1128    ///     Name,
1129    ///     NumOfCakes,
1130    /// }
1131    ///
1132    /// let res: Vec<(String, i32)> =
1133    ///     <(String, i32)>::find_by_statement::<ResultCol>(Statement::from_sql_and_values(
1134    ///         DbBackend::Postgres,
1135    ///         r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
1136    ///         [],
1137    ///     ))
1138    ///     .all(&db)
1139    ///     .await?;
1140    ///
1141    /// assert_eq!(
1142    ///     res,
1143    ///     [
1144    ///         ("Chocolate Forest".to_owned(), 1),
1145    ///         ("New York Cheese".to_owned(), 1),
1146    ///     ]
1147    /// );
1148    ///
1149    /// assert_eq!(
1150    ///     db.into_transaction_log(),
1151    ///     [Transaction::from_sql_and_values(
1152    ///         DbBackend::Postgres,
1153    ///         r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
1154    ///         []
1155    ///     ),]
1156    /// );
1157    /// #
1158    /// # Ok(())
1159    /// # }
1160    /// ```
1161    fn find_by_statement<C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<Self, C>>
1162    where
1163        C: strum::IntoEnumIterator + sea_query::Iden,
1164    {
1165        SelectorRaw::<SelectGetableValue<Self, C>>::with_columns(stmt)
1166    }
1167}
1168
1169impl<T> TryGetableMany for T
1170where
1171    T: TryGetable,
1172{
1173    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1174        try_get_many_with_slice_len_of(1, cols)?;
1175        T::try_get(res, pre, &cols[0])
1176    }
1177
1178    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1179        T::try_get_by_index(res, 0)
1180    }
1181}
1182
1183impl<T> TryGetableMany for (T,)
1184where
1185    T: TryGetableMany,
1186{
1187    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1188        T::try_get_many(res, pre, cols).map(|r| (r,))
1189    }
1190
1191    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1192        T::try_get_many_by_index(res).map(|r| (r,))
1193    }
1194}
1195
1196macro_rules! impl_try_get_many {
1197    ( $LEN:expr, $($T:ident : $N:expr),+ $(,)? ) => {
1198        impl< $($T),+ > TryGetableMany for ( $($T),+ )
1199        where
1200            $($T: TryGetable),+
1201        {
1202            fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1203                try_get_many_with_slice_len_of($LEN, cols)?;
1204                Ok((
1205                    $($T::try_get(res, pre, &cols[$N])?),+
1206                ))
1207            }
1208
1209            fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1210                Ok((
1211                    $($T::try_get_by_index(res, $N)?),+
1212                ))
1213            }
1214        }
1215    };
1216}
1217
1218#[rustfmt::skip]
1219mod impl_try_get_many {
1220    use super::*;
1221
1222    impl_try_get_many!( 2, T0:0, T1:1);
1223    impl_try_get_many!( 3, T0:0, T1:1, T2:2);
1224    impl_try_get_many!( 4, T0:0, T1:1, T2:2, T3:3);
1225    impl_try_get_many!( 5, T0:0, T1:1, T2:2, T3:3, T4:4);
1226    impl_try_get_many!( 6, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
1227    impl_try_get_many!( 7, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
1228    impl_try_get_many!( 8, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
1229    impl_try_get_many!( 9, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
1230    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);
1231    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);
1232    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);
1233}
1234
1235fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
1236    if cols.len() < len {
1237        Err(type_err(format!(
1238            "Expect {} column names supplied but got slice of length {}",
1239            len,
1240            cols.len()
1241        ))
1242        .into())
1243    } else {
1244        Ok(())
1245    }
1246}
1247
1248/// An interface to get an array of values from the query result.
1249/// A type can only implement `ActiveEnum` or `TryGetableFromJson`, but not both.
1250/// A blanket impl is provided for `TryGetableFromJson`, while the impl for `ActiveEnum`
1251/// is provided by the `DeriveActiveEnum` macro. So as an end user you won't normally
1252/// touch this trait.
1253pub trait TryGetableArray: Sized {
1254    /// Just a delegate
1255    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
1256}
1257
1258impl<T> TryGetable for Vec<T>
1259where
1260    T: TryGetableArray,
1261{
1262    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1263        T::try_get_by(res, index)
1264    }
1265}
1266
1267// TryGetableFromJson //
1268
1269/// An interface to get a JSON from the query result
1270#[cfg(feature = "with-json")]
1271pub trait TryGetableFromJson: Sized
1272where
1273    for<'de> Self: serde::Deserialize<'de>,
1274{
1275    /// Get a JSON from the query result with prefixed column name
1276    #[allow(unused_variables, unreachable_code)]
1277    fn try_get_from_json<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1278        match &res.row {
1279            #[cfg(feature = "sqlx-mysql")]
1280            QueryResultRow::SqlxMySql(row) => row
1281                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
1282                .map_err(|e| sqlx_error_to_query_err(e).into())
1283                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1284            #[cfg(feature = "sqlx-postgres")]
1285            QueryResultRow::SqlxPostgres(row) => row
1286                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
1287                .map_err(|e| sqlx_error_to_query_err(e).into())
1288                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1289            #[cfg(feature = "sqlx-sqlite")]
1290            QueryResultRow::SqlxSqlite(row) => row
1291                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
1292                .map_err(|e| sqlx_error_to_query_err(e).into())
1293                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1294            #[cfg(feature = "mock")]
1295            QueryResultRow::Mock(row) => row
1296                .try_get::<serde_json::Value, I>(idx)
1297                .map_err(|e| {
1298                    debug_print!("{:#?}", e.to_string());
1299                    err_null_idx_col(idx)
1300                })
1301                .and_then(|json| serde_json::from_value(json).map_err(|e| json_err(e).into())),
1302            #[cfg(feature = "proxy")]
1303            QueryResultRow::Proxy(row) => row
1304                .try_get::<serde_json::Value, I>(idx)
1305                .map_err(|e| {
1306                    debug_print!("{:#?}", e.to_string());
1307                    err_null_idx_col(idx)
1308                })
1309                .and_then(|json| serde_json::from_value(json).map_err(|e| json_err(e).into())),
1310            #[allow(unreachable_patterns)]
1311            _ => unreachable!(),
1312        }
1313    }
1314
1315    /// Get a Vec<Self> from an Array of Json
1316    fn from_json_vec(value: serde_json::Value) -> Result<Vec<Self>, TryGetError> {
1317        match value {
1318            serde_json::Value::Array(values) => {
1319                let mut res = Vec::new();
1320                for item in values {
1321                    res.push(serde_json::from_value(item).map_err(json_err)?);
1322                }
1323                Ok(res)
1324            }
1325            _ => Err(TryGetError::DbErr(DbErr::Json(
1326                "Value is not an Array".to_owned(),
1327            ))),
1328        }
1329    }
1330}
1331
1332#[cfg(feature = "with-json")]
1333impl<T> TryGetable for T
1334where
1335    T: TryGetableFromJson,
1336{
1337    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1338        T::try_get_from_json(res, index)
1339    }
1340}
1341
1342#[cfg(feature = "with-json")]
1343impl<T> TryGetableArray for T
1344where
1345    T: TryGetableFromJson,
1346{
1347    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<T>, TryGetError> {
1348        T::from_json_vec(serde_json::Value::try_get_by(res, index)?)
1349    }
1350}
1351
1352// TryFromU64 //
1353/// Try to convert a type to a u64
1354pub trait TryFromU64: Sized {
1355    /// The method to convert the type to a u64
1356    fn try_from_u64(n: u64) -> Result<Self, DbErr>;
1357}
1358
1359macro_rules! try_from_u64_err {
1360    ( $type: ty ) => {
1361        impl TryFromU64 for $type {
1362            fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1363                Err(DbErr::ConvertFromU64(stringify!($type)))
1364            }
1365        }
1366    };
1367
1368    ( $($gen_type: ident),* ) => {
1369        impl<$( $gen_type, )*> TryFromU64 for ($( $gen_type, )*)
1370        where
1371            $( $gen_type: TryFromU64, )*
1372        {
1373            fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1374                Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*)))
1375            }
1376        }
1377    };
1378}
1379
1380#[rustfmt::skip]
1381mod try_from_u64_err {
1382    use super::*;
1383
1384    try_from_u64_err!(T0, T1);
1385    try_from_u64_err!(T0, T1, T2);
1386    try_from_u64_err!(T0, T1, T2, T3);
1387    try_from_u64_err!(T0, T1, T2, T3, T4);
1388    try_from_u64_err!(T0, T1, T2, T3, T4, T5);
1389    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6);
1390    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7);
1391    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
1392    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
1393    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
1394    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
1395}
1396
1397macro_rules! try_from_u64_numeric {
1398    ( $type: ty ) => {
1399        impl TryFromU64 for $type {
1400            fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1401                use std::convert::TryInto;
1402                n.try_into().map_err(|e| DbErr::TryIntoErr {
1403                    from: stringify!(u64),
1404                    into: stringify!($type),
1405                    source: Box::new(e),
1406                })
1407            }
1408        }
1409    };
1410}
1411
1412try_from_u64_numeric!(i8);
1413try_from_u64_numeric!(i16);
1414try_from_u64_numeric!(i32);
1415try_from_u64_numeric!(i64);
1416try_from_u64_numeric!(u8);
1417try_from_u64_numeric!(u16);
1418try_from_u64_numeric!(u32);
1419try_from_u64_numeric!(u64);
1420
1421macro_rules! try_from_u64_string {
1422    ( $type: ty ) => {
1423        impl TryFromU64 for $type {
1424            fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1425                Ok(n.to_string())
1426            }
1427        }
1428    };
1429}
1430
1431try_from_u64_string!(String);
1432
1433try_from_u64_err!(bool);
1434try_from_u64_err!(f32);
1435try_from_u64_err!(f64);
1436try_from_u64_err!(Vec<u8>);
1437
1438#[cfg(feature = "with-json")]
1439try_from_u64_err!(serde_json::Value);
1440
1441#[cfg(feature = "with-chrono")]
1442try_from_u64_err!(chrono::NaiveDate);
1443
1444#[cfg(feature = "with-chrono")]
1445try_from_u64_err!(chrono::NaiveTime);
1446
1447#[cfg(feature = "with-chrono")]
1448try_from_u64_err!(chrono::NaiveDateTime);
1449
1450#[cfg(feature = "with-chrono")]
1451try_from_u64_err!(chrono::DateTime<chrono::FixedOffset>);
1452
1453#[cfg(feature = "with-chrono")]
1454try_from_u64_err!(chrono::DateTime<chrono::Utc>);
1455
1456#[cfg(feature = "with-chrono")]
1457try_from_u64_err!(chrono::DateTime<chrono::Local>);
1458
1459#[cfg(feature = "with-time")]
1460try_from_u64_err!(time::Date);
1461
1462#[cfg(feature = "with-time")]
1463try_from_u64_err!(time::Time);
1464
1465#[cfg(feature = "with-time")]
1466try_from_u64_err!(time::PrimitiveDateTime);
1467
1468#[cfg(feature = "with-time")]
1469try_from_u64_err!(time::OffsetDateTime);
1470
1471#[cfg(feature = "with-rust_decimal")]
1472try_from_u64_err!(rust_decimal::Decimal);
1473
1474#[cfg(feature = "with-uuid")]
1475try_from_u64_err!(uuid::Uuid);
1476
1477#[cfg(test)]
1478mod tests {
1479    use std::collections::BTreeMap;
1480
1481    use sea_query::Value;
1482
1483    use super::*;
1484
1485    #[test]
1486    fn from_try_get_error() {
1487        // TryGetError::DbErr
1488        let try_get_error = TryGetError::DbErr(DbErr::Query(RuntimeErr::Internal(
1489            "expected error message".to_owned(),
1490        )));
1491        assert_eq!(
1492            DbErr::from(try_get_error),
1493            DbErr::Query(RuntimeErr::Internal("expected error message".to_owned()))
1494        );
1495
1496        // TryGetError::Null
1497        let try_get_error = TryGetError::Null("column".to_owned());
1498        let expected = "A null value was encountered while decoding column".to_owned();
1499        assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected));
1500    }
1501
1502    #[test]
1503    fn build_with_query() {
1504        use sea_orm::{DbBackend, Statement};
1505        use sea_query::*;
1506
1507        let base_query = SelectStatement::new()
1508            .column(Alias::new("id"))
1509            .expr(1i32)
1510            .column(Alias::new("next"))
1511            .column(Alias::new("value"))
1512            .from(Alias::new("table"))
1513            .to_owned();
1514
1515        let cte_referencing = SelectStatement::new()
1516            .column(Alias::new("id"))
1517            .expr(Expr::col(Alias::new("depth")).add(1i32))
1518            .column(Alias::new("next"))
1519            .column(Alias::new("value"))
1520            .from(Alias::new("table"))
1521            .join(
1522                JoinType::InnerJoin,
1523                Alias::new("cte_traversal"),
1524                Expr::col((Alias::new("cte_traversal"), Alias::new("next")))
1525                    .equals((Alias::new("table"), Alias::new("id"))),
1526            )
1527            .to_owned();
1528
1529        let common_table_expression = CommonTableExpression::new()
1530            .query(
1531                base_query
1532                    .clone()
1533                    .union(UnionType::All, cte_referencing)
1534                    .to_owned(),
1535            )
1536            .columns([
1537                Alias::new("id"),
1538                Alias::new("depth"),
1539                Alias::new("next"),
1540                Alias::new("value"),
1541            ])
1542            .table_name(Alias::new("cte_traversal"))
1543            .to_owned();
1544
1545        let select = SelectStatement::new()
1546            .column(ColumnRef::Asterisk)
1547            .from(Alias::new("cte_traversal"))
1548            .to_owned();
1549
1550        let with_clause = WithClause::new()
1551            .recursive(true)
1552            .cte(common_table_expression)
1553            .cycle(Cycle::new_from_expr_set_using(
1554                SimpleExpr::Column(ColumnRef::Column(Alias::new("id").into_iden())),
1555                Alias::new("looped"),
1556                Alias::new("traversal_path"),
1557            ))
1558            .to_owned();
1559
1560        let with_query = select.with(with_clause).to_owned();
1561
1562        assert_eq!(
1563            DbBackend::MySql.build(&with_query),
1564            Statement::from_sql_and_values(
1565                DbBackend::MySql,
1566                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`"#,
1567                [1.into(), 1.into()]
1568            )
1569        );
1570    }
1571
1572    #[test]
1573    fn column_names_from_query_result() {
1574        let mut values = BTreeMap::new();
1575        values.insert("id".to_string(), Value::Int(Some(1)));
1576        values.insert(
1577            "name".to_string(),
1578            Value::String(Some(Box::new("Abc".to_owned()))),
1579        );
1580        let query_result = QueryResult {
1581            row: QueryResultRow::Mock(crate::MockRow { values }),
1582        };
1583        assert_eq!(
1584            query_result.column_names(),
1585            vec!["id".to_owned(), "name".to_owned()]
1586        );
1587    }
1588}