Skip to main content

sea_orm/executor/
query.rs

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