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