1pub use crate::error::TryGetError;
2use crate::{
3 error::{type_err, DbErr},
4 SelectGetableValue, SelectorRaw, Statement,
5};
6use std::fmt;
7
8#[cfg(any(feature = "mock", feature = "proxy"))]
9use crate::debug_print;
10
11#[cfg(feature = "sqlx-dep")]
12use crate::driver::*;
13#[cfg(feature = "sqlx-dep")]
14use sqlx::Row;
15
16#[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
36pub trait TryGetable: Sized {
38 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError>;
40
41 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 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
73impl QueryResult {
76 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 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 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 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[cfg(feature = "proxy")]
219 pub fn try_as_proxy_row(&self) -> Option<&crate::ProxyRow> {
220 match &self.row {
221 QueryResultRow::Proxy(proxy_row) => Some(proxy_row),
222 #[allow(unreachable_patterns)]
223 _ => None,
224 }
225 }
226}
227
228#[allow(unused_variables)]
229impl fmt::Debug for QueryResultRow {
230 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
231 match self {
232 #[cfg(feature = "sqlx-mysql")]
233 Self::SqlxMySql(row) => write!(f, "{row:?}"),
234 #[cfg(feature = "sqlx-postgres")]
235 Self::SqlxPostgres(_) => write!(f, "QueryResultRow::SqlxPostgres cannot be inspected"),
236 #[cfg(feature = "sqlx-sqlite")]
237 Self::SqlxSqlite(_) => write!(f, "QueryResultRow::SqlxSqlite cannot be inspected"),
238 #[cfg(feature = "mock")]
239 Self::Mock(row) => write!(f, "{row:?}"),
240 #[cfg(feature = "proxy")]
241 Self::Proxy(row) => write!(f, "{row:?}"),
242 #[allow(unreachable_patterns)]
243 _ => unreachable!(),
244 }
245 }
246}
247
248impl<T: TryGetable> TryGetable for Option<T> {
251 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
252 match T::try_get_by(res, index) {
253 Ok(v) => Ok(Some(v)),
254 Err(TryGetError::Null(_)) => Ok(None),
255 #[cfg(feature = "sqlx-dep")]
256 Err(TryGetError::DbErr(DbErr::Query(crate::RuntimeErr::SqlxError(
257 sqlx::Error::ColumnNotFound(_),
258 )))) => Ok(None),
259 Err(e) => Err(e),
260 }
261 }
262}
263
264pub trait ColIdx: std::fmt::Debug + Copy {
266 #[cfg(feature = "sqlx-mysql")]
267 type SqlxMySqlIndex: sqlx::ColumnIndex<sqlx::mysql::MySqlRow>;
269 #[cfg(feature = "sqlx-postgres")]
270 type SqlxPostgresIndex: sqlx::ColumnIndex<sqlx::postgres::PgRow>;
272 #[cfg(feature = "sqlx-sqlite")]
273 type SqlxSqliteIndex: sqlx::ColumnIndex<sqlx::sqlite::SqliteRow>;
275
276 #[cfg(feature = "sqlx-mysql")]
277 fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex;
279 #[cfg(feature = "sqlx-postgres")]
280 fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex;
282 #[cfg(feature = "sqlx-sqlite")]
283 fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex;
285
286 fn as_str(&self) -> Option<&str>;
288 fn as_usize(&self) -> Option<&usize>;
290}
291
292impl ColIdx for &str {
293 #[cfg(feature = "sqlx-mysql")]
294 type SqlxMySqlIndex = Self;
295 #[cfg(feature = "sqlx-postgres")]
296 type SqlxPostgresIndex = Self;
297 #[cfg(feature = "sqlx-sqlite")]
298 type SqlxSqliteIndex = Self;
299
300 #[cfg(feature = "sqlx-mysql")]
301 #[inline]
302 fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
303 self
304 }
305 #[cfg(feature = "sqlx-postgres")]
306 #[inline]
307 fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
308 self
309 }
310 #[cfg(feature = "sqlx-sqlite")]
311 #[inline]
312 fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
313 self
314 }
315
316 #[inline]
317 fn as_str(&self) -> Option<&str> {
318 Some(self)
319 }
320 #[inline]
321 fn as_usize(&self) -> Option<&usize> {
322 None
323 }
324}
325
326impl ColIdx for usize {
327 #[cfg(feature = "sqlx-mysql")]
328 type SqlxMySqlIndex = Self;
329 #[cfg(feature = "sqlx-postgres")]
330 type SqlxPostgresIndex = Self;
331 #[cfg(feature = "sqlx-sqlite")]
332 type SqlxSqliteIndex = Self;
333
334 #[cfg(feature = "sqlx-mysql")]
335 #[inline]
336 fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
337 *self
338 }
339 #[cfg(feature = "sqlx-postgres")]
340 #[inline]
341 fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
342 *self
343 }
344 #[cfg(feature = "sqlx-sqlite")]
345 #[inline]
346 fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
347 *self
348 }
349
350 #[inline]
351 fn as_str(&self) -> Option<&str> {
352 None
353 }
354 #[inline]
355 fn as_usize(&self) -> Option<&usize> {
356 Some(self)
357 }
358}
359
360macro_rules! try_getable_all {
361 ( $type: ty ) => {
362 impl TryGetable for $type {
363 #[allow(unused_variables)]
364 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
365 match &res.row {
366 #[cfg(feature = "sqlx-mysql")]
367 QueryResultRow::SqlxMySql(row) => row
368 .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
369 .map_err(|e| sqlx_error_to_query_err(e).into())
370 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
371 #[cfg(feature = "sqlx-postgres")]
372 QueryResultRow::SqlxPostgres(row) => row
373 .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
374 .map_err(|e| sqlx_error_to_query_err(e).into())
375 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
376 #[cfg(feature = "sqlx-sqlite")]
377 QueryResultRow::SqlxSqlite(row) => row
378 .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
379 .map_err(|e| sqlx_error_to_query_err(e).into())
380 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
381 #[cfg(feature = "mock")]
382 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
383 debug_print!("{:#?}", e.to_string());
384 err_null_idx_col(idx)
385 }),
386 #[cfg(feature = "proxy")]
387 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
388 debug_print!("{:#?}", e.to_string());
389 err_null_idx_col(idx)
390 }),
391 #[allow(unreachable_patterns)]
392 _ => unreachable!(),
393 }
394 }
395 }
396 };
397}
398
399macro_rules! try_getable_unsigned {
400 ( $type: ty ) => {
401 impl TryGetable for $type {
402 #[allow(unused_variables)]
403 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
404 match &res.row {
405 #[cfg(feature = "sqlx-mysql")]
406 QueryResultRow::SqlxMySql(row) => row
407 .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
408 .map_err(|e| sqlx_error_to_query_err(e).into())
409 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
410 #[cfg(feature = "sqlx-postgres")]
411 QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
412 "{} unsupported by sqlx-postgres",
413 stringify!($type)
414 ))
415 .into()),
416 #[cfg(feature = "sqlx-sqlite")]
417 QueryResultRow::SqlxSqlite(row) => row
418 .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
419 .map_err(|e| sqlx_error_to_query_err(e).into())
420 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
421 #[cfg(feature = "mock")]
422 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
423 debug_print!("{:#?}", e.to_string());
424 err_null_idx_col(idx)
425 }),
426 #[cfg(feature = "proxy")]
427 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
428 debug_print!("{:#?}", e.to_string());
429 err_null_idx_col(idx)
430 }),
431 #[allow(unreachable_patterns)]
432 _ => unreachable!(),
433 }
434 }
435 }
436 };
437}
438
439macro_rules! try_getable_mysql {
440 ( $type: ty ) => {
441 impl TryGetable for $type {
442 #[allow(unused_variables)]
443 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
444 match &res.row {
445 #[cfg(feature = "sqlx-mysql")]
446 QueryResultRow::SqlxMySql(row) => row
447 .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
448 .map_err(|e| sqlx_error_to_query_err(e).into())
449 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
450 #[cfg(feature = "sqlx-postgres")]
451 QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
452 "{} unsupported by sqlx-postgres",
453 stringify!($type)
454 ))
455 .into()),
456 #[cfg(feature = "sqlx-sqlite")]
457 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
458 "{} unsupported by sqlx-sqlite",
459 stringify!($type)
460 ))
461 .into()),
462 #[cfg(feature = "mock")]
463 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
464 debug_print!("{:#?}", e.to_string());
465 err_null_idx_col(idx)
466 }),
467 #[cfg(feature = "proxy")]
468 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
469 debug_print!("{:#?}", e.to_string());
470 err_null_idx_col(idx)
471 }),
472 #[allow(unreachable_patterns)]
473 _ => unreachable!(),
474 }
475 }
476 }
477 };
478}
479
480#[allow(unused_macros)]
481macro_rules! try_getable_postgres {
482 ( $type: ty ) => {
483 impl TryGetable for $type {
484 #[allow(unused_variables)]
485 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
486 match &res.row {
487 #[cfg(feature = "sqlx-mysql")]
488 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
489 "{} unsupported by sqlx-mysql",
490 stringify!($type)
491 ))
492 .into()),
493 #[cfg(feature = "sqlx-postgres")]
494 QueryResultRow::SqlxPostgres(row) => row
495 .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
496 .map_err(|e| sqlx_error_to_query_err(e).into())
497 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
498 #[cfg(feature = "sqlx-sqlite")]
499 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
500 "{} unsupported by sqlx-sqlite",
501 stringify!($type)
502 ))
503 .into()),
504 #[cfg(feature = "mock")]
505 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
506 debug_print!("{:#?}", e.to_string());
507 err_null_idx_col(idx)
508 }),
509 #[cfg(feature = "proxy")]
510 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
511 debug_print!("{:#?}", e.to_string());
512 err_null_idx_col(idx)
513 }),
514 #[allow(unreachable_patterns)]
515 _ => unreachable!(),
516 }
517 }
518 }
519 };
520}
521
522#[allow(unused_macros)]
523macro_rules! try_getable_date_time {
524 ( $type: ty ) => {
525 impl TryGetable for $type {
526 #[allow(unused_variables)]
527 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
528 match &res.row {
529 #[cfg(feature = "sqlx-mysql")]
530 QueryResultRow::SqlxMySql(row) => {
531 use chrono::{DateTime, Utc};
532 row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_mysql_index())
533 .map_err(|e| sqlx_error_to_query_err(e).into())
534 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
535 .map(|v| v.into())
536 }
537 #[cfg(feature = "sqlx-postgres")]
538 QueryResultRow::SqlxPostgres(row) => row
539 .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
540 .map_err(|e| sqlx_error_to_query_err(e).into())
541 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
542 #[cfg(feature = "sqlx-sqlite")]
543 QueryResultRow::SqlxSqlite(row) => {
544 use chrono::{DateTime, Utc};
545 row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_sqlite_index())
546 .map_err(|e| sqlx_error_to_query_err(e).into())
547 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
548 .map(|v| v.into())
549 }
550 #[cfg(feature = "mock")]
551 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
552 debug_print!("{:#?}", e.to_string());
553 err_null_idx_col(idx)
554 }),
555 #[cfg(feature = "proxy")]
556 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
557 debug_print!("{:#?}", e.to_string());
558 err_null_idx_col(idx)
559 }),
560 #[allow(unreachable_patterns)]
561 _ => unreachable!(),
562 }
563 }
564 }
565 };
566}
567
568try_getable_all!(bool);
569try_getable_all!(i8);
570try_getable_all!(i16);
571try_getable_all!(i32);
572try_getable_all!(i64);
573try_getable_unsigned!(u8);
574try_getable_unsigned!(u16);
575try_getable_mysql!(u64);
576try_getable_all!(f32);
577try_getable_all!(f64);
578try_getable_all!(Vec<u8>);
579
580#[cfg(feature = "with-json")]
581try_getable_all!(serde_json::Value);
582
583#[cfg(feature = "with-chrono")]
584try_getable_all!(chrono::NaiveDate);
585
586#[cfg(feature = "with-chrono")]
587try_getable_all!(chrono::NaiveTime);
588
589#[cfg(feature = "with-chrono")]
590try_getable_all!(chrono::NaiveDateTime);
591
592#[cfg(feature = "with-chrono")]
593try_getable_date_time!(chrono::DateTime<chrono::FixedOffset>);
594
595#[cfg(feature = "with-chrono")]
596try_getable_all!(chrono::DateTime<chrono::Utc>);
597
598#[cfg(feature = "with-chrono")]
599try_getable_all!(chrono::DateTime<chrono::Local>);
600
601#[cfg(feature = "with-time")]
602try_getable_all!(time::Date);
603
604#[cfg(feature = "with-time")]
605try_getable_all!(time::Time);
606
607#[cfg(feature = "with-time")]
608try_getable_all!(time::PrimitiveDateTime);
609
610#[cfg(feature = "with-time")]
611try_getable_all!(time::OffsetDateTime);
612
613#[cfg(feature = "with-rust_decimal")]
614use rust_decimal::Decimal;
615
616#[cfg(feature = "with-rust_decimal")]
617impl TryGetable for Decimal {
618 #[allow(unused_variables)]
619 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
620 match &res.row {
621 #[cfg(feature = "sqlx-mysql")]
622 QueryResultRow::SqlxMySql(row) => row
623 .try_get::<Option<Decimal>, _>(idx.as_sqlx_mysql_index())
624 .map_err(|e| sqlx_error_to_query_err(e).into())
625 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
626 #[cfg(feature = "sqlx-postgres")]
627 QueryResultRow::SqlxPostgres(row) => row
628 .try_get::<Option<Decimal>, _>(idx.as_sqlx_postgres_index())
629 .map_err(|e| sqlx_error_to_query_err(e).into())
630 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
631 #[cfg(feature = "sqlx-sqlite")]
632 QueryResultRow::SqlxSqlite(row) => {
633 let val: Option<f64> = row
634 .try_get(idx.as_sqlx_sqlite_index())
635 .map_err(sqlx_error_to_query_err)?;
636 match val {
637 Some(v) => Decimal::try_from(v).map_err(|e| {
638 DbErr::TryIntoErr {
639 from: "f64",
640 into: "Decimal",
641 source: Box::new(e),
642 }
643 .into()
644 }),
645 None => Err(err_null_idx_col(idx)),
646 }
647 }
648 #[cfg(feature = "mock")]
649 #[allow(unused_variables)]
650 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
651 debug_print!("{:#?}", e.to_string());
652 err_null_idx_col(idx)
653 }),
654 #[cfg(feature = "proxy")]
655 #[allow(unused_variables)]
656 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
657 debug_print!("{:#?}", e.to_string());
658 err_null_idx_col(idx)
659 }),
660 #[allow(unreachable_patterns)]
661 _ => unreachable!(),
662 }
663 }
664}
665
666#[cfg(feature = "with-bigdecimal")]
667use bigdecimal::BigDecimal;
668
669#[cfg(feature = "with-bigdecimal")]
670impl TryGetable for BigDecimal {
671 #[allow(unused_variables)]
672 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
673 match &res.row {
674 #[cfg(feature = "sqlx-mysql")]
675 QueryResultRow::SqlxMySql(row) => row
676 .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_mysql_index())
677 .map_err(|e| sqlx_error_to_query_err(e).into())
678 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
679 #[cfg(feature = "sqlx-postgres")]
680 QueryResultRow::SqlxPostgres(row) => row
681 .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_postgres_index())
682 .map_err(|e| sqlx_error_to_query_err(e).into())
683 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
684 #[cfg(feature = "sqlx-sqlite")]
685 QueryResultRow::SqlxSqlite(row) => {
686 let val: Option<f64> = row
687 .try_get(idx.as_sqlx_sqlite_index())
688 .map_err(sqlx_error_to_query_err)?;
689 match val {
690 Some(v) => BigDecimal::try_from(v).map_err(|e| {
691 DbErr::TryIntoErr {
692 from: "f64",
693 into: "BigDecimal",
694 source: Box::new(e),
695 }
696 .into()
697 }),
698 None => Err(err_null_idx_col(idx)),
699 }
700 }
701 #[cfg(feature = "mock")]
702 #[allow(unused_variables)]
703 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
704 debug_print!("{:#?}", e.to_string());
705 err_null_idx_col(idx)
706 }),
707 #[cfg(feature = "proxy")]
708 #[allow(unused_variables)]
709 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
710 debug_print!("{:#?}", e.to_string());
711 err_null_idx_col(idx)
712 }),
713 #[allow(unreachable_patterns)]
714 _ => unreachable!(),
715 }
716 }
717}
718
719#[allow(unused_macros)]
720macro_rules! try_getable_uuid {
721 ( $type: ty, $conversion_fn: expr ) => {
722 #[allow(unused_variables, unreachable_code)]
723 impl TryGetable for $type {
724 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
725 let res: Result<uuid::Uuid, TryGetError> = match &res.row {
726 #[cfg(feature = "sqlx-mysql")]
727 QueryResultRow::SqlxMySql(row) => row
728 .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_mysql_index())
729 .map_err(|e| sqlx_error_to_query_err(e).into())
730 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
731 .or_else(|_| {
732 row.try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
735 .map_err(|e| sqlx_error_to_query_err(e).into())
736 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
737 .map(|bytes| {
738 String::from_utf8(bytes).map_err(|e| {
739 DbErr::TryIntoErr {
740 from: "Vec<u8>",
741 into: "String",
742 source: Box::new(e),
743 }
744 .into()
745 })
746 })?
747 .and_then(|s| {
748 uuid::Uuid::parse_str(&s).map_err(|e| {
749 DbErr::TryIntoErr {
750 from: "String",
751 into: "uuid::Uuid",
752 source: Box::new(e),
753 }
754 .into()
755 })
756 })
757 }),
758 #[cfg(feature = "sqlx-postgres")]
759 QueryResultRow::SqlxPostgres(row) => row
760 .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_postgres_index())
761 .map_err(|e| sqlx_error_to_query_err(e).into())
762 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
763 #[cfg(feature = "sqlx-sqlite")]
764 QueryResultRow::SqlxSqlite(row) => row
765 .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_sqlite_index())
766 .map_err(|e| sqlx_error_to_query_err(e).into())
767 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
768 #[cfg(feature = "mock")]
769 #[allow(unused_variables)]
770 QueryResultRow::Mock(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
771 debug_print!("{:#?}", e.to_string());
772 err_null_idx_col(idx)
773 }),
774 #[cfg(feature = "proxy")]
775 #[allow(unused_variables)]
776 QueryResultRow::Proxy(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
777 debug_print!("{:#?}", e.to_string());
778 err_null_idx_col(idx)
779 }),
780 #[allow(unreachable_patterns)]
781 _ => unreachable!(),
782 };
783 res.map($conversion_fn)
784 }
785 }
786 };
787}
788
789#[cfg(feature = "with-uuid")]
790try_getable_uuid!(uuid::Uuid, Into::into);
791
792#[cfg(feature = "with-uuid")]
793try_getable_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
794
795#[cfg(feature = "with-uuid")]
796try_getable_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
797
798#[cfg(feature = "with-uuid")]
799try_getable_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
800
801#[cfg(feature = "with-uuid")]
802try_getable_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
803
804#[cfg(feature = "with-ipnetwork")]
805try_getable_postgres!(ipnetwork::IpNetwork);
806
807impl TryGetable for u32 {
808 #[allow(unused_variables)]
809 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
810 match &res.row {
811 #[cfg(feature = "sqlx-mysql")]
812 QueryResultRow::SqlxMySql(row) => row
813 .try_get::<Option<u32>, _>(idx.as_sqlx_mysql_index())
814 .map_err(|e| sqlx_error_to_query_err(e).into())
815 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
816 #[cfg(feature = "sqlx-postgres")]
817 QueryResultRow::SqlxPostgres(row) => {
818 use sqlx::postgres::types::Oid;
819 row.try_get::<Option<Oid>, _>(idx.as_sqlx_postgres_index())
822 .map_err(|e| sqlx_error_to_query_err(e).into())
823 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
824 .map(|oid| oid.0)
825 }
826 #[cfg(feature = "sqlx-sqlite")]
827 QueryResultRow::SqlxSqlite(row) => row
828 .try_get::<Option<u32>, _>(idx.as_sqlx_sqlite_index())
829 .map_err(|e| sqlx_error_to_query_err(e).into())
830 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
831 #[cfg(feature = "mock")]
832 #[allow(unused_variables)]
833 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
834 debug_print!("{:#?}", e.to_string());
835 err_null_idx_col(idx)
836 }),
837 #[cfg(feature = "proxy")]
838 #[allow(unused_variables)]
839 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
840 debug_print!("{:#?}", e.to_string());
841 err_null_idx_col(idx)
842 }),
843 #[allow(unreachable_patterns)]
844 _ => unreachable!(),
845 }
846 }
847}
848
849impl TryGetable for String {
850 #[allow(unused_variables)]
851 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
852 match &res.row {
853 #[cfg(feature = "sqlx-mysql")]
854 QueryResultRow::SqlxMySql(row) => row
855 .try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
856 .map_err(|e| sqlx_error_to_query_err(e).into())
857 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
858 .map(|bytes| {
859 String::from_utf8(bytes).map_err(|e| {
860 DbErr::TryIntoErr {
861 from: "Vec<u8>",
862 into: "String",
863 source: Box::new(e),
864 }
865 .into()
866 })
867 })?,
868 #[cfg(feature = "sqlx-postgres")]
869 QueryResultRow::SqlxPostgres(row) => row
870 .try_get::<Option<String>, _>(idx.as_sqlx_postgres_index())
871 .map_err(|e| sqlx_error_to_query_err(e).into())
872 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
873 #[cfg(feature = "sqlx-sqlite")]
874 QueryResultRow::SqlxSqlite(row) => row
875 .try_get::<Option<String>, _>(idx.as_sqlx_sqlite_index())
876 .map_err(|e| sqlx_error_to_query_err(e).into())
877 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
878 #[cfg(feature = "mock")]
879 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
880 debug_print!("{:#?}", e.to_string());
881 err_null_idx_col(idx)
882 }),
883 #[cfg(feature = "proxy")]
884 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
885 debug_print!("{:#?}", e.to_string());
886 err_null_idx_col(idx)
887 }),
888 #[allow(unreachable_patterns)]
889 _ => unreachable!(),
890 }
891 }
892}
893
894#[allow(dead_code)]
895fn err_null_idx_col<I: ColIdx>(idx: I) -> TryGetError {
896 TryGetError::Null(format!("{idx:?}"))
897}
898
899#[cfg(feature = "postgres-array")]
900mod postgres_array {
901 use super::*;
902
903 #[allow(unused_macros)]
904 macro_rules! try_getable_postgres_array {
905 ( $type: ty ) => {
906 #[allow(unused_variables)]
907 impl TryGetable for Vec<$type> {
908 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
909 match &res.row {
910 #[cfg(feature = "sqlx-mysql")]
911 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
912 "{} unsupported by sqlx-mysql",
913 stringify!($type)
914 ))
915 .into()),
916 #[cfg(feature = "sqlx-postgres")]
917 QueryResultRow::SqlxPostgres(row) => row
918 .try_get::<Option<Vec<$type>>, _>(idx.as_sqlx_postgres_index())
919 .map_err(|e| sqlx_error_to_query_err(e).into())
920 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
921 #[cfg(feature = "sqlx-sqlite")]
922 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
923 "{} unsupported by sqlx-sqlite",
924 stringify!($type)
925 ))
926 .into()),
927 #[cfg(feature = "mock")]
928 #[allow(unused_variables)]
929 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
930 debug_print!("{:#?}", e.to_string());
931 err_null_idx_col(idx)
932 }),
933 #[cfg(feature = "proxy")]
934 #[allow(unused_variables)]
935 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
936 debug_print!("{:#?}", e.to_string());
937 err_null_idx_col(idx)
938 }),
939 #[allow(unreachable_patterns)]
940 _ => unreachable!(),
941 }
942 }
943 }
944 };
945 }
946
947 try_getable_postgres_array!(bool);
948 try_getable_postgres_array!(i8);
949 try_getable_postgres_array!(i16);
950 try_getable_postgres_array!(i32);
951 try_getable_postgres_array!(i64);
952 try_getable_postgres_array!(f32);
953 try_getable_postgres_array!(f64);
954 try_getable_postgres_array!(String);
955 try_getable_postgres_array!(Vec<u8>);
956
957 #[cfg(feature = "with-json")]
958 try_getable_postgres_array!(serde_json::Value);
959
960 #[cfg(feature = "with-chrono")]
961 try_getable_postgres_array!(chrono::NaiveDate);
962
963 #[cfg(feature = "with-chrono")]
964 try_getable_postgres_array!(chrono::NaiveTime);
965
966 #[cfg(feature = "with-chrono")]
967 try_getable_postgres_array!(chrono::NaiveDateTime);
968
969 #[cfg(feature = "with-chrono")]
970 try_getable_postgres_array!(chrono::DateTime<chrono::FixedOffset>);
971
972 #[cfg(feature = "with-chrono")]
973 try_getable_postgres_array!(chrono::DateTime<chrono::Utc>);
974
975 #[cfg(feature = "with-chrono")]
976 try_getable_postgres_array!(chrono::DateTime<chrono::Local>);
977
978 #[cfg(feature = "with-time")]
979 try_getable_postgres_array!(time::Date);
980
981 #[cfg(feature = "with-time")]
982 try_getable_postgres_array!(time::Time);
983
984 #[cfg(feature = "with-time")]
985 try_getable_postgres_array!(time::PrimitiveDateTime);
986
987 #[cfg(feature = "with-time")]
988 try_getable_postgres_array!(time::OffsetDateTime);
989
990 #[cfg(feature = "with-rust_decimal")]
991 try_getable_postgres_array!(rust_decimal::Decimal);
992
993 #[cfg(feature = "with-bigdecimal")]
994 try_getable_postgres_array!(bigdecimal::BigDecimal);
995
996 #[cfg(feature = "with-ipnetwork")]
997 try_getable_postgres_array!(ipnetwork::IpNetwork);
998
999 #[allow(unused_macros)]
1000 macro_rules! try_getable_postgres_array_uuid {
1001 ( $type: ty, $conversion_fn: expr ) => {
1002 #[allow(unused_variables, unreachable_code)]
1003 impl TryGetable for Vec<$type> {
1004 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1005 let res: Result<Vec<uuid::Uuid>, TryGetError> = match &res.row {
1006 #[cfg(feature = "sqlx-mysql")]
1007 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
1008 "{} unsupported by sqlx-mysql",
1009 stringify!($type)
1010 ))
1011 .into()),
1012 #[cfg(feature = "sqlx-postgres")]
1013 QueryResultRow::SqlxPostgres(row) => row
1014 .try_get::<Option<Vec<uuid::Uuid>>, _>(idx.as_sqlx_postgres_index())
1015 .map_err(|e| sqlx_error_to_query_err(e).into())
1016 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1017 #[cfg(feature = "sqlx-sqlite")]
1018 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1019 "{} unsupported by sqlx-sqlite",
1020 stringify!($type)
1021 ))
1022 .into()),
1023 #[cfg(feature = "mock")]
1024 QueryResultRow::Mock(row) => {
1025 row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1026 debug_print!("{:#?}", e.to_string());
1027 err_null_idx_col(idx)
1028 })
1029 }
1030 #[cfg(feature = "proxy")]
1031 QueryResultRow::Proxy(row) => {
1032 row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1033 debug_print!("{:#?}", e.to_string());
1034 err_null_idx_col(idx)
1035 })
1036 }
1037 #[allow(unreachable_patterns)]
1038 _ => unreachable!(),
1039 };
1040 res.map(|vec| vec.into_iter().map($conversion_fn).collect())
1041 }
1042 }
1043 };
1044 }
1045
1046 #[cfg(feature = "with-uuid")]
1047 try_getable_postgres_array_uuid!(uuid::Uuid, Into::into);
1048
1049 #[cfg(feature = "with-uuid")]
1050 try_getable_postgres_array_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
1051
1052 #[cfg(feature = "with-uuid")]
1053 try_getable_postgres_array_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
1054
1055 #[cfg(feature = "with-uuid")]
1056 try_getable_postgres_array_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
1057
1058 #[cfg(feature = "with-uuid")]
1059 try_getable_postgres_array_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
1060
1061 impl TryGetable for Vec<u32> {
1062 #[allow(unused_variables)]
1063 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1064 match &res.row {
1065 #[cfg(feature = "sqlx-mysql")]
1066 QueryResultRow::SqlxMySql(_) => {
1067 Err(type_err(format!("{} unsupported by sqlx-mysql", stringify!($type))).into())
1068 }
1069 #[cfg(feature = "sqlx-postgres")]
1070 QueryResultRow::SqlxPostgres(row) => {
1071 use sqlx::postgres::types::Oid;
1072 row.try_get::<Option<Vec<Oid>>, _>(idx.as_sqlx_postgres_index())
1075 .map_err(|e| sqlx_error_to_query_err(e).into())
1076 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
1077 .map(|oids| oids.into_iter().map(|oid| oid.0).collect())
1078 }
1079 #[cfg(feature = "sqlx-sqlite")]
1080 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1081 "{} unsupported by sqlx-sqlite",
1082 stringify!($type)
1083 ))
1084 .into()),
1085 #[cfg(feature = "mock")]
1086 #[allow(unused_variables)]
1087 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1088 debug_print!("{:#?}", e.to_string());
1089 err_null_idx_col(idx)
1090 }),
1091 #[cfg(feature = "proxy")]
1092 #[allow(unused_variables)]
1093 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
1094 debug_print!("{:#?}", e.to_string());
1095 err_null_idx_col(idx)
1096 }),
1097 #[allow(unreachable_patterns)]
1098 _ => unreachable!(),
1099 }
1100 }
1101 }
1102}
1103
1104#[cfg(feature = "postgres-vector")]
1105impl TryGetable for pgvector::Vector {
1106 #[allow(unused_variables)]
1107 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1108 match &res.row {
1109 #[cfg(feature = "sqlx-mysql")]
1110 QueryResultRow::SqlxMySql(_) => {
1111 Err(type_err("Vector unsupported by sqlx-mysql").into())
1112 }
1113 #[cfg(feature = "sqlx-postgres")]
1114 QueryResultRow::SqlxPostgres(row) => row
1115 .try_get::<Option<pgvector::Vector>, _>(idx.as_sqlx_postgres_index())
1116 .map_err(|e| sqlx_error_to_query_err(e).into())
1117 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1118 #[cfg(feature = "sqlx-sqlite")]
1119 QueryResultRow::SqlxSqlite(_) => {
1120 Err(type_err("Vector unsupported by sqlx-sqlite").into())
1121 }
1122 #[cfg(feature = "mock")]
1123 QueryResultRow::Mock(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1124 debug_print!("{:#?}", e.to_string());
1125 err_null_idx_col(idx)
1126 }),
1127 #[cfg(feature = "proxy")]
1128 QueryResultRow::Proxy(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1129 debug_print!("{:#?}", e.to_string());
1130 err_null_idx_col(idx)
1131 }),
1132 #[allow(unreachable_patterns)]
1133 _ => unreachable!(),
1134 }
1135 }
1136}
1137
1138pub trait TryGetableMany: Sized {
1142 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>;
1144
1145 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError>;
1147
1148 fn find_by_statement<C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<Self, C>>
1206 where
1207 C: strum::IntoEnumIterator + sea_query::Iden,
1208 {
1209 SelectorRaw::<SelectGetableValue<Self, C>>::with_columns(stmt)
1210 }
1211}
1212
1213impl<T> TryGetableMany for T
1214where
1215 T: TryGetable,
1216{
1217 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1218 try_get_many_with_slice_len_of(1, cols)?;
1219 T::try_get(res, pre, &cols[0])
1220 }
1221
1222 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1223 T::try_get_by_index(res, 0)
1224 }
1225}
1226
1227impl<T> TryGetableMany for (T,)
1228where
1229 T: TryGetableMany,
1230{
1231 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1232 T::try_get_many(res, pre, cols).map(|r| (r,))
1233 }
1234
1235 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1236 T::try_get_many_by_index(res).map(|r| (r,))
1237 }
1238}
1239
1240macro_rules! impl_try_get_many {
1241 ( $LEN:expr, $($T:ident : $N:expr),+ $(,)? ) => {
1242 impl< $($T),+ > TryGetableMany for ( $($T),+ )
1243 where
1244 $($T: TryGetable),+
1245 {
1246 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1247 try_get_many_with_slice_len_of($LEN, cols)?;
1248 Ok((
1249 $($T::try_get(res, pre, &cols[$N])?),+
1250 ))
1251 }
1252
1253 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1254 Ok((
1255 $($T::try_get_by_index(res, $N)?),+
1256 ))
1257 }
1258 }
1259 };
1260}
1261
1262#[rustfmt::skip]
1263mod impl_try_get_many {
1264 use super::*;
1265
1266 impl_try_get_many!( 2, T0:0, T1:1);
1267 impl_try_get_many!( 3, T0:0, T1:1, T2:2);
1268 impl_try_get_many!( 4, T0:0, T1:1, T2:2, T3:3);
1269 impl_try_get_many!( 5, T0:0, T1:1, T2:2, T3:3, T4:4);
1270 impl_try_get_many!( 6, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
1271 impl_try_get_many!( 7, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
1272 impl_try_get_many!( 8, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
1273 impl_try_get_many!( 9, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
1274 impl_try_get_many!(10, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9);
1275 impl_try_get_many!(11, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9, T10:10);
1276 impl_try_get_many!(12, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9, T10:10, T11:11);
1277}
1278
1279fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
1280 if cols.len() < len {
1281 Err(type_err(format!(
1282 "Expect {} column names supplied but got slice of length {}",
1283 len,
1284 cols.len()
1285 ))
1286 .into())
1287 } else {
1288 Ok(())
1289 }
1290}
1291
1292pub trait TryGetableArray: Sized {
1298 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
1300}
1301
1302impl<T> TryGetable for Vec<T>
1303where
1304 T: TryGetableArray,
1305{
1306 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1307 T::try_get_by(res, index)
1308 }
1309}
1310
1311#[cfg(feature = "with-json")]
1315pub trait TryGetableFromJson: Sized
1316where
1317 for<'de> Self: serde::Deserialize<'de>,
1318{
1319 #[allow(unused_variables, unreachable_code)]
1321 fn try_get_from_json<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1322 match &res.row {
1323 #[cfg(feature = "sqlx-mysql")]
1324 QueryResultRow::SqlxMySql(row) => row
1325 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
1326 .map_err(|e| sqlx_error_to_query_err(e).into())
1327 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1328 #[cfg(feature = "sqlx-postgres")]
1329 QueryResultRow::SqlxPostgres(row) => row
1330 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
1331 .map_err(|e| sqlx_error_to_query_err(e).into())
1332 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1333 #[cfg(feature = "sqlx-sqlite")]
1334 QueryResultRow::SqlxSqlite(row) => row
1335 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
1336 .map_err(|e| sqlx_error_to_query_err(e).into())
1337 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1338 #[cfg(feature = "mock")]
1339 QueryResultRow::Mock(row) => row
1340 .try_get::<serde_json::Value, I>(idx)
1341 .map_err(|e| {
1342 debug_print!("{:#?}", e.to_string());
1343 err_null_idx_col(idx)
1344 })
1345 .and_then(|json| {
1346 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1347 }),
1348 #[cfg(feature = "proxy")]
1349 QueryResultRow::Proxy(row) => row
1350 .try_get::<serde_json::Value, I>(idx)
1351 .map_err(|e| {
1352 debug_print!("{:#?}", e.to_string());
1353 err_null_idx_col(idx)
1354 })
1355 .and_then(|json| {
1356 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1357 }),
1358 #[allow(unreachable_patterns)]
1359 _ => unreachable!(),
1360 }
1361 }
1362
1363 fn from_json_vec(value: serde_json::Value) -> Result<Vec<Self>, TryGetError> {
1365 match value {
1366 serde_json::Value::Array(values) => {
1367 let mut res = Vec::new();
1368 for item in values {
1369 res.push(serde_json::from_value(item).map_err(crate::error::json_err)?);
1370 }
1371 Ok(res)
1372 }
1373 _ => Err(TryGetError::DbErr(DbErr::Json(
1374 "Value is not an Array".to_owned(),
1375 ))),
1376 }
1377 }
1378}
1379
1380#[cfg(feature = "with-json")]
1381impl<T> TryGetable for T
1382where
1383 T: TryGetableFromJson,
1384{
1385 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1386 T::try_get_from_json(res, index)
1387 }
1388}
1389
1390#[cfg(feature = "with-json")]
1391impl<T> TryGetableArray for T
1392where
1393 T: TryGetableFromJson,
1394{
1395 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<T>, TryGetError> {
1396 T::from_json_vec(serde_json::Value::try_get_by(res, index)?)
1397 }
1398}
1399
1400pub trait TryFromU64: Sized {
1403 fn try_from_u64(n: u64) -> Result<Self, DbErr>;
1405}
1406
1407macro_rules! try_from_u64_err {
1408 ( $type: ty ) => {
1409 impl TryFromU64 for $type {
1410 fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1411 Err(DbErr::ConvertFromU64(stringify!($type)))
1412 }
1413 }
1414 };
1415
1416 ( $($gen_type: ident),* ) => {
1417 impl<$( $gen_type, )*> TryFromU64 for ($( $gen_type, )*)
1418 where
1419 $( $gen_type: TryFromU64, )*
1420 {
1421 fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1422 Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*)))
1423 }
1424 }
1425 };
1426}
1427
1428#[rustfmt::skip]
1429mod try_from_u64_err {
1430 use super::*;
1431
1432 try_from_u64_err!(T0, T1);
1433 try_from_u64_err!(T0, T1, T2);
1434 try_from_u64_err!(T0, T1, T2, T3);
1435 try_from_u64_err!(T0, T1, T2, T3, T4);
1436 try_from_u64_err!(T0, T1, T2, T3, T4, T5);
1437 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6);
1438 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7);
1439 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
1440 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
1441 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
1442 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
1443}
1444
1445macro_rules! try_from_u64_numeric {
1446 ( $type: ty ) => {
1447 impl TryFromU64 for $type {
1448 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1449 use std::convert::TryInto;
1450 n.try_into().map_err(|e| DbErr::TryIntoErr {
1451 from: stringify!(u64),
1452 into: stringify!($type),
1453 source: Box::new(e),
1454 })
1455 }
1456 }
1457 };
1458}
1459
1460try_from_u64_numeric!(i8);
1461try_from_u64_numeric!(i16);
1462try_from_u64_numeric!(i32);
1463try_from_u64_numeric!(i64);
1464try_from_u64_numeric!(u8);
1465try_from_u64_numeric!(u16);
1466try_from_u64_numeric!(u32);
1467try_from_u64_numeric!(u64);
1468
1469macro_rules! try_from_u64_string {
1470 ( $type: ty ) => {
1471 impl TryFromU64 for $type {
1472 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1473 Ok(n.to_string())
1474 }
1475 }
1476 };
1477}
1478
1479try_from_u64_string!(String);
1480
1481try_from_u64_err!(bool);
1482try_from_u64_err!(f32);
1483try_from_u64_err!(f64);
1484try_from_u64_err!(Vec<u8>);
1485
1486#[cfg(feature = "with-json")]
1487try_from_u64_err!(serde_json::Value);
1488
1489#[cfg(feature = "with-chrono")]
1490try_from_u64_err!(chrono::NaiveDate);
1491
1492#[cfg(feature = "with-chrono")]
1493try_from_u64_err!(chrono::NaiveTime);
1494
1495#[cfg(feature = "with-chrono")]
1496try_from_u64_err!(chrono::NaiveDateTime);
1497
1498#[cfg(feature = "with-chrono")]
1499try_from_u64_err!(chrono::DateTime<chrono::FixedOffset>);
1500
1501#[cfg(feature = "with-chrono")]
1502try_from_u64_err!(chrono::DateTime<chrono::Utc>);
1503
1504#[cfg(feature = "with-chrono")]
1505try_from_u64_err!(chrono::DateTime<chrono::Local>);
1506
1507#[cfg(feature = "with-time")]
1508try_from_u64_err!(time::Date);
1509
1510#[cfg(feature = "with-time")]
1511try_from_u64_err!(time::Time);
1512
1513#[cfg(feature = "with-time")]
1514try_from_u64_err!(time::PrimitiveDateTime);
1515
1516#[cfg(feature = "with-time")]
1517try_from_u64_err!(time::OffsetDateTime);
1518
1519#[cfg(feature = "with-rust_decimal")]
1520try_from_u64_err!(rust_decimal::Decimal);
1521
1522#[cfg(feature = "with-uuid")]
1523try_from_u64_err!(uuid::Uuid);
1524
1525#[cfg(feature = "with-uuid")]
1526try_from_u64_err!(uuid::fmt::Simple);
1527
1528#[cfg(feature = "with-uuid")]
1529try_from_u64_err!(uuid::fmt::Hyphenated);
1530
1531#[cfg(feature = "with-uuid")]
1532try_from_u64_err!(uuid::fmt::Urn);
1533
1534#[cfg(feature = "with-uuid")]
1535try_from_u64_err!(uuid::fmt::Braced);
1536
1537#[cfg(feature = "with-ipnetwork")]
1538try_from_u64_err!(ipnetwork::IpNetwork);
1539
1540#[cfg(test)]
1541mod tests {
1542 use super::*;
1543 use crate::RuntimeErr;
1544 use sea_query::Value;
1545 use std::collections::BTreeMap;
1546
1547 #[test]
1548 fn from_try_get_error() {
1549 let try_get_error = TryGetError::DbErr(DbErr::Query(RuntimeErr::Internal(
1551 "expected error message".to_owned(),
1552 )));
1553 assert_eq!(
1554 DbErr::from(try_get_error),
1555 DbErr::Query(RuntimeErr::Internal("expected error message".to_owned()))
1556 );
1557
1558 let try_get_error = TryGetError::Null("column".to_owned());
1560 let expected = "A null value was encountered while decoding column".to_owned();
1561 assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected));
1562 }
1563
1564 #[test]
1565 fn build_with_query() {
1566 use sea_orm::{DbBackend, Statement};
1567 use sea_query::*;
1568
1569 let base_query = SelectStatement::new()
1570 .column("id")
1571 .expr(1i32)
1572 .column("next")
1573 .column("value")
1574 .from("table")
1575 .to_owned();
1576
1577 let cte_referencing = SelectStatement::new()
1578 .column("id")
1579 .expr(Expr::col("depth").add(1i32))
1580 .column("next")
1581 .column("value")
1582 .from("table")
1583 .join(
1584 JoinType::InnerJoin,
1585 "cte_traversal",
1586 Expr::col(("cte_traversal", "next")).equals(("table", "id")),
1587 )
1588 .to_owned();
1589
1590 let common_table_expression = CommonTableExpression::new()
1591 .query(
1592 base_query
1593 .clone()
1594 .union(UnionType::All, cte_referencing)
1595 .to_owned(),
1596 )
1597 .columns(["id", "depth", "next", "value"])
1598 .table_name("cte_traversal")
1599 .to_owned();
1600
1601 let select = SelectStatement::new()
1602 .column(ColumnRef::Asterisk)
1603 .from("cte_traversal")
1604 .to_owned();
1605
1606 let with_clause = WithClause::new()
1607 .recursive(true)
1608 .cte(common_table_expression)
1609 .cycle(Cycle::new_from_expr_set_using(
1610 SimpleExpr::Column(ColumnRef::Column("id".into_iden())),
1611 "looped",
1612 "traversal_path",
1613 ))
1614 .to_owned();
1615
1616 let with_query = select.with(with_clause).to_owned();
1617
1618 assert_eq!(
1619 DbBackend::MySql.build(&with_query),
1620 Statement::from_sql_and_values(
1621 DbBackend::MySql,
1622 r#"WITH RECURSIVE `cte_traversal` (`id`, `depth`, `next`, `value`) AS (SELECT `id`, ?, `next`, `value` FROM `table` UNION ALL (SELECT `id`, `depth` + ?, `next`, `value` FROM `table` INNER JOIN `cte_traversal` ON `cte_traversal`.`next` = `table`.`id`)) SELECT * FROM `cte_traversal`"#,
1623 [1.into(), 1.into()]
1624 )
1625 );
1626 }
1627
1628 #[test]
1629 fn column_names_from_query_result() {
1630 let mut values = BTreeMap::new();
1631 values.insert("id".to_string(), Value::Int(Some(1)));
1632 values.insert(
1633 "name".to_string(),
1634 Value::String(Some(Box::new("Abc".to_owned()))),
1635 );
1636 let query_result = QueryResult {
1637 row: QueryResultRow::Mock(crate::MockRow { values }),
1638 };
1639 assert_eq!(
1640 query_result.column_names(),
1641 vec!["id".to_owned(), "name".to_owned()]
1642 );
1643 }
1644}