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