Skip to main content

sea_orm/executor/
query.rs

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