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
807impl TryGetable for u32 {
808    #[allow(unused_variables)]
809    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
810        match &res.row {
811            #[cfg(feature = "sqlx-mysql")]
812            QueryResultRow::SqlxMySql(row) => row
813                .try_get::<Option<u32>, _>(idx.as_sqlx_mysql_index())
814                .map_err(|e| sqlx_error_to_query_err(e).into())
815                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
816            #[cfg(feature = "sqlx-postgres")]
817            QueryResultRow::SqlxPostgres(row) => {
818                use sqlx::postgres::types::Oid;
819                // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
820                // Instead, `u32` was wrapped by a `sqlx::Oid`.
821                row.try_get::<Option<Oid>, _>(idx.as_sqlx_postgres_index())
822                    .map_err(|e| sqlx_error_to_query_err(e).into())
823                    .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
824                    .map(|oid| oid.0)
825            }
826            #[cfg(feature = "sqlx-sqlite")]
827            QueryResultRow::SqlxSqlite(row) => row
828                .try_get::<Option<u32>, _>(idx.as_sqlx_sqlite_index())
829                .map_err(|e| sqlx_error_to_query_err(e).into())
830                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
831            #[cfg(feature = "mock")]
832            #[allow(unused_variables)]
833            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
834                debug_print!("{:#?}", e.to_string());
835                err_null_idx_col(idx)
836            }),
837            #[cfg(feature = "proxy")]
838            #[allow(unused_variables)]
839            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
840                debug_print!("{:#?}", e.to_string());
841                err_null_idx_col(idx)
842            }),
843            #[allow(unreachable_patterns)]
844            _ => unreachable!(),
845        }
846    }
847}
848
849impl TryGetable for String {
850    #[allow(unused_variables)]
851    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
852        match &res.row {
853            #[cfg(feature = "sqlx-mysql")]
854            QueryResultRow::SqlxMySql(row) => row
855                .try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
856                .map_err(|e| sqlx_error_to_query_err(e).into())
857                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
858                .map(|bytes| {
859                    String::from_utf8(bytes).map_err(|e| {
860                        DbErr::TryIntoErr {
861                            from: "Vec<u8>",
862                            into: "String",
863                            source: Box::new(e),
864                        }
865                        .into()
866                    })
867                })?,
868            #[cfg(feature = "sqlx-postgres")]
869            QueryResultRow::SqlxPostgres(row) => row
870                .try_get::<Option<String>, _>(idx.as_sqlx_postgres_index())
871                .map_err(|e| sqlx_error_to_query_err(e).into())
872                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
873            #[cfg(feature = "sqlx-sqlite")]
874            QueryResultRow::SqlxSqlite(row) => row
875                .try_get::<Option<String>, _>(idx.as_sqlx_sqlite_index())
876                .map_err(|e| sqlx_error_to_query_err(e).into())
877                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
878            #[cfg(feature = "mock")]
879            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
880                debug_print!("{:#?}", e.to_string());
881                err_null_idx_col(idx)
882            }),
883            #[cfg(feature = "proxy")]
884            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
885                debug_print!("{:#?}", e.to_string());
886                err_null_idx_col(idx)
887            }),
888            #[allow(unreachable_patterns)]
889            _ => unreachable!(),
890        }
891    }
892}
893
894#[allow(dead_code)]
895fn err_null_idx_col<I: ColIdx>(idx: I) -> TryGetError {
896    TryGetError::Null(format!("{idx:?}"))
897}
898
899#[cfg(feature = "postgres-array")]
900mod postgres_array {
901    use super::*;
902
903    #[allow(unused_macros)]
904    macro_rules! try_getable_postgres_array {
905        ( $type: ty ) => {
906            #[allow(unused_variables)]
907            impl TryGetable for Vec<$type> {
908                fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
909                    match &res.row {
910                        #[cfg(feature = "sqlx-mysql")]
911                        QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
912                            "{} unsupported by sqlx-mysql",
913                            stringify!($type)
914                        ))
915                        .into()),
916                        #[cfg(feature = "sqlx-postgres")]
917                        QueryResultRow::SqlxPostgres(row) => row
918                            .try_get::<Option<Vec<$type>>, _>(idx.as_sqlx_postgres_index())
919                            .map_err(|e| sqlx_error_to_query_err(e).into())
920                            .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
921                        #[cfg(feature = "sqlx-sqlite")]
922                        QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
923                            "{} unsupported by sqlx-sqlite",
924                            stringify!($type)
925                        ))
926                        .into()),
927                        #[cfg(feature = "mock")]
928                        #[allow(unused_variables)]
929                        QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
930                            debug_print!("{:#?}", e.to_string());
931                            err_null_idx_col(idx)
932                        }),
933                        #[cfg(feature = "proxy")]
934                        #[allow(unused_variables)]
935                        QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
936                            debug_print!("{:#?}", e.to_string());
937                            err_null_idx_col(idx)
938                        }),
939                        #[allow(unreachable_patterns)]
940                        _ => unreachable!(),
941                    }
942                }
943            }
944        };
945    }
946
947    try_getable_postgres_array!(bool);
948    try_getable_postgres_array!(i8);
949    try_getable_postgres_array!(i16);
950    try_getable_postgres_array!(i32);
951    try_getable_postgres_array!(i64);
952    try_getable_postgres_array!(f32);
953    try_getable_postgres_array!(f64);
954    try_getable_postgres_array!(String);
955    try_getable_postgres_array!(Vec<u8>);
956
957    #[cfg(feature = "with-json")]
958    try_getable_postgres_array!(serde_json::Value);
959
960    #[cfg(feature = "with-chrono")]
961    try_getable_postgres_array!(chrono::NaiveDate);
962
963    #[cfg(feature = "with-chrono")]
964    try_getable_postgres_array!(chrono::NaiveTime);
965
966    #[cfg(feature = "with-chrono")]
967    try_getable_postgres_array!(chrono::NaiveDateTime);
968
969    #[cfg(feature = "with-chrono")]
970    try_getable_postgres_array!(chrono::DateTime<chrono::FixedOffset>);
971
972    #[cfg(feature = "with-chrono")]
973    try_getable_postgres_array!(chrono::DateTime<chrono::Utc>);
974
975    #[cfg(feature = "with-chrono")]
976    try_getable_postgres_array!(chrono::DateTime<chrono::Local>);
977
978    #[cfg(feature = "with-time")]
979    try_getable_postgres_array!(time::Date);
980
981    #[cfg(feature = "with-time")]
982    try_getable_postgres_array!(time::Time);
983
984    #[cfg(feature = "with-time")]
985    try_getable_postgres_array!(time::PrimitiveDateTime);
986
987    #[cfg(feature = "with-time")]
988    try_getable_postgres_array!(time::OffsetDateTime);
989
990    #[cfg(feature = "with-rust_decimal")]
991    try_getable_postgres_array!(rust_decimal::Decimal);
992
993    #[cfg(feature = "with-bigdecimal")]
994    try_getable_postgres_array!(bigdecimal::BigDecimal);
995
996    #[cfg(feature = "with-ipnetwork")]
997    try_getable_postgres_array!(ipnetwork::IpNetwork);
998
999    #[allow(unused_macros)]
1000    macro_rules! try_getable_postgres_array_uuid {
1001        ( $type: ty, $conversion_fn: expr ) => {
1002            #[allow(unused_variables, unreachable_code)]
1003            impl TryGetable for Vec<$type> {
1004                fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1005                    let res: Result<Vec<uuid::Uuid>, TryGetError> = match &res.row {
1006                        #[cfg(feature = "sqlx-mysql")]
1007                        QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
1008                            "{} unsupported by sqlx-mysql",
1009                            stringify!($type)
1010                        ))
1011                        .into()),
1012                        #[cfg(feature = "sqlx-postgres")]
1013                        QueryResultRow::SqlxPostgres(row) => row
1014                            .try_get::<Option<Vec<uuid::Uuid>>, _>(idx.as_sqlx_postgres_index())
1015                            .map_err(|e| sqlx_error_to_query_err(e).into())
1016                            .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1017                        #[cfg(feature = "sqlx-sqlite")]
1018                        QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1019                            "{} unsupported by sqlx-sqlite",
1020                            stringify!($type)
1021                        ))
1022                        .into()),
1023                        #[cfg(feature = "mock")]
1024                        QueryResultRow::Mock(row) => {
1025                            row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1026                                debug_print!("{:#?}", e.to_string());
1027                                err_null_idx_col(idx)
1028                            })
1029                        }
1030                        #[cfg(feature = "proxy")]
1031                        QueryResultRow::Proxy(row) => {
1032                            row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1033                                debug_print!("{:#?}", e.to_string());
1034                                err_null_idx_col(idx)
1035                            })
1036                        }
1037                        #[allow(unreachable_patterns)]
1038                        _ => unreachable!(),
1039                    };
1040                    res.map(|vec| vec.into_iter().map($conversion_fn).collect())
1041                }
1042            }
1043        };
1044    }
1045
1046    #[cfg(feature = "with-uuid")]
1047    try_getable_postgres_array_uuid!(uuid::Uuid, Into::into);
1048
1049    #[cfg(feature = "with-uuid")]
1050    try_getable_postgres_array_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
1051
1052    #[cfg(feature = "with-uuid")]
1053    try_getable_postgres_array_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
1054
1055    #[cfg(feature = "with-uuid")]
1056    try_getable_postgres_array_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
1057
1058    #[cfg(feature = "with-uuid")]
1059    try_getable_postgres_array_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
1060
1061    impl TryGetable for Vec<u32> {
1062        #[allow(unused_variables)]
1063        fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1064            match &res.row {
1065                #[cfg(feature = "sqlx-mysql")]
1066                QueryResultRow::SqlxMySql(_) => {
1067                    Err(type_err(format!("{} unsupported by sqlx-mysql", stringify!($type))).into())
1068                }
1069                #[cfg(feature = "sqlx-postgres")]
1070                QueryResultRow::SqlxPostgres(row) => {
1071                    use sqlx::postgres::types::Oid;
1072                    // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
1073                    // Instead, `u32` was wrapped by a `sqlx::Oid`.
1074                    row.try_get::<Option<Vec<Oid>>, _>(idx.as_sqlx_postgres_index())
1075                        .map_err(|e| sqlx_error_to_query_err(e).into())
1076                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
1077                        .map(|oids| oids.into_iter().map(|oid| oid.0).collect())
1078                }
1079                #[cfg(feature = "sqlx-sqlite")]
1080                QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1081                    "{} unsupported by sqlx-sqlite",
1082                    stringify!($type)
1083                ))
1084                .into()),
1085                #[cfg(feature = "mock")]
1086                #[allow(unused_variables)]
1087                QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1088                    debug_print!("{:#?}", e.to_string());
1089                    err_null_idx_col(idx)
1090                }),
1091                #[cfg(feature = "proxy")]
1092                #[allow(unused_variables)]
1093                QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
1094                    debug_print!("{:#?}", e.to_string());
1095                    err_null_idx_col(idx)
1096                }),
1097                #[allow(unreachable_patterns)]
1098                _ => unreachable!(),
1099            }
1100        }
1101    }
1102}
1103
1104#[cfg(feature = "postgres-vector")]
1105impl TryGetable for pgvector::Vector {
1106    #[allow(unused_variables)]
1107    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1108        match &res.row {
1109            #[cfg(feature = "sqlx-mysql")]
1110            QueryResultRow::SqlxMySql(_) => {
1111                Err(type_err("Vector unsupported by sqlx-mysql").into())
1112            }
1113            #[cfg(feature = "sqlx-postgres")]
1114            QueryResultRow::SqlxPostgres(row) => row
1115                .try_get::<Option<pgvector::Vector>, _>(idx.as_sqlx_postgres_index())
1116                .map_err(|e| sqlx_error_to_query_err(e).into())
1117                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1118            #[cfg(feature = "sqlx-sqlite")]
1119            QueryResultRow::SqlxSqlite(_) => {
1120                Err(type_err("Vector unsupported by sqlx-sqlite").into())
1121            }
1122            #[cfg(feature = "mock")]
1123            QueryResultRow::Mock(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1124                debug_print!("{:#?}", e.to_string());
1125                err_null_idx_col(idx)
1126            }),
1127            #[cfg(feature = "proxy")]
1128            QueryResultRow::Proxy(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1129                debug_print!("{:#?}", e.to_string());
1130                err_null_idx_col(idx)
1131            }),
1132            #[allow(unreachable_patterns)]
1133            _ => unreachable!(),
1134        }
1135    }
1136}
1137
1138// TryGetableMany //
1139
1140/// An interface to get a tuple value from the query result
1141pub trait TryGetableMany: Sized {
1142    /// Get a tuple value from the query result with prefixed column name
1143    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>;
1144
1145    /// Get a tuple value from the query result based on the order in the select expressions
1146    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError>;
1147
1148    /// ```
1149    /// # use sea_orm::{error::*, tests_cfg::*, *};
1150    /// #
1151    /// # #[smol_potat::main]
1152    /// # #[cfg(all(feature = "mock", feature = "macros"))]
1153    /// # pub async fn main() -> Result<(), DbErr> {
1154    /// #
1155    /// # let db = MockDatabase::new(DbBackend::Postgres)
1156    /// #     .append_query_results([[
1157    /// #         maplit::btreemap! {
1158    /// #             "name" => Into::<Value>::into("Chocolate Forest"),
1159    /// #             "num_of_cakes" => Into::<Value>::into(1),
1160    /// #         },
1161    /// #         maplit::btreemap! {
1162    /// #             "name" => Into::<Value>::into("New York Cheese"),
1163    /// #             "num_of_cakes" => Into::<Value>::into(1),
1164    /// #         },
1165    /// #     ]])
1166    /// #     .into_connection();
1167    /// #
1168    /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DeriveIden, EnumIter, TryGetableMany};
1169    ///
1170    /// #[derive(EnumIter, DeriveIden)]
1171    /// enum ResultCol {
1172    ///     Name,
1173    ///     NumOfCakes,
1174    /// }
1175    ///
1176    /// let res: Vec<(String, i32)> =
1177    ///     <(String, i32)>::find_by_statement::<ResultCol>(Statement::from_sql_and_values(
1178    ///         DbBackend::Postgres,
1179    ///         r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
1180    ///         [],
1181    ///     ))
1182    ///     .all(&db)
1183    ///     .await?;
1184    ///
1185    /// assert_eq!(
1186    ///     res,
1187    ///     [
1188    ///         ("Chocolate Forest".to_owned(), 1),
1189    ///         ("New York Cheese".to_owned(), 1),
1190    ///     ]
1191    /// );
1192    ///
1193    /// assert_eq!(
1194    ///     db.into_transaction_log(),
1195    ///     [Transaction::from_sql_and_values(
1196    ///         DbBackend::Postgres,
1197    ///         r#"SELECT "cake"."name", count("cake"."id") AS "num_of_cakes" FROM "cake""#,
1198    ///         []
1199    ///     ),]
1200    /// );
1201    /// #
1202    /// # Ok(())
1203    /// # }
1204    /// ```
1205    fn find_by_statement<C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<Self, C>>
1206    where
1207        C: strum::IntoEnumIterator + sea_query::Iden,
1208    {
1209        SelectorRaw::<SelectGetableValue<Self, C>>::with_columns(stmt)
1210    }
1211}
1212
1213impl<T> TryGetableMany for T
1214where
1215    T: TryGetable,
1216{
1217    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1218        try_get_many_with_slice_len_of(1, cols)?;
1219        T::try_get(res, pre, &cols[0])
1220    }
1221
1222    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1223        T::try_get_by_index(res, 0)
1224    }
1225}
1226
1227impl<T> TryGetableMany for (T,)
1228where
1229    T: TryGetableMany,
1230{
1231    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1232        T::try_get_many(res, pre, cols).map(|r| (r,))
1233    }
1234
1235    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1236        T::try_get_many_by_index(res).map(|r| (r,))
1237    }
1238}
1239
1240macro_rules! impl_try_get_many {
1241    ( $LEN:expr, $($T:ident : $N:expr),+ $(,)? ) => {
1242        impl< $($T),+ > TryGetableMany for ( $($T),+ )
1243        where
1244            $($T: TryGetable),+
1245        {
1246            fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1247                try_get_many_with_slice_len_of($LEN, cols)?;
1248                Ok((
1249                    $($T::try_get(res, pre, &cols[$N])?),+
1250                ))
1251            }
1252
1253            fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1254                Ok((
1255                    $($T::try_get_by_index(res, $N)?),+
1256                ))
1257            }
1258        }
1259    };
1260}
1261
1262#[rustfmt::skip]
1263mod impl_try_get_many {
1264    use super::*;
1265
1266    impl_try_get_many!( 2, T0:0, T1:1);
1267    impl_try_get_many!( 3, T0:0, T1:1, T2:2);
1268    impl_try_get_many!( 4, T0:0, T1:1, T2:2, T3:3);
1269    impl_try_get_many!( 5, T0:0, T1:1, T2:2, T3:3, T4:4);
1270    impl_try_get_many!( 6, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
1271    impl_try_get_many!( 7, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
1272    impl_try_get_many!( 8, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
1273    impl_try_get_many!( 9, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
1274    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);
1275    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);
1276    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);
1277}
1278
1279fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
1280    if cols.len() < len {
1281        Err(type_err(format!(
1282            "Expect {} column names supplied but got slice of length {}",
1283            len,
1284            cols.len()
1285        ))
1286        .into())
1287    } else {
1288        Ok(())
1289    }
1290}
1291
1292/// An interface to get an array of values from the query result.
1293/// A type can only implement `ActiveEnum` or `TryGetableFromJson`, but not both.
1294/// A blanket impl is provided for `TryGetableFromJson`, while the impl for `ActiveEnum`
1295/// is provided by the `DeriveActiveEnum` macro. So as an end user you won't normally
1296/// touch this trait.
1297pub trait TryGetableArray: Sized {
1298    /// Just a delegate
1299    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
1300}
1301
1302impl<T> TryGetable for Vec<T>
1303where
1304    T: TryGetableArray,
1305{
1306    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1307        T::try_get_by(res, index)
1308    }
1309}
1310
1311// TryGetableFromJson //
1312
1313/// An interface to get a JSON from the query result
1314#[cfg(feature = "with-json")]
1315pub trait TryGetableFromJson: Sized
1316where
1317    for<'de> Self: serde::Deserialize<'de>,
1318{
1319    /// Get a JSON from the query result with prefixed column name
1320    #[allow(unused_variables, unreachable_code)]
1321    fn try_get_from_json<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1322        match &res.row {
1323            #[cfg(feature = "sqlx-mysql")]
1324            QueryResultRow::SqlxMySql(row) => row
1325                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
1326                .map_err(|e| sqlx_error_to_query_err(e).into())
1327                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1328            #[cfg(feature = "sqlx-postgres")]
1329            QueryResultRow::SqlxPostgres(row) => row
1330                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
1331                .map_err(|e| sqlx_error_to_query_err(e).into())
1332                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1333            #[cfg(feature = "sqlx-sqlite")]
1334            QueryResultRow::SqlxSqlite(row) => row
1335                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
1336                .map_err(|e| sqlx_error_to_query_err(e).into())
1337                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1338            #[cfg(feature = "mock")]
1339            QueryResultRow::Mock(row) => row
1340                .try_get::<serde_json::Value, I>(idx)
1341                .map_err(|e| {
1342                    debug_print!("{:#?}", e.to_string());
1343                    err_null_idx_col(idx)
1344                })
1345                .and_then(|json| {
1346                    serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1347                }),
1348            #[cfg(feature = "proxy")]
1349            QueryResultRow::Proxy(row) => row
1350                .try_get::<serde_json::Value, I>(idx)
1351                .map_err(|e| {
1352                    debug_print!("{:#?}", e.to_string());
1353                    err_null_idx_col(idx)
1354                })
1355                .and_then(|json| {
1356                    serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1357                }),
1358            #[allow(unreachable_patterns)]
1359            _ => unreachable!(),
1360        }
1361    }
1362
1363    /// Get a Vec<Self> from an Array of Json
1364    fn from_json_vec(value: serde_json::Value) -> Result<Vec<Self>, TryGetError> {
1365        match value {
1366            serde_json::Value::Array(values) => {
1367                let mut res = Vec::new();
1368                for item in values {
1369                    res.push(serde_json::from_value(item).map_err(crate::error::json_err)?);
1370                }
1371                Ok(res)
1372            }
1373            _ => Err(TryGetError::DbErr(DbErr::Json(
1374                "Value is not an Array".to_owned(),
1375            ))),
1376        }
1377    }
1378}
1379
1380#[cfg(feature = "with-json")]
1381impl<T> TryGetable for T
1382where
1383    T: TryGetableFromJson,
1384{
1385    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1386        T::try_get_from_json(res, index)
1387    }
1388}
1389
1390#[cfg(feature = "with-json")]
1391impl<T> TryGetableArray for T
1392where
1393    T: TryGetableFromJson,
1394{
1395    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<T>, TryGetError> {
1396        T::from_json_vec(serde_json::Value::try_get_by(res, index)?)
1397    }
1398}
1399
1400// TryFromU64 //
1401/// Try to convert a type to a u64
1402pub trait TryFromU64: Sized {
1403    /// The method to convert the type to a u64
1404    fn try_from_u64(n: u64) -> Result<Self, DbErr>;
1405}
1406
1407macro_rules! try_from_u64_err {
1408    ( $type: ty ) => {
1409        impl TryFromU64 for $type {
1410            fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1411                Err(DbErr::ConvertFromU64(stringify!($type)))
1412            }
1413        }
1414    };
1415
1416    ( $($gen_type: ident),* ) => {
1417        impl<$( $gen_type, )*> TryFromU64 for ($( $gen_type, )*)
1418        where
1419            $( $gen_type: TryFromU64, )*
1420        {
1421            fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1422                Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*)))
1423            }
1424        }
1425    };
1426}
1427
1428#[rustfmt::skip]
1429mod try_from_u64_err {
1430    use super::*;
1431
1432    try_from_u64_err!(T0, T1);
1433    try_from_u64_err!(T0, T1, T2);
1434    try_from_u64_err!(T0, T1, T2, T3);
1435    try_from_u64_err!(T0, T1, T2, T3, T4);
1436    try_from_u64_err!(T0, T1, T2, T3, T4, T5);
1437    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6);
1438    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7);
1439    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
1440    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
1441    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
1442    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
1443}
1444
1445macro_rules! try_from_u64_numeric {
1446    ( $type: ty ) => {
1447        impl TryFromU64 for $type {
1448            fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1449                use std::convert::TryInto;
1450                n.try_into().map_err(|e| DbErr::TryIntoErr {
1451                    from: stringify!(u64),
1452                    into: stringify!($type),
1453                    source: Box::new(e),
1454                })
1455            }
1456        }
1457    };
1458}
1459
1460try_from_u64_numeric!(i8);
1461try_from_u64_numeric!(i16);
1462try_from_u64_numeric!(i32);
1463try_from_u64_numeric!(i64);
1464try_from_u64_numeric!(u8);
1465try_from_u64_numeric!(u16);
1466try_from_u64_numeric!(u32);
1467try_from_u64_numeric!(u64);
1468
1469macro_rules! try_from_u64_string {
1470    ( $type: ty ) => {
1471        impl TryFromU64 for $type {
1472            fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1473                Ok(n.to_string())
1474            }
1475        }
1476    };
1477}
1478
1479try_from_u64_string!(String);
1480
1481try_from_u64_err!(bool);
1482try_from_u64_err!(f32);
1483try_from_u64_err!(f64);
1484try_from_u64_err!(Vec<u8>);
1485
1486#[cfg(feature = "with-json")]
1487try_from_u64_err!(serde_json::Value);
1488
1489#[cfg(feature = "with-chrono")]
1490try_from_u64_err!(chrono::NaiveDate);
1491
1492#[cfg(feature = "with-chrono")]
1493try_from_u64_err!(chrono::NaiveTime);
1494
1495#[cfg(feature = "with-chrono")]
1496try_from_u64_err!(chrono::NaiveDateTime);
1497
1498#[cfg(feature = "with-chrono")]
1499try_from_u64_err!(chrono::DateTime<chrono::FixedOffset>);
1500
1501#[cfg(feature = "with-chrono")]
1502try_from_u64_err!(chrono::DateTime<chrono::Utc>);
1503
1504#[cfg(feature = "with-chrono")]
1505try_from_u64_err!(chrono::DateTime<chrono::Local>);
1506
1507#[cfg(feature = "with-time")]
1508try_from_u64_err!(time::Date);
1509
1510#[cfg(feature = "with-time")]
1511try_from_u64_err!(time::Time);
1512
1513#[cfg(feature = "with-time")]
1514try_from_u64_err!(time::PrimitiveDateTime);
1515
1516#[cfg(feature = "with-time")]
1517try_from_u64_err!(time::OffsetDateTime);
1518
1519#[cfg(feature = "with-rust_decimal")]
1520try_from_u64_err!(rust_decimal::Decimal);
1521
1522#[cfg(feature = "with-uuid")]
1523try_from_u64_err!(uuid::Uuid);
1524
1525#[cfg(feature = "with-uuid")]
1526try_from_u64_err!(uuid::fmt::Simple);
1527
1528#[cfg(feature = "with-uuid")]
1529try_from_u64_err!(uuid::fmt::Hyphenated);
1530
1531#[cfg(feature = "with-uuid")]
1532try_from_u64_err!(uuid::fmt::Urn);
1533
1534#[cfg(feature = "with-uuid")]
1535try_from_u64_err!(uuid::fmt::Braced);
1536
1537#[cfg(feature = "with-ipnetwork")]
1538try_from_u64_err!(ipnetwork::IpNetwork);
1539
1540#[cfg(test)]
1541mod tests {
1542    use super::*;
1543    use crate::RuntimeErr;
1544    use sea_query::Value;
1545    use std::collections::BTreeMap;
1546
1547    #[test]
1548    fn from_try_get_error() {
1549        // TryGetError::DbErr
1550        let try_get_error = TryGetError::DbErr(DbErr::Query(RuntimeErr::Internal(
1551            "expected error message".to_owned(),
1552        )));
1553        assert_eq!(
1554            DbErr::from(try_get_error),
1555            DbErr::Query(RuntimeErr::Internal("expected error message".to_owned()))
1556        );
1557
1558        // TryGetError::Null
1559        let try_get_error = TryGetError::Null("column".to_owned());
1560        let expected = "A null value was encountered while decoding column".to_owned();
1561        assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected));
1562    }
1563
1564    #[test]
1565    fn build_with_query() {
1566        use sea_orm::{DbBackend, Statement};
1567        use sea_query::*;
1568
1569        let base_query = SelectStatement::new()
1570            .column("id")
1571            .expr(1i32)
1572            .column("next")
1573            .column("value")
1574            .from("table")
1575            .to_owned();
1576
1577        let cte_referencing = SelectStatement::new()
1578            .column("id")
1579            .expr(Expr::col("depth").add(1i32))
1580            .column("next")
1581            .column("value")
1582            .from("table")
1583            .join(
1584                JoinType::InnerJoin,
1585                "cte_traversal",
1586                Expr::col(("cte_traversal", "next")).equals(("table", "id")),
1587            )
1588            .to_owned();
1589
1590        let common_table_expression = CommonTableExpression::new()
1591            .query(
1592                base_query
1593                    .clone()
1594                    .union(UnionType::All, cte_referencing)
1595                    .to_owned(),
1596            )
1597            .columns(["id", "depth", "next", "value"])
1598            .table_name("cte_traversal")
1599            .to_owned();
1600
1601        let select = SelectStatement::new()
1602            .column(ColumnRef::Asterisk)
1603            .from("cte_traversal")
1604            .to_owned();
1605
1606        let with_clause = WithClause::new()
1607            .recursive(true)
1608            .cte(common_table_expression)
1609            .cycle(Cycle::new_from_expr_set_using(
1610                SimpleExpr::Column(ColumnRef::Column("id".into_iden())),
1611                "looped",
1612                "traversal_path",
1613            ))
1614            .to_owned();
1615
1616        let with_query = select.with(with_clause).to_owned();
1617
1618        assert_eq!(
1619            DbBackend::MySql.build(&with_query),
1620            Statement::from_sql_and_values(
1621                DbBackend::MySql,
1622                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`"#,
1623                [1.into(), 1.into()]
1624            )
1625        );
1626    }
1627
1628    #[test]
1629    fn column_names_from_query_result() {
1630        let mut values = BTreeMap::new();
1631        values.insert("id".to_string(), Value::Int(Some(1)));
1632        values.insert(
1633            "name".to_string(),
1634            Value::String(Some(Box::new("Abc".to_owned()))),
1635        );
1636        let query_result = QueryResult {
1637            row: QueryResultRow::Mock(crate::MockRow { values }),
1638        };
1639        assert_eq!(
1640            query_result.column_names(),
1641            vec!["id".to_owned(), "name".to_owned()]
1642        );
1643    }
1644}