sea_orm/executor/
query.rs

1pub use crate::error::TryGetError;
2use crate::{
3    SelectGetableValue, SelectorRaw, Statement,
4    error::{DbErr, type_err},
5};
6use std::{fmt::Debug, marker::PhantomData, sync::Arc};
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 Debug for QueryResultRow {
230    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::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(err)))) => {
257                use std::ops::Deref;
258                match err.deref() {
259                    sqlx::Error::ColumnNotFound(_) => Ok(None),
260                    _ => Err(TryGetError::DbErr(DbErr::Query(
261                        crate::RuntimeErr::SqlxError(err),
262                    ))),
263                }
264            }
265            Err(e) => Err(e),
266        }
267    }
268}
269
270/// Column Index, used by [`TryGetable`]. Implemented for `&str` and `usize`
271pub trait ColIdx: Debug + Copy {
272    #[cfg(feature = "sqlx-mysql")]
273    /// Type surrogate
274    type SqlxMySqlIndex: sqlx::ColumnIndex<sqlx::mysql::MySqlRow>;
275    #[cfg(feature = "sqlx-postgres")]
276    /// Type surrogate
277    type SqlxPostgresIndex: sqlx::ColumnIndex<sqlx::postgres::PgRow>;
278    #[cfg(feature = "sqlx-sqlite")]
279    /// Type surrogate
280    type SqlxSqliteIndex: sqlx::ColumnIndex<sqlx::sqlite::SqliteRow>;
281
282    #[cfg(feature = "sqlx-mysql")]
283    /// Basically a no-op; only to satisfy trait bounds
284    fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex;
285    #[cfg(feature = "sqlx-postgres")]
286    /// Basically a no-op; only to satisfy trait bounds
287    fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex;
288    #[cfg(feature = "sqlx-sqlite")]
289    /// Basically a no-op; only to satisfy trait bounds
290    fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex;
291
292    /// Self must be `&str`, return `None` otherwise
293    fn as_str(&self) -> Option<&str>;
294    /// Self must be `usize`, return `None` otherwise
295    fn as_usize(&self) -> Option<&usize>;
296}
297
298impl ColIdx for &str {
299    #[cfg(feature = "sqlx-mysql")]
300    type SqlxMySqlIndex = Self;
301    #[cfg(feature = "sqlx-postgres")]
302    type SqlxPostgresIndex = Self;
303    #[cfg(feature = "sqlx-sqlite")]
304    type SqlxSqliteIndex = Self;
305
306    #[cfg(feature = "sqlx-mysql")]
307    #[inline]
308    fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
309        self
310    }
311    #[cfg(feature = "sqlx-postgres")]
312    #[inline]
313    fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
314        self
315    }
316    #[cfg(feature = "sqlx-sqlite")]
317    #[inline]
318    fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
319        self
320    }
321
322    #[inline]
323    fn as_str(&self) -> Option<&str> {
324        Some(self)
325    }
326    #[inline]
327    fn as_usize(&self) -> Option<&usize> {
328        None
329    }
330}
331
332impl ColIdx for usize {
333    #[cfg(feature = "sqlx-mysql")]
334    type SqlxMySqlIndex = Self;
335    #[cfg(feature = "sqlx-postgres")]
336    type SqlxPostgresIndex = Self;
337    #[cfg(feature = "sqlx-sqlite")]
338    type SqlxSqliteIndex = Self;
339
340    #[cfg(feature = "sqlx-mysql")]
341    #[inline]
342    fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
343        *self
344    }
345    #[cfg(feature = "sqlx-postgres")]
346    #[inline]
347    fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
348        *self
349    }
350    #[cfg(feature = "sqlx-sqlite")]
351    #[inline]
352    fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
353        *self
354    }
355
356    #[inline]
357    fn as_str(&self) -> Option<&str> {
358        None
359    }
360    #[inline]
361    fn as_usize(&self) -> Option<&usize> {
362        Some(self)
363    }
364}
365
366macro_rules! try_getable_all {
367    ( $type: ty ) => {
368        impl TryGetable for $type {
369            #[allow(unused_variables)]
370            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
371                match &res.row {
372                    #[cfg(feature = "sqlx-mysql")]
373                    QueryResultRow::SqlxMySql(row) => row
374                        .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
375                        .map_err(|e| sqlx_error_to_query_err(e).into())
376                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
377                    #[cfg(feature = "sqlx-postgres")]
378                    QueryResultRow::SqlxPostgres(row) => row
379                        .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
380                        .map_err(|e| sqlx_error_to_query_err(e).into())
381                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
382                    #[cfg(feature = "sqlx-sqlite")]
383                    QueryResultRow::SqlxSqlite(row) => row
384                        .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
385                        .map_err(|e| sqlx_error_to_query_err(e).into())
386                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
387                    #[cfg(feature = "mock")]
388                    QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
389                        debug_print!("{:#?}", e.to_string());
390                        err_null_idx_col(idx)
391                    }),
392                    #[cfg(feature = "proxy")]
393                    QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
394                        debug_print!("{:#?}", e.to_string());
395                        err_null_idx_col(idx)
396                    }),
397                    #[allow(unreachable_patterns)]
398                    _ => unreachable!(),
399                }
400            }
401        }
402    };
403}
404
405macro_rules! try_getable_unsigned {
406    ( $type: ty ) => {
407        impl TryGetable for $type {
408            #[allow(unused_variables)]
409            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
410                match &res.row {
411                    #[cfg(feature = "sqlx-mysql")]
412                    QueryResultRow::SqlxMySql(row) => row
413                        .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
414                        .map_err(|e| sqlx_error_to_query_err(e).into())
415                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
416                    #[cfg(feature = "sqlx-postgres")]
417                    QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
418                        "{} unsupported by sqlx-postgres",
419                        stringify!($type)
420                    ))
421                    .into()),
422                    #[cfg(feature = "sqlx-sqlite")]
423                    QueryResultRow::SqlxSqlite(row) => row
424                        .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
425                        .map_err(|e| sqlx_error_to_query_err(e).into())
426                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
427                    #[cfg(feature = "mock")]
428                    QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
429                        debug_print!("{:#?}", e.to_string());
430                        err_null_idx_col(idx)
431                    }),
432                    #[cfg(feature = "proxy")]
433                    QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
434                        debug_print!("{:#?}", e.to_string());
435                        err_null_idx_col(idx)
436                    }),
437                    #[allow(unreachable_patterns)]
438                    _ => unreachable!(),
439                }
440            }
441        }
442    };
443}
444
445macro_rules! try_getable_mysql {
446    ( $type: ty ) => {
447        impl TryGetable for $type {
448            #[allow(unused_variables)]
449            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
450                match &res.row {
451                    #[cfg(feature = "sqlx-mysql")]
452                    QueryResultRow::SqlxMySql(row) => row
453                        .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
454                        .map_err(|e| sqlx_error_to_query_err(e).into())
455                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
456                    #[cfg(feature = "sqlx-postgres")]
457                    QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
458                        "{} unsupported by sqlx-postgres",
459                        stringify!($type)
460                    ))
461                    .into()),
462                    #[cfg(feature = "sqlx-sqlite")]
463                    QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
464                        "{} unsupported by sqlx-sqlite",
465                        stringify!($type)
466                    ))
467                    .into()),
468                    #[cfg(feature = "mock")]
469                    QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
470                        debug_print!("{:#?}", e.to_string());
471                        err_null_idx_col(idx)
472                    }),
473                    #[cfg(feature = "proxy")]
474                    QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
475                        debug_print!("{:#?}", e.to_string());
476                        err_null_idx_col(idx)
477                    }),
478                    #[allow(unreachable_patterns)]
479                    _ => unreachable!(),
480                }
481            }
482        }
483    };
484}
485
486#[allow(unused_macros)]
487macro_rules! try_getable_postgres {
488    ( $type: ty ) => {
489        impl TryGetable for $type {
490            #[allow(unused_variables)]
491            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
492                match &res.row {
493                    #[cfg(feature = "sqlx-mysql")]
494                    QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
495                        "{} unsupported by sqlx-mysql",
496                        stringify!($type)
497                    ))
498                    .into()),
499                    #[cfg(feature = "sqlx-postgres")]
500                    QueryResultRow::SqlxPostgres(row) => row
501                        .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
502                        .map_err(|e| sqlx_error_to_query_err(e).into())
503                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
504                    #[cfg(feature = "sqlx-sqlite")]
505                    QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
506                        "{} unsupported by sqlx-sqlite",
507                        stringify!($type)
508                    ))
509                    .into()),
510                    #[cfg(feature = "mock")]
511                    QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
512                        debug_print!("{:#?}", e.to_string());
513                        err_null_idx_col(idx)
514                    }),
515                    #[cfg(feature = "proxy")]
516                    QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
517                        debug_print!("{:#?}", e.to_string());
518                        err_null_idx_col(idx)
519                    }),
520                    #[allow(unreachable_patterns)]
521                    _ => unreachable!(),
522                }
523            }
524        }
525    };
526}
527
528#[allow(unused_macros)]
529macro_rules! try_getable_date_time {
530    ( $type: ty ) => {
531        impl TryGetable for $type {
532            #[allow(unused_variables)]
533            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
534                match &res.row {
535                    #[cfg(feature = "sqlx-mysql")]
536                    QueryResultRow::SqlxMySql(row) => {
537                        use chrono::{DateTime, Utc};
538                        row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_mysql_index())
539                            .map_err(|e| sqlx_error_to_query_err(e).into())
540                            .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
541                            .map(|v| v.into())
542                    }
543                    #[cfg(feature = "sqlx-postgres")]
544                    QueryResultRow::SqlxPostgres(row) => row
545                        .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_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                    #[cfg(feature = "sqlx-sqlite")]
549                    QueryResultRow::SqlxSqlite(row) => {
550                        use chrono::{DateTime, Utc};
551                        row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_sqlite_index())
552                            .map_err(|e| sqlx_error_to_query_err(e).into())
553                            .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
554                            .map(|v| v.into())
555                    }
556                    #[cfg(feature = "mock")]
557                    QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
558                        debug_print!("{:#?}", e.to_string());
559                        err_null_idx_col(idx)
560                    }),
561                    #[cfg(feature = "proxy")]
562                    QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
563                        debug_print!("{:#?}", e.to_string());
564                        err_null_idx_col(idx)
565                    }),
566                    #[allow(unreachable_patterns)]
567                    _ => unreachable!(),
568                }
569            }
570        }
571    };
572}
573
574try_getable_all!(bool);
575try_getable_all!(i8);
576try_getable_all!(i16);
577try_getable_all!(i32);
578try_getable_all!(i64);
579try_getable_unsigned!(u8);
580try_getable_unsigned!(u16);
581try_getable_mysql!(u64);
582try_getable_all!(f32);
583try_getable_all!(f64);
584try_getable_all!(Vec<u8>);
585
586#[cfg(feature = "with-json")]
587try_getable_all!(serde_json::Value);
588
589#[cfg(feature = "with-chrono")]
590try_getable_all!(chrono::NaiveDate);
591
592#[cfg(feature = "with-chrono")]
593try_getable_all!(chrono::NaiveTime);
594
595#[cfg(feature = "with-chrono")]
596try_getable_all!(chrono::NaiveDateTime);
597
598#[cfg(feature = "with-chrono")]
599try_getable_date_time!(chrono::DateTime<chrono::FixedOffset>);
600
601#[cfg(feature = "with-chrono")]
602try_getable_all!(chrono::DateTime<chrono::Utc>);
603
604#[cfg(feature = "with-chrono")]
605try_getable_all!(chrono::DateTime<chrono::Local>);
606
607#[cfg(feature = "with-time")]
608try_getable_all!(time::Date);
609
610#[cfg(feature = "with-time")]
611try_getable_all!(time::Time);
612
613#[cfg(feature = "with-time")]
614try_getable_all!(time::PrimitiveDateTime);
615
616#[cfg(feature = "with-time")]
617try_getable_all!(time::OffsetDateTime);
618
619#[cfg(feature = "with-rust_decimal")]
620use rust_decimal::Decimal;
621
622#[cfg(feature = "with-rust_decimal")]
623impl TryGetable for Decimal {
624    #[allow(unused_variables)]
625    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
626        match &res.row {
627            #[cfg(feature = "sqlx-mysql")]
628            QueryResultRow::SqlxMySql(row) => row
629                .try_get::<Option<Decimal>, _>(idx.as_sqlx_mysql_index())
630                .map_err(|e| sqlx_error_to_query_err(e).into())
631                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
632            #[cfg(feature = "sqlx-postgres")]
633            QueryResultRow::SqlxPostgres(row) => row
634                .try_get::<Option<Decimal>, _>(idx.as_sqlx_postgres_index())
635                .map_err(|e| sqlx_error_to_query_err(e).into())
636                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
637            #[cfg(feature = "sqlx-sqlite")]
638            QueryResultRow::SqlxSqlite(row) => {
639                let val: Option<f64> = row
640                    .try_get(idx.as_sqlx_sqlite_index())
641                    .map_err(sqlx_error_to_query_err)?;
642                match val {
643                    Some(v) => Decimal::try_from(v).map_err(|e| {
644                        DbErr::TryIntoErr {
645                            from: "f64",
646                            into: "Decimal",
647                            source: Arc::new(e),
648                        }
649                        .into()
650                    }),
651                    None => Err(err_null_idx_col(idx)),
652                }
653            }
654            #[cfg(feature = "mock")]
655            #[allow(unused_variables)]
656            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
657                debug_print!("{:#?}", e.to_string());
658                err_null_idx_col(idx)
659            }),
660            #[cfg(feature = "proxy")]
661            #[allow(unused_variables)]
662            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
663                debug_print!("{:#?}", e.to_string());
664                err_null_idx_col(idx)
665            }),
666            #[allow(unreachable_patterns)]
667            _ => unreachable!(),
668        }
669    }
670}
671
672#[cfg(feature = "with-bigdecimal")]
673use bigdecimal::BigDecimal;
674
675#[cfg(feature = "with-bigdecimal")]
676impl TryGetable for BigDecimal {
677    #[allow(unused_variables)]
678    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
679        match &res.row {
680            #[cfg(feature = "sqlx-mysql")]
681            QueryResultRow::SqlxMySql(row) => row
682                .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_mysql_index())
683                .map_err(|e| sqlx_error_to_query_err(e).into())
684                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
685            #[cfg(feature = "sqlx-postgres")]
686            QueryResultRow::SqlxPostgres(row) => row
687                .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_postgres_index())
688                .map_err(|e| sqlx_error_to_query_err(e).into())
689                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
690            #[cfg(feature = "sqlx-sqlite")]
691            QueryResultRow::SqlxSqlite(row) => {
692                let val: Option<f64> = row
693                    .try_get(idx.as_sqlx_sqlite_index())
694                    .map_err(sqlx_error_to_query_err)?;
695                match val {
696                    Some(v) => BigDecimal::try_from(v).map_err(|e| {
697                        DbErr::TryIntoErr {
698                            from: "f64",
699                            into: "BigDecimal",
700                            source: Arc::new(e),
701                        }
702                        .into()
703                    }),
704                    None => Err(err_null_idx_col(idx)),
705                }
706            }
707            #[cfg(feature = "mock")]
708            #[allow(unused_variables)]
709            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
710                debug_print!("{:#?}", e.to_string());
711                err_null_idx_col(idx)
712            }),
713            #[cfg(feature = "proxy")]
714            #[allow(unused_variables)]
715            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
716                debug_print!("{:#?}", e.to_string());
717                err_null_idx_col(idx)
718            }),
719            #[allow(unreachable_patterns)]
720            _ => unreachable!(),
721        }
722    }
723}
724
725#[allow(unused_macros)]
726macro_rules! try_getable_uuid {
727    ( $type: ty, $conversion_fn: expr ) => {
728        #[allow(unused_variables, unreachable_code)]
729        impl TryGetable for $type {
730            fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
731                let res: Result<uuid::Uuid, TryGetError> = match &res.row {
732                    #[cfg(feature = "sqlx-mysql")]
733                    QueryResultRow::SqlxMySql(row) => row
734                        .try_get::<Option<uuid::Uuid>, _>(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                        .or_else(|_| {
738                            // MariaDB's UUID type stores UUIDs as hyphenated strings.
739                            // reference: https://github.com/SeaQL/sea-orm/pull/2485
740                            row.try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
741                                .map_err(|e| sqlx_error_to_query_err(e).into())
742                                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
743                                .map(|bytes| {
744                                    String::from_utf8(bytes).map_err(|e| {
745                                        DbErr::TryIntoErr {
746                                            from: "Vec<u8>",
747                                            into: "String",
748                                            source: Arc::new(e),
749                                        }
750                                        .into()
751                                    })
752                                })?
753                                .and_then(|s| {
754                                    uuid::Uuid::parse_str(&s).map_err(|e| {
755                                        DbErr::TryIntoErr {
756                                            from: "String",
757                                            into: "uuid::Uuid",
758                                            source: Arc::new(e),
759                                        }
760                                        .into()
761                                    })
762                                })
763                        }),
764                    #[cfg(feature = "sqlx-postgres")]
765                    QueryResultRow::SqlxPostgres(row) => row
766                        .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_postgres_index())
767                        .map_err(|e| sqlx_error_to_query_err(e).into())
768                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
769                    #[cfg(feature = "sqlx-sqlite")]
770                    QueryResultRow::SqlxSqlite(row) => row
771                        .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_sqlite_index())
772                        .map_err(|e| sqlx_error_to_query_err(e).into())
773                        .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
774                    #[cfg(feature = "mock")]
775                    #[allow(unused_variables)]
776                    QueryResultRow::Mock(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
777                        debug_print!("{:#?}", e.to_string());
778                        err_null_idx_col(idx)
779                    }),
780                    #[cfg(feature = "proxy")]
781                    #[allow(unused_variables)]
782                    QueryResultRow::Proxy(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
783                        debug_print!("{:#?}", e.to_string());
784                        err_null_idx_col(idx)
785                    }),
786                    #[allow(unreachable_patterns)]
787                    _ => unreachable!(),
788                };
789                res.map($conversion_fn)
790            }
791        }
792    };
793}
794
795#[cfg(feature = "with-uuid")]
796try_getable_uuid!(uuid::Uuid, Into::into);
797
798#[cfg(feature = "with-uuid")]
799try_getable_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
800
801#[cfg(feature = "with-uuid")]
802try_getable_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
803
804#[cfg(feature = "with-uuid")]
805try_getable_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
806
807#[cfg(feature = "with-uuid")]
808try_getable_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
809
810#[cfg(feature = "with-ipnetwork")]
811try_getable_postgres!(ipnetwork::IpNetwork);
812
813impl TryGetable for u32 {
814    #[allow(unused_variables)]
815    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
816        match &res.row {
817            #[cfg(feature = "sqlx-mysql")]
818            QueryResultRow::SqlxMySql(row) => row
819                .try_get::<Option<u32>, _>(idx.as_sqlx_mysql_index())
820                .map_err(|e| sqlx_error_to_query_err(e).into())
821                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
822            #[cfg(feature = "sqlx-postgres")]
823            QueryResultRow::SqlxPostgres(row) => {
824                use sqlx::postgres::types::Oid;
825                // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
826                // Instead, `u32` was wrapped by a `sqlx::Oid`.
827                row.try_get::<Option<Oid>, _>(idx.as_sqlx_postgres_index())
828                    .map_err(|e| sqlx_error_to_query_err(e).into())
829                    .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
830                    .map(|oid| oid.0)
831            }
832            #[cfg(feature = "sqlx-sqlite")]
833            QueryResultRow::SqlxSqlite(row) => row
834                .try_get::<Option<u32>, _>(idx.as_sqlx_sqlite_index())
835                .map_err(|e| sqlx_error_to_query_err(e).into())
836                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
837            #[cfg(feature = "mock")]
838            #[allow(unused_variables)]
839            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
840                debug_print!("{:#?}", e.to_string());
841                err_null_idx_col(idx)
842            }),
843            #[cfg(feature = "proxy")]
844            #[allow(unused_variables)]
845            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
846                debug_print!("{:#?}", e.to_string());
847                err_null_idx_col(idx)
848            }),
849            #[allow(unreachable_patterns)]
850            _ => unreachable!(),
851        }
852    }
853}
854
855impl TryGetable for String {
856    #[allow(unused_variables)]
857    fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
858        match &res.row {
859            #[cfg(feature = "sqlx-mysql")]
860            QueryResultRow::SqlxMySql(row) => row
861                .try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
862                .map_err(|e| sqlx_error_to_query_err(e).into())
863                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
864                .map(|bytes| {
865                    String::from_utf8(bytes).map_err(|e| {
866                        DbErr::TryIntoErr {
867                            from: "Vec<u8>",
868                            into: "String",
869                            source: Arc::new(e),
870                        }
871                        .into()
872                    })
873                })?,
874            #[cfg(feature = "sqlx-postgres")]
875            QueryResultRow::SqlxPostgres(row) => row
876                .try_get::<Option<String>, _>(idx.as_sqlx_postgres_index())
877                .map_err(|e| sqlx_error_to_query_err(e).into())
878                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
879            #[cfg(feature = "sqlx-sqlite")]
880            QueryResultRow::SqlxSqlite(row) => row
881                .try_get::<Option<String>, _>(idx.as_sqlx_sqlite_index())
882                .map_err(|e| sqlx_error_to_query_err(e).into())
883                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
884            #[cfg(feature = "mock")]
885            QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
886                debug_print!("{:#?}", e.to_string());
887                err_null_idx_col(idx)
888            }),
889            #[cfg(feature = "proxy")]
890            QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
891                debug_print!("{:#?}", e.to_string());
892                err_null_idx_col(idx)
893            }),
894            #[allow(unreachable_patterns)]
895            _ => unreachable!(),
896        }
897    }
898}
899
900#[allow(dead_code)]
901fn err_null_idx_col<I: ColIdx>(idx: I) -> TryGetError {
902    TryGetError::Null(format!("{idx:?}"))
903}
904
905#[cfg(feature = "postgres-array")]
906mod postgres_array {
907    use super::*;
908
909    #[allow(unused_macros)]
910    macro_rules! try_getable_postgres_array {
911        ( $type: ty ) => {
912            #[allow(unused_variables)]
913            impl TryGetable for Vec<$type> {
914                fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
915                    match &res.row {
916                        #[cfg(feature = "sqlx-mysql")]
917                        QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
918                            "{} unsupported by sqlx-mysql",
919                            stringify!($type)
920                        ))
921                        .into()),
922                        #[cfg(feature = "sqlx-postgres")]
923                        QueryResultRow::SqlxPostgres(row) => row
924                            .try_get::<Option<Vec<$type>>, _>(idx.as_sqlx_postgres_index())
925                            .map_err(|e| sqlx_error_to_query_err(e).into())
926                            .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
927                        #[cfg(feature = "sqlx-sqlite")]
928                        QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
929                            "{} unsupported by sqlx-sqlite",
930                            stringify!($type)
931                        ))
932                        .into()),
933                        #[cfg(feature = "mock")]
934                        #[allow(unused_variables)]
935                        QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
936                            debug_print!("{:#?}", e.to_string());
937                            err_null_idx_col(idx)
938                        }),
939                        #[cfg(feature = "proxy")]
940                        #[allow(unused_variables)]
941                        QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
942                            debug_print!("{:#?}", e.to_string());
943                            err_null_idx_col(idx)
944                        }),
945                        #[allow(unreachable_patterns)]
946                        _ => unreachable!(),
947                    }
948                }
949            }
950        };
951    }
952
953    try_getable_postgres_array!(bool);
954    try_getable_postgres_array!(i8);
955    try_getable_postgres_array!(i16);
956    try_getable_postgres_array!(i32);
957    try_getable_postgres_array!(i64);
958    try_getable_postgres_array!(f32);
959    try_getable_postgres_array!(f64);
960    try_getable_postgres_array!(String);
961    try_getable_postgres_array!(Vec<u8>);
962
963    #[cfg(feature = "with-json")]
964    try_getable_postgres_array!(serde_json::Value);
965
966    #[cfg(feature = "with-chrono")]
967    try_getable_postgres_array!(chrono::NaiveDate);
968
969    #[cfg(feature = "with-chrono")]
970    try_getable_postgres_array!(chrono::NaiveTime);
971
972    #[cfg(feature = "with-chrono")]
973    try_getable_postgres_array!(chrono::NaiveDateTime);
974
975    #[cfg(feature = "with-chrono")]
976    try_getable_postgres_array!(chrono::DateTime<chrono::FixedOffset>);
977
978    #[cfg(feature = "with-chrono")]
979    try_getable_postgres_array!(chrono::DateTime<chrono::Utc>);
980
981    #[cfg(feature = "with-chrono")]
982    try_getable_postgres_array!(chrono::DateTime<chrono::Local>);
983
984    #[cfg(feature = "with-time")]
985    try_getable_postgres_array!(time::Date);
986
987    #[cfg(feature = "with-time")]
988    try_getable_postgres_array!(time::Time);
989
990    #[cfg(feature = "with-time")]
991    try_getable_postgres_array!(time::PrimitiveDateTime);
992
993    #[cfg(feature = "with-time")]
994    try_getable_postgres_array!(time::OffsetDateTime);
995
996    #[cfg(feature = "with-rust_decimal")]
997    try_getable_postgres_array!(rust_decimal::Decimal);
998
999    #[cfg(feature = "with-bigdecimal")]
1000    try_getable_postgres_array!(bigdecimal::BigDecimal);
1001
1002    #[cfg(feature = "with-ipnetwork")]
1003    try_getable_postgres_array!(ipnetwork::IpNetwork);
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::{DeriveIden, EnumIter, TryGetableMany, entity::*, query::*, tests_cfg::cake};
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 {
1216            stmt,
1217            selector: PhantomData,
1218        }
1219    }
1220}
1221
1222impl<T> TryGetableMany for T
1223where
1224    T: TryGetable,
1225{
1226    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1227        try_get_many_with_slice_len_of(1, cols)?;
1228        T::try_get(res, pre, &cols[0])
1229    }
1230
1231    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1232        T::try_get_by_index(res, 0)
1233    }
1234}
1235
1236impl<T> TryGetableMany for (T,)
1237where
1238    T: TryGetableMany,
1239{
1240    fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1241        T::try_get_many(res, pre, cols).map(|r| (r,))
1242    }
1243
1244    fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1245        T::try_get_many_by_index(res).map(|r| (r,))
1246    }
1247}
1248
1249macro_rules! impl_try_get_many {
1250    ( $LEN:expr, $($T:ident : $N:expr),+ $(,)? ) => {
1251        impl< $($T),+ > TryGetableMany for ( $($T),+ )
1252        where
1253            $($T: TryGetable),+
1254        {
1255            fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1256                try_get_many_with_slice_len_of($LEN, cols)?;
1257                Ok((
1258                    $($T::try_get(res, pre, &cols[$N])?),+
1259                ))
1260            }
1261
1262            fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1263                Ok((
1264                    $($T::try_get_by_index(res, $N)?),+
1265                ))
1266            }
1267        }
1268    };
1269}
1270
1271#[rustfmt::skip]
1272mod impl_try_get_many {
1273    use super::*;
1274
1275    impl_try_get_many!( 2, T0:0, T1:1);
1276    impl_try_get_many!( 3, T0:0, T1:1, T2:2);
1277    impl_try_get_many!( 4, T0:0, T1:1, T2:2, T3:3);
1278    impl_try_get_many!( 5, T0:0, T1:1, T2:2, T3:3, T4:4);
1279    impl_try_get_many!( 6, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
1280    impl_try_get_many!( 7, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
1281    impl_try_get_many!( 8, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
1282    impl_try_get_many!( 9, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
1283    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);
1284    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);
1285    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);
1286}
1287
1288fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
1289    if cols.len() < len {
1290        Err(type_err(format!(
1291            "Expect {} column names supplied but got slice of length {}",
1292            len,
1293            cols.len()
1294        ))
1295        .into())
1296    } else {
1297        Ok(())
1298    }
1299}
1300
1301/// An interface to get an array of values from the query result.
1302/// A type can only implement `ActiveEnum` or `TryGetableFromJson`, but not both.
1303/// A blanket impl is provided for `TryGetableFromJson`, while the impl for `ActiveEnum`
1304/// is provided by the `DeriveActiveEnum` macro. So as an end user you won't normally
1305/// touch this trait.
1306pub trait TryGetableArray: Sized {
1307    /// Just a delegate
1308    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
1309}
1310
1311impl<T> TryGetable for Vec<T>
1312where
1313    T: TryGetableArray,
1314{
1315    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1316        T::try_get_by(res, index)
1317    }
1318}
1319
1320// TryGetableFromJson //
1321
1322/// An interface to get a JSON from the query result
1323#[cfg(feature = "with-json")]
1324pub trait TryGetableFromJson: Sized
1325where
1326    for<'de> Self: serde::Deserialize<'de>,
1327{
1328    /// Get a JSON from the query result with prefixed column name
1329    #[allow(unused_variables, unreachable_code)]
1330    fn try_get_from_json<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1331        match &res.row {
1332            #[cfg(feature = "sqlx-mysql")]
1333            QueryResultRow::SqlxMySql(row) => row
1334                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
1335                .map_err(|e| sqlx_error_to_query_err(e).into())
1336                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1337            #[cfg(feature = "sqlx-postgres")]
1338            QueryResultRow::SqlxPostgres(row) => row
1339                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
1340                .map_err(|e| sqlx_error_to_query_err(e).into())
1341                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1342            #[cfg(feature = "sqlx-sqlite")]
1343            QueryResultRow::SqlxSqlite(row) => row
1344                .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
1345                .map_err(|e| sqlx_error_to_query_err(e).into())
1346                .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1347            #[cfg(feature = "mock")]
1348            QueryResultRow::Mock(row) => row
1349                .try_get::<serde_json::Value, I>(idx)
1350                .map_err(|e| {
1351                    debug_print!("{:#?}", e.to_string());
1352                    err_null_idx_col(idx)
1353                })
1354                .and_then(|json| {
1355                    serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1356                }),
1357            #[cfg(feature = "proxy")]
1358            QueryResultRow::Proxy(row) => row
1359                .try_get::<serde_json::Value, I>(idx)
1360                .map_err(|e| {
1361                    debug_print!("{:#?}", e.to_string());
1362                    err_null_idx_col(idx)
1363                })
1364                .and_then(|json| {
1365                    serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1366                }),
1367            #[allow(unreachable_patterns)]
1368            _ => unreachable!(),
1369        }
1370    }
1371
1372    /// Get a Vec<Self> from an Array of Json
1373    fn from_json_vec(value: serde_json::Value) -> Result<Vec<Self>, TryGetError> {
1374        match value {
1375            serde_json::Value::Array(values) => {
1376                let mut res = Vec::new();
1377                for item in values {
1378                    res.push(serde_json::from_value(item).map_err(crate::error::json_err)?);
1379                }
1380                Ok(res)
1381            }
1382            _ => Err(TryGetError::DbErr(DbErr::Json(
1383                "Value is not an Array".to_owned(),
1384            ))),
1385        }
1386    }
1387}
1388
1389#[cfg(feature = "with-json")]
1390impl<T> TryGetable for T
1391where
1392    T: TryGetableFromJson,
1393{
1394    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1395        T::try_get_from_json(res, index)
1396    }
1397}
1398
1399#[cfg(feature = "with-json")]
1400impl<T> TryGetableArray for T
1401where
1402    T: TryGetableFromJson,
1403{
1404    fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<T>, TryGetError> {
1405        T::from_json_vec(serde_json::Value::try_get_by(res, index)?)
1406    }
1407}
1408
1409// TryFromU64 //
1410/// Try to convert a type to a u64
1411pub trait TryFromU64: Sized {
1412    /// The method to convert the type to a u64
1413    fn try_from_u64(n: u64) -> Result<Self, DbErr>;
1414}
1415
1416macro_rules! try_from_u64_err {
1417    ( $type: ty ) => {
1418        impl TryFromU64 for $type {
1419            fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1420                Err(DbErr::ConvertFromU64(stringify!($type)))
1421            }
1422        }
1423    };
1424
1425    ( $($gen_type: ident),* ) => {
1426        impl<$( $gen_type, )*> TryFromU64 for ($( $gen_type, )*)
1427        where
1428            $( $gen_type: TryFromU64, )*
1429        {
1430            fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1431                Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*)))
1432            }
1433        }
1434    };
1435}
1436
1437#[rustfmt::skip]
1438mod try_from_u64_err {
1439    use super::*;
1440
1441    try_from_u64_err!(T0, T1);
1442    try_from_u64_err!(T0, T1, T2);
1443    try_from_u64_err!(T0, T1, T2, T3);
1444    try_from_u64_err!(T0, T1, T2, T3, T4);
1445    try_from_u64_err!(T0, T1, T2, T3, T4, T5);
1446    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6);
1447    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7);
1448    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
1449    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
1450    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
1451    try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
1452}
1453
1454macro_rules! try_from_u64_numeric {
1455    ( $type: ty ) => {
1456        impl TryFromU64 for $type {
1457            fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1458                use std::convert::TryInto;
1459                n.try_into().map_err(|e| DbErr::TryIntoErr {
1460                    from: stringify!(u64),
1461                    into: stringify!($type),
1462                    source: Arc::new(e),
1463                })
1464            }
1465        }
1466    };
1467}
1468
1469try_from_u64_numeric!(i8);
1470try_from_u64_numeric!(i16);
1471try_from_u64_numeric!(i32);
1472try_from_u64_numeric!(i64);
1473try_from_u64_numeric!(u8);
1474try_from_u64_numeric!(u16);
1475try_from_u64_numeric!(u32);
1476try_from_u64_numeric!(u64);
1477
1478macro_rules! try_from_u64_string {
1479    ( $type: ty ) => {
1480        impl TryFromU64 for $type {
1481            fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1482                Ok(n.to_string())
1483            }
1484        }
1485    };
1486}
1487
1488try_from_u64_string!(String);
1489
1490try_from_u64_err!(bool);
1491try_from_u64_err!(f32);
1492try_from_u64_err!(f64);
1493try_from_u64_err!(Vec<u8>);
1494
1495#[cfg(feature = "with-json")]
1496try_from_u64_err!(serde_json::Value);
1497
1498#[cfg(feature = "with-chrono")]
1499try_from_u64_err!(chrono::NaiveDate);
1500
1501#[cfg(feature = "with-chrono")]
1502try_from_u64_err!(chrono::NaiveTime);
1503
1504#[cfg(feature = "with-chrono")]
1505try_from_u64_err!(chrono::NaiveDateTime);
1506
1507#[cfg(feature = "with-chrono")]
1508try_from_u64_err!(chrono::DateTime<chrono::FixedOffset>);
1509
1510#[cfg(feature = "with-chrono")]
1511try_from_u64_err!(chrono::DateTime<chrono::Utc>);
1512
1513#[cfg(feature = "with-chrono")]
1514try_from_u64_err!(chrono::DateTime<chrono::Local>);
1515
1516#[cfg(feature = "with-time")]
1517try_from_u64_err!(time::Date);
1518
1519#[cfg(feature = "with-time")]
1520try_from_u64_err!(time::Time);
1521
1522#[cfg(feature = "with-time")]
1523try_from_u64_err!(time::PrimitiveDateTime);
1524
1525#[cfg(feature = "with-time")]
1526try_from_u64_err!(time::OffsetDateTime);
1527
1528#[cfg(feature = "with-rust_decimal")]
1529try_from_u64_err!(rust_decimal::Decimal);
1530
1531#[cfg(feature = "with-uuid")]
1532try_from_u64_err!(uuid::Uuid);
1533
1534#[cfg(feature = "with-ipnetwork")]
1535try_from_u64_err!(ipnetwork::IpNetwork);
1536
1537#[cfg(test)]
1538mod tests {
1539    use super::*;
1540    use crate::RuntimeErr;
1541    use sea_query::Value;
1542    use std::collections::BTreeMap;
1543
1544    #[test]
1545    fn from_try_get_error() {
1546        // TryGetError::DbErr
1547        let try_get_error = TryGetError::DbErr(DbErr::Query(RuntimeErr::Internal(
1548            "expected error message".to_owned(),
1549        )));
1550        assert_eq!(
1551            DbErr::from(try_get_error),
1552            DbErr::Query(RuntimeErr::Internal("expected error message".to_owned()))
1553        );
1554
1555        // TryGetError::Null
1556        let try_get_error = TryGetError::Null("column".to_owned());
1557        let expected = "A null value was encountered while decoding column".to_owned();
1558        assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected));
1559    }
1560
1561    #[test]
1562    fn build_with_query() {
1563        use sea_orm::{DbBackend, Statement};
1564        use sea_query::{
1565            ColumnRef, CommonTableExpression, Cycle, Expr, ExprTrait, JoinType, SelectStatement,
1566            UnionType, WithClause,
1567        };
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(None))
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                Expr::column("id"),
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("name".to_string(), Value::String(Some("Abc".to_owned())));
1633        let query_result = QueryResult {
1634            row: QueryResultRow::Mock(crate::MockRow { values }),
1635        };
1636        assert_eq!(
1637            query_result.column_names(),
1638            vec!["id".to_owned(), "name".to_owned()]
1639        );
1640    }
1641}