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    /// # #[smol_potat::main]
1308    /// # #[cfg(all(feature = "mock", feature = "macros"))]
1309    /// # pub async fn main() -> Result<(), DbErr> {
1310    /// #
1311    /// # let db = MockDatabase::new(DbBackend::Postgres)
1312    /// #     .append_query_results([[
1313    /// #         maplit::btreemap! {
1314    /// #             "name" => Into::<Value>::into("Chocolate Forest"),
1315    /// #             "num_of_cakes" => Into::<Value>::into(1),
1316    /// #         },
1317    /// #         maplit::btreemap! {
1318    /// #             "name" => Into::<Value>::into("New York Cheese"),
1319    /// #             "num_of_cakes" => Into::<Value>::into(1),
1320    /// #         },
1321    /// #     ]])
1322    /// #     .into_connection();
1323    /// #
1324    /// use sea_orm::{DeriveIden, EnumIter, TryGetableMany, entity::*, query::*, tests_cfg::cake};
1325    ///
1326    /// #[derive(EnumIter, DeriveIden)]
1327    /// enum ResultCol {
1328    ///     Name,
1329    ///     NumOfCakes,
1330    /// }
1331    ///
1332    /// let res: Vec<(String, i32)> =
1333    ///     <(String, i32)>::find_by_statement::<ResultCol>(Statement::from_sql_and_values(
1334    ///         DbBackend::Postgres,
1335    ///         r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
1336    ///         [],
1337    ///     ))
1338    ///     .all(&db)
1339    ///     .await?;
1340    ///
1341    /// assert_eq!(
1342    ///     res,
1343    ///     [
1344    ///         ("Chocolate Forest".to_owned(), 1),
1345    ///         ("New York Cheese".to_owned(), 1),
1346    ///     ]
1347    /// );
1348    ///
1349    /// assert_eq!(
1350    ///     db.into_transaction_log(),
1351    ///     [Transaction::from_sql_and_values(
1352    ///         DbBackend::Postgres,
1353    ///         r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
1354    ///         []
1355    ///     ),]
1356    /// );
1357    /// #
1358    /// # Ok(())
1359    /// # }
1360    /// ```
1361    fn find_by_statement<C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<Self, C>>
1362    where
1363        C: strum::IntoEnumIterator + sea_query::Iden,
1364    {
1365        SelectorRaw {
1366            stmt,
1367            selector: PhantomData,
1368        }
1369    }
1370}
1371
1372impl<T> TryGetableMany for T
1373where
1374    T: TryGetable,
1375{
1376    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1377        try_get_many_with_slice_len_of(1, cols)?;
1378        T::try_get(res, pre, &cols[0])
1379    }
1380
1381    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1382        T::try_get_by_index(res, 0)
1383    }
1384}
1385
1386impl<T> TryGetableMany for (T,)
1387where
1388    T: TryGetableMany,
1389{
1390    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1391        T::try_get_many(res, pre, cols).map(|r| (r,))
1392    }
1393
1394    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1395        T::try_get_many_by_index(res).map(|r| (r,))
1396    }
1397}
1398
1399macro_rules! impl_try_get_many {
1400    ( $LEN:expr, $($T:ident : $N:expr),+ $(,)? ) => {
1401        impl< $($T),+ > TryGetableMany for ( $($T),+ )
1402        where
1403            $($T: TryGetable),+
1404        {
1405            fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1406                try_get_many_with_slice_len_of($LEN, cols)?;
1407                Ok((
1408                    $($T::try_get(res, pre, &cols[$N])?),+
1409                ))
1410            }
1411
1412            fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1413                Ok((
1414                    $($T::try_get_by_index(res, $N)?),+
1415                ))
1416            }
1417        }
1418    };
1419}
1420
1421#[rustfmt::skip]
1422mod impl_try_get_many {
1423    use super::*;
1424
1425    impl_try_get_many!( 2, T0:0, T1:1);
1426    impl_try_get_many!( 3, T0:0, T1:1, T2:2);
1427    impl_try_get_many!( 4, T0:0, T1:1, T2:2, T3:3);
1428    impl_try_get_many!( 5, T0:0, T1:1, T2:2, T3:3, T4:4);
1429    impl_try_get_many!( 6, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
1430    impl_try_get_many!( 7, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
1431    impl_try_get_many!( 8, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
1432    impl_try_get_many!( 9, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
1433    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);
1434    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);
1435    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);
1436}
1437
1438fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
1439    if cols.len() < len {
1440        Err(type_err(format!(
1441            "Expect {} column names supplied but got slice of length {}",
1442            len,
1443            cols.len()
1444        ))
1445        .into())
1446    } else {
1447        Ok(())
1448    }
1449}
1450
1451/// An interface to get an array of values from the query result.
1452/// A type can only implement `ActiveEnum` or `TryGetableFromJson`, but not both.
1453/// A blanket impl is provided for `TryGetableFromJson`, while the impl for `ActiveEnum`
1454/// is provided by the `DeriveActiveEnum` macro. So as an end user you won't normally
1455/// touch this trait.
1456pub trait TryGetableArray: Sized {
1457    /// Just a delegate
1458    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
1459}
1460
1461impl<T> TryGetable for Vec<T>
1462where
1463    T: TryGetableArray,
1464{
1465    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1466        T::try_get_by(res, index)
1467    }
1468}
1469
1470// TryGetableFromJson //
1471
1472/// An interface to get a JSON from the query result
1473#[cfg(feature = "with-json")]
1474pub trait TryGetableFromJson: Sized
1475where
1476    for<'de> Self: serde::Deserialize<'de>,
1477{
1478    /// Get a JSON from the query result with prefixed column name
1479    #[allow(unused_variables, unreachable_code)]
1480    fn try_get_from_json<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1481        match &res.row {
1482            #[cfg(feature = "sqlx-mysql")]
1483            QueryResultRow::SqlxMySql(row) => row
1484                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
1485                .map_err(|e| sqlx_error_to_query_err(e).into())
1486                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1487            #[cfg(feature = "sqlx-postgres")]
1488            QueryResultRow::SqlxPostgres(row) => row
1489                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
1490                .map_err(|e| sqlx_error_to_query_err(e).into())
1491                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1492            #[cfg(feature = "sqlx-sqlite")]
1493            QueryResultRow::SqlxSqlite(row) => row
1494                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
1495                .map_err(|e| sqlx_error_to_query_err(e).into())
1496                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1497            #[cfg(feature = "rusqlite")]
1498            QueryResultRow::Rusqlite(row) => row
1499                .try_get::<Option<serde_json::Value>, _>(idx)?
1500                .ok_or_else(|| err_null_idx_col(idx))
1501                .and_then(|json| {
1502                    serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1503                }),
1504            #[cfg(feature = "mock")]
1505            QueryResultRow::Mock(row) => row
1506                .try_get::<serde_json::Value, I>(idx)
1507                .map_err(|e| {
1508                    debug_print!("{:#?}", e.to_string());
1509                    err_null_idx_col(idx)
1510                })
1511                .and_then(|json| {
1512                    serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1513                }),
1514            #[cfg(feature = "proxy")]
1515            QueryResultRow::Proxy(row) => row
1516                .try_get::<serde_json::Value, I>(idx)
1517                .map_err(|e| {
1518                    debug_print!("{:#?}", e.to_string());
1519                    err_null_idx_col(idx)
1520                })
1521                .and_then(|json| {
1522                    serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1523                }),
1524            #[allow(unreachable_patterns)]
1525            _ => unreachable!(),
1526        }
1527    }
1528
1529    /// Get a Vec<Self> from an Array of Json
1530    fn from_json_vec(value: serde_json::Value) -> Result<Vec<Self>, TryGetError> {
1531        match value {
1532            serde_json::Value::Array(values) => {
1533                let mut res = Vec::new();
1534                for item in values {
1535                    res.push(serde_json::from_value(item).map_err(crate::error::json_err)?);
1536                }
1537                Ok(res)
1538            }
1539            _ => Err(TryGetError::DbErr(DbErr::Json(
1540                "Value is not an Array".to_owned(),
1541            ))),
1542        }
1543    }
1544}
1545
1546#[cfg(feature = "with-json")]
1547impl<T> TryGetable for T
1548where
1549    T: TryGetableFromJson,
1550{
1551    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1552        T::try_get_from_json(res, index)
1553    }
1554}
1555
1556#[cfg(feature = "with-json")]
1557impl<T> TryGetableArray for T
1558where
1559    T: TryGetableFromJson,
1560{
1561    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<T>, TryGetError> {
1562        T::from_json_vec(serde_json::Value::try_get_by(res, index)?)
1563    }
1564}
1565
1566// TryFromU64 //
1567/// Try to convert a type to a u64
1568pub trait TryFromU64: Sized {
1569    /// The method to convert the type to a u64
1570    fn try_from_u64(n: u64) -> Result<Self, DbErr>;
1571}
1572
1573#[cfg(feature = "with-json")]
1574use serde::de::DeserializeOwned;
1575
1576#[cfg(feature = "with-json")]
1577impl<K, V> TryGetableFromJson for HashMap<K, V>
1578where
1579    K: DeserializeOwned + Eq + Hash,
1580    V: DeserializeOwned,
1581{
1582}
1583
1584#[cfg(feature = "with-json")]
1585impl<K, V> TryGetableFromJson for BTreeMap<K, V>
1586where
1587    K: DeserializeOwned + Ord,
1588    V: DeserializeOwned,
1589{
1590}
1591
1592macro_rules! try_from_u64_err {
1593    ( $type: ty ) => {
1594        impl TryFromU64 for $type {
1595            fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1596                Err(DbErr::ConvertFromU64(stringify!($type)))
1597            }
1598        }
1599    };
1600
1601    ( $($gen_type: ident),* ) => {
1602        impl<$( $gen_type, )*> TryFromU64 for ($( $gen_type, )*)
1603        where
1604            $( $gen_type: TryFromU64, )*
1605        {
1606            fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1607                Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*)))
1608            }
1609        }
1610    };
1611}
1612
1613#[rustfmt::skip]
1614mod try_from_u64_err {
1615    use super::*;
1616
1617    try_from_u64_err!(T0, T1);
1618    try_from_u64_err!(T0, T1, T2);
1619    try_from_u64_err!(T0, T1, T2, T3);
1620    try_from_u64_err!(T0, T1, T2, T3, T4);
1621    try_from_u64_err!(T0, T1, T2, T3, T4, T5);
1622    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6);
1623    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7);
1624    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
1625    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
1626    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
1627    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
1628}
1629
1630macro_rules! try_from_u64_numeric {
1631    ( $type: ty ) => {
1632        impl TryFromU64 for $type {
1633            fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1634                use std::convert::TryInto;
1635                n.try_into().map_err(|e| DbErr::TryIntoErr {
1636                    from: stringify!(u64),
1637                    into: stringify!($type),
1638                    source: Arc::new(e),
1639                })
1640            }
1641        }
1642    };
1643}
1644
1645try_from_u64_numeric!(i8);
1646try_from_u64_numeric!(i16);
1647try_from_u64_numeric!(i32);
1648try_from_u64_numeric!(i64);
1649try_from_u64_numeric!(u8);
1650try_from_u64_numeric!(u16);
1651try_from_u64_numeric!(u32);
1652try_from_u64_numeric!(u64);
1653
1654macro_rules! try_from_u64_string {
1655    ( $type: ty ) => {
1656        impl TryFromU64 for $type {
1657            fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1658                Ok(n.to_string())
1659            }
1660        }
1661    };
1662}
1663
1664try_from_u64_string!(String);
1665
1666try_from_u64_err!(bool);
1667try_from_u64_err!(f32);
1668try_from_u64_err!(f64);
1669try_from_u64_err!(Vec<u8>);
1670
1671#[cfg(feature = "with-json")]
1672try_from_u64_err!(serde_json::Value);
1673
1674#[cfg(feature = "with-chrono")]
1675try_from_u64_err!(chrono::NaiveDate);
1676
1677#[cfg(feature = "with-chrono")]
1678try_from_u64_err!(chrono::NaiveTime);
1679
1680#[cfg(feature = "with-chrono")]
1681try_from_u64_err!(chrono::NaiveDateTime);
1682
1683#[cfg(feature = "with-chrono")]
1684try_from_u64_err!(chrono::DateTime<chrono::FixedOffset>);
1685
1686#[cfg(feature = "with-chrono")]
1687try_from_u64_err!(chrono::DateTime<chrono::Utc>);
1688
1689#[cfg(feature = "with-chrono")]
1690try_from_u64_err!(chrono::DateTime<chrono::Local>);
1691
1692#[cfg(feature = "with-time")]
1693try_from_u64_err!(time::Date);
1694
1695#[cfg(feature = "with-time")]
1696try_from_u64_err!(time::Time);
1697
1698#[cfg(feature = "with-time")]
1699try_from_u64_err!(time::PrimitiveDateTime);
1700
1701#[cfg(feature = "with-time")]
1702try_from_u64_err!(time::OffsetDateTime);
1703
1704#[cfg(feature = "with-rust_decimal")]
1705try_from_u64_err!(rust_decimal::Decimal);
1706
1707#[cfg(feature = "with-uuid")]
1708try_from_u64_err!(uuid::Uuid);
1709
1710#[cfg(feature = "with-ipnetwork")]
1711try_from_u64_err!(ipnetwork::IpNetwork);
1712
1713#[cfg(feature = "with-mac_address")]
1714try_from_u64_err!(mac_address::MacAddress);
1715
1716#[cfg(test)]
1717mod tests {
1718    use super::*;
1719    use crate::{MockRow, RuntimeErr};
1720    use sea_query::Value;
1721
1722    use crate::{QueryResult, TryGetable};
1723    use serde::{Deserialize, Serialize};
1724    use std::collections::{BTreeMap, HashMap};
1725
1726    #[test]
1727    fn from_try_get_error() {
1728        // TryGetError::DbErr
1729        let try_get_error = TryGetError::DbErr(DbErr::Query(RuntimeErr::Internal(
1730            "expected error message".to_owned(),
1731        )));
1732        assert_eq!(
1733            DbErr::from(try_get_error),
1734            DbErr::Query(RuntimeErr::Internal("expected error message".to_owned()))
1735        );
1736
1737        // TryGetError::Null
1738        let try_get_error = TryGetError::Null("column".to_owned());
1739        let expected = "A null value was encountered while decoding column".to_owned();
1740        assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected));
1741    }
1742
1743    #[test]
1744    fn build_with_query() {
1745        use sea_orm::{DbBackend, Statement};
1746        use sea_query::{
1747            ColumnRef, CommonTableExpression, Cycle, Expr, ExprTrait, JoinType, SelectStatement,
1748            UnionType, WithClause,
1749        };
1750
1751        let base_query = SelectStatement::new()
1752            .column("id")
1753            .expr(1i32)
1754            .column("next")
1755            .column("value")
1756            .from("table")
1757            .to_owned();
1758
1759        let cte_referencing = SelectStatement::new()
1760            .column("id")
1761            .expr(Expr::col("depth").add(1i32))
1762            .column("next")
1763            .column("value")
1764            .from("table")
1765            .join(
1766                JoinType::InnerJoin,
1767                "cte_traversal",
1768                Expr::col(("cte_traversal", "next")).equals(("table", "id")),
1769            )
1770            .to_owned();
1771
1772        let common_table_expression = CommonTableExpression::new()
1773            .query(
1774                base_query
1775                    .clone()
1776                    .union(UnionType::All, cte_referencing)
1777                    .to_owned(),
1778            )
1779            .columns(["id", "depth", "next", "value"])
1780            .table_name("cte_traversal")
1781            .to_owned();
1782
1783        let select = SelectStatement::new()
1784            .column(ColumnRef::Asterisk(None))
1785            .from("cte_traversal")
1786            .to_owned();
1787
1788        let with_clause = WithClause::new()
1789            .recursive(true)
1790            .cte(common_table_expression)
1791            .cycle(Cycle::new_from_expr_set_using(
1792                Expr::column("id"),
1793                "looped",
1794                "traversal_path",
1795            ))
1796            .to_owned();
1797
1798        let with_query = select.with(with_clause).to_owned();
1799
1800        assert_eq!(
1801            DbBackend::MySql.build(&with_query),
1802            Statement::from_sql_and_values(
1803                DbBackend::MySql,
1804                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`",
1805                [1.into(), 1.into()]
1806            )
1807        );
1808    }
1809
1810    #[test]
1811    fn column_names_from_query_result() {
1812        let mut values = BTreeMap::new();
1813        values.insert("id".to_string(), Value::Int(Some(1)));
1814        values.insert("name".to_string(), Value::String(Some("Abc".to_owned())));
1815        let query_result = QueryResult {
1816            row: QueryResultRow::Mock(MockRow { values }),
1817        };
1818        assert_eq!(
1819            query_result.column_names(),
1820            vec!["id".to_owned(), "name".to_owned()]
1821        );
1822    }
1823
1824    #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1825    struct Component {
1826        base_price: i32,
1827        component_type: String,
1828    }
1829
1830    #[test]
1831    fn json_deserialize_to_btreemap() {
1832        let json_value = serde_json::json!({
1833            "engine": {
1834                "base_price": 100,
1835                "component_type": "metal"
1836            }
1837        });
1838
1839        let values = BTreeMap::from([(
1840            "components".to_string(),
1841            Value::Json(Some(Box::new(json_value))),
1842        )]);
1843
1844        let row = QueryResultRow::Mock(MockRow { values });
1845
1846        let result: BTreeMap<String, Component> =
1847            TryGetable::try_get_by(&QueryResult { row }, "components").unwrap();
1848
1849        assert_eq!(
1850            result.get("engine"),
1851            Some(&Component {
1852                base_price: 100,
1853                component_type: "metal".to_owned(),
1854            })
1855        );
1856    }
1857
1858    #[test]
1859    fn json_deserialize_to_hashmap() {
1860        let json_value = serde_json::json!({
1861            "engine": {
1862                "base_price": 100,
1863                "component_type": "metal"
1864            }
1865        });
1866
1867        let values = BTreeMap::from([(
1868            "components".to_string(),
1869            Value::Json(Some(Box::new(json_value))),
1870        )]);
1871
1872        let row = QueryResultRow::Mock(MockRow { values });
1873
1874        let result: HashMap<String, Component> =
1875            TryGetable::try_get_by(&QueryResult { row }, "components").unwrap();
1876
1877        assert_eq!(
1878            result.get("engine"),
1879            Some(&Component {
1880                base_price: 100,
1881                component_type: "metal".to_owned(),
1882            })
1883        );
1884    }
1885}