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