1pub use crate::error::TryGetError;
2use crate::{
3 SelectGetableValue, SelectorRaw, Statement,
4 error::{DbErr, type_err},
5};
6use std::{
7 collections::{BTreeMap, HashMap},
8 fmt::Debug,
9 hash::Hash,
10 marker::PhantomData,
11 sync::Arc,
12};
13
14#[cfg(any(feature = "mock", feature = "proxy"))]
15use crate::debug_print;
16
17#[cfg(feature = "sqlx-dep")]
18use crate::driver::*;
19#[cfg(feature = "sqlx-dep")]
20use sqlx::Row;
21#[cfg(feature = "sqlx-postgres")]
22use sqlx::{TypeInfo, ValueRef};
23
24#[derive(Debug)]
31pub struct QueryResult {
32 pub(crate) row: QueryResultRow,
33}
34
35#[allow(clippy::enum_variant_names)]
36pub(crate) enum QueryResultRow {
37 #[cfg(feature = "sqlx-mysql")]
38 SqlxMySql(sqlx::mysql::MySqlRow),
39 #[cfg(feature = "sqlx-postgres")]
40 SqlxPostgres(sqlx::postgres::PgRow),
41 #[cfg(feature = "sqlx-sqlite")]
42 SqlxSqlite(sqlx::sqlite::SqliteRow),
43 #[cfg(feature = "rusqlite")]
44 Rusqlite(crate::driver::rusqlite::RusqliteRow),
45 #[cfg(feature = "mock")]
46 Mock(crate::MockRow),
47 #[cfg(feature = "proxy")]
48 Proxy(crate::ProxyRow),
49}
50
51pub trait TryGetable: Sized {
58 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError>;
60
61 fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
64 if pre.is_empty() {
65 Self::try_get_by(res, col)
66 } else {
67 Self::try_get_by(res, format!("{pre}{col}").as_str())
68 }
69 }
70
71 fn try_get_by_index(res: &QueryResult, index: usize) -> Result<Self, TryGetError> {
73 Self::try_get_by(res, index)
74 }
75}
76
77impl From<TryGetError> for DbErr {
78 fn from(e: TryGetError) -> DbErr {
79 match e {
80 TryGetError::DbErr(e) => e,
81 TryGetError::Null(s) => {
82 type_err(format!("A null value was encountered while decoding {s}"))
83 }
84 }
85 }
86}
87
88impl From<DbErr> for TryGetError {
89 fn from(e: DbErr) -> TryGetError {
90 Self::DbErr(e)
91 }
92}
93
94impl QueryResult {
97 #[doc(hidden)]
98 #[cfg(feature = "sqlx-postgres")]
99 pub fn try_get_from_sqlx_postgres<T, I>(&self, idx: I) -> Option<Result<T, TryGetError>>
100 where
101 T: sqlx::Type<sqlx::Postgres> + for<'r> sqlx::Decode<'r, sqlx::Postgres>,
102 I: ColIdx,
103 {
104 match &self.row {
105 QueryResultRow::SqlxPostgres(row) => {
106 let value = match row.try_get_raw(idx.as_sqlx_postgres_index()) {
107 Ok(value) => value,
108 Err(err) => return Some(Err(sqlx_error_to_query_err(err).into())),
109 };
110
111 if !value.is_null() {
112 let ty = value.type_info();
113 if !ty.is_null() && !T::compatible(&ty) {
114 return None;
115 }
116 }
117
118 Some(
119 row.try_get::<Option<T>, _>(idx.as_sqlx_postgres_index())
120 .map_err(|e| sqlx_error_to_query_err(e).into())
121 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
122 )
123 }
124 _ => None,
125 }
126 }
127
128 pub fn try_get_by<T, I>(&self, index: I) -> Result<T, DbErr>
130 where
131 T: TryGetable,
132 I: ColIdx,
133 {
134 Ok(T::try_get_by(self, index)?)
135 }
136
137 pub fn try_get_by_nullable<T, I>(&self, index: I) -> Result<T, TryGetError>
139 where
140 T: TryGetable,
141 I: ColIdx,
142 {
143 T::try_get_by(self, index)
144 }
145
146 pub fn try_get<T>(&self, pre: &str, col: &str) -> Result<T, DbErr>
148 where
149 T: TryGetable,
150 {
151 Ok(T::try_get(self, pre, col)?)
152 }
153
154 pub fn try_get_nullable<T>(&self, pre: &str, col: &str) -> Result<T, TryGetError>
156 where
157 T: TryGetable,
158 {
159 T::try_get(self, pre, col)
160 }
161
162 pub fn try_get_by_index<T>(&self, idx: usize) -> Result<T, DbErr>
164 where
165 T: TryGetable,
166 {
167 Ok(T::try_get_by_index(self, idx)?)
168 }
169
170 pub fn try_get_by_index_nullable<T>(&self, idx: usize) -> Result<T, TryGetError>
172 where
173 T: TryGetable,
174 {
175 T::try_get_by_index(self, idx)
176 }
177
178 pub fn try_get_many<T>(&self, pre: &str, cols: &[String]) -> Result<T, DbErr>
180 where
181 T: TryGetableMany,
182 {
183 Ok(T::try_get_many(self, pre, cols)?)
184 }
185
186 pub fn try_get_many_by_index<T>(&self) -> Result<T, DbErr>
188 where
189 T: TryGetableMany,
190 {
191 Ok(T::try_get_many_by_index(self)?)
192 }
193
194 pub fn column_names(&self) -> Vec<String> {
196 #[cfg(feature = "sqlx-dep")]
197 use sqlx::Column;
198
199 match &self.row {
200 #[cfg(feature = "sqlx-mysql")]
201 QueryResultRow::SqlxMySql(row) => {
202 row.columns().iter().map(|c| c.name().to_string()).collect()
203 }
204 #[cfg(feature = "sqlx-postgres")]
205 QueryResultRow::SqlxPostgres(row) => {
206 row.columns().iter().map(|c| c.name().to_string()).collect()
207 }
208 #[cfg(feature = "sqlx-sqlite")]
209 QueryResultRow::SqlxSqlite(row) => {
210 row.columns().iter().map(|c| c.name().to_string()).collect()
211 }
212 #[cfg(feature = "rusqlite")]
213 QueryResultRow::Rusqlite(row) => row.columns().iter().map(|c| c.to_string()).collect(),
214 #[cfg(feature = "mock")]
215 QueryResultRow::Mock(row) => row
216 .clone()
217 .into_column_value_tuples()
218 .map(|(c, _)| c.to_string())
219 .collect(),
220 #[cfg(feature = "proxy")]
221 QueryResultRow::Proxy(row) => row
222 .clone()
223 .into_column_value_tuples()
224 .map(|(c, _)| c.to_string())
225 .collect(),
226 #[allow(unreachable_patterns)]
227 _ => unreachable!(),
228 }
229 }
230
231 #[cfg(feature = "sqlx-mysql")]
233 pub fn try_as_mysql_row(&self) -> Option<&sqlx::mysql::MySqlRow> {
234 match &self.row {
235 QueryResultRow::SqlxMySql(mysql_row) => Some(mysql_row),
236 #[allow(unreachable_patterns)]
237 _ => None,
238 }
239 }
240
241 #[cfg(feature = "sqlx-postgres")]
243 pub fn try_as_pg_row(&self) -> Option<&sqlx::postgres::PgRow> {
244 match &self.row {
245 QueryResultRow::SqlxPostgres(pg_row) => Some(pg_row),
246 #[allow(unreachable_patterns)]
247 _ => None,
248 }
249 }
250
251 #[cfg(feature = "sqlx-sqlite")]
253 pub fn try_as_sqlite_row(&self) -> Option<&sqlx::sqlite::SqliteRow> {
254 match &self.row {
255 QueryResultRow::SqlxSqlite(sqlite_row) => Some(sqlite_row),
256 #[allow(unreachable_patterns)]
257 _ => None,
258 }
259 }
260
261 #[cfg(feature = "mock")]
263 pub fn try_as_mock_row(&self) -> Option<&crate::MockRow> {
264 match &self.row {
265 QueryResultRow::Mock(mock_row) => Some(mock_row),
266 #[allow(unreachable_patterns)]
267 _ => None,
268 }
269 }
270
271 #[cfg(feature = "proxy")]
273 pub fn try_as_proxy_row(&self) -> Option<&crate::ProxyRow> {
274 match &self.row {
275 QueryResultRow::Proxy(proxy_row) => Some(proxy_row),
276 #[allow(unreachable_patterns)]
277 _ => None,
278 }
279 }
280}
281
282#[allow(unused_variables)]
283impl Debug for QueryResultRow {
284 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
285 match self {
286 #[cfg(feature = "sqlx-mysql")]
287 Self::SqlxMySql(row) => write!(f, "{row:?}"),
288 #[cfg(feature = "sqlx-postgres")]
289 Self::SqlxPostgres(_) => write!(f, "QueryResultRow::SqlxPostgres cannot be inspected"),
290 #[cfg(feature = "sqlx-sqlite")]
291 Self::SqlxSqlite(_) => write!(f, "QueryResultRow::SqlxSqlite cannot be inspected"),
292 #[cfg(feature = "rusqlite")]
293 Self::Rusqlite(row) => write!(f, "{row:?}"),
294 #[cfg(feature = "mock")]
295 Self::Mock(row) => write!(f, "{row:?}"),
296 #[cfg(feature = "proxy")]
297 Self::Proxy(row) => write!(f, "{row:?}"),
298 #[allow(unreachable_patterns)]
299 _ => unreachable!(),
300 }
301 }
302}
303
304impl<T: TryGetable> TryGetable for Option<T> {
307 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
308 match T::try_get_by(res, index) {
309 Ok(v) => Ok(Some(v)),
310 Err(TryGetError::Null(_)) => Ok(None),
311 #[cfg(feature = "sqlx-dep")]
312 Err(TryGetError::DbErr(DbErr::Query(crate::RuntimeErr::SqlxError(err)))) => {
313 use std::ops::Deref;
314 match err.deref() {
315 sqlx::Error::ColumnNotFound(_) => Ok(None),
316 _ => Err(TryGetError::DbErr(DbErr::Query(
317 crate::RuntimeErr::SqlxError(err),
318 ))),
319 }
320 }
321 Err(e) => Err(e),
322 }
323 }
324}
325
326pub trait ColIdx: Debug + Copy {
328 #[cfg(feature = "sqlx-mysql")]
329 type SqlxMySqlIndex: sqlx::ColumnIndex<sqlx::mysql::MySqlRow>;
331 #[cfg(feature = "sqlx-postgres")]
332 type SqlxPostgresIndex: sqlx::ColumnIndex<sqlx::postgres::PgRow>;
334 #[cfg(feature = "sqlx-sqlite")]
335 type SqlxSqliteIndex: sqlx::ColumnIndex<sqlx::sqlite::SqliteRow>;
337
338 #[cfg(feature = "sqlx-mysql")]
339 fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex;
341 #[cfg(feature = "sqlx-postgres")]
342 fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex;
344 #[cfg(feature = "sqlx-sqlite")]
345 fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex;
347
348 fn as_str(&self) -> Option<&str>;
350 fn as_usize(&self) -> Option<&usize>;
352}
353
354impl ColIdx for &str {
355 #[cfg(feature = "sqlx-mysql")]
356 type SqlxMySqlIndex = Self;
357 #[cfg(feature = "sqlx-postgres")]
358 type SqlxPostgresIndex = Self;
359 #[cfg(feature = "sqlx-sqlite")]
360 type SqlxSqliteIndex = Self;
361
362 #[cfg(feature = "sqlx-mysql")]
363 #[inline]
364 fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
365 self
366 }
367 #[cfg(feature = "sqlx-postgres")]
368 #[inline]
369 fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
370 self
371 }
372 #[cfg(feature = "sqlx-sqlite")]
373 #[inline]
374 fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
375 self
376 }
377
378 #[inline]
379 fn as_str(&self) -> Option<&str> {
380 Some(self)
381 }
382 #[inline]
383 fn as_usize(&self) -> Option<&usize> {
384 None
385 }
386}
387
388impl ColIdx for usize {
389 #[cfg(feature = "sqlx-mysql")]
390 type SqlxMySqlIndex = Self;
391 #[cfg(feature = "sqlx-postgres")]
392 type SqlxPostgresIndex = Self;
393 #[cfg(feature = "sqlx-sqlite")]
394 type SqlxSqliteIndex = Self;
395
396 #[cfg(feature = "sqlx-mysql")]
397 #[inline]
398 fn as_sqlx_mysql_index(&self) -> Self::SqlxMySqlIndex {
399 *self
400 }
401 #[cfg(feature = "sqlx-postgres")]
402 #[inline]
403 fn as_sqlx_postgres_index(&self) -> Self::SqlxPostgresIndex {
404 *self
405 }
406 #[cfg(feature = "sqlx-sqlite")]
407 #[inline]
408 fn as_sqlx_sqlite_index(&self) -> Self::SqlxSqliteIndex {
409 *self
410 }
411
412 #[inline]
413 fn as_str(&self) -> Option<&str> {
414 None
415 }
416 #[inline]
417 fn as_usize(&self) -> Option<&usize> {
418 Some(self)
419 }
420}
421
422macro_rules! try_getable_all {
423 ( $type: ty ) => {
424 impl TryGetable for $type {
425 #[allow(unused_variables)]
426 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
427 match &res.row {
428 #[cfg(feature = "sqlx-mysql")]
429 QueryResultRow::SqlxMySql(row) => row
430 .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
431 .map_err(|e| sqlx_error_to_query_err(e).into())
432 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
433 #[cfg(feature = "sqlx-postgres")]
434 QueryResultRow::SqlxPostgres(row) => row
435 .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
436 .map_err(|e| sqlx_error_to_query_err(e).into())
437 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
438 #[cfg(feature = "sqlx-sqlite")]
439 QueryResultRow::SqlxSqlite(row) => row
440 .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
441 .map_err(|e| sqlx_error_to_query_err(e).into())
442 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
443 #[cfg(feature = "rusqlite")]
444 QueryResultRow::Rusqlite(row) => row
445 .try_get::<Option<$type>, _>(idx)
446 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
447 #[cfg(feature = "mock")]
448 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
449 debug_print!("{:#?}", e.to_string());
450 err_null_idx_col(idx)
451 }),
452 #[cfg(feature = "proxy")]
453 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
454 debug_print!("{:#?}", e.to_string());
455 err_null_idx_col(idx)
456 }),
457 #[allow(unreachable_patterns)]
458 _ => unreachable!(),
459 }
460 }
461 }
462 };
463}
464
465macro_rules! try_getable_unsigned {
466 ( $type: ty ) => {
467 impl TryGetable for $type {
468 #[allow(unused_variables)]
469 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
470 match &res.row {
471 #[cfg(feature = "sqlx-mysql")]
472 QueryResultRow::SqlxMySql(row) => row
473 .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
474 .map_err(|e| sqlx_error_to_query_err(e).into())
475 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
476 #[cfg(feature = "sqlx-postgres")]
477 QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
478 "{} unsupported by sqlx-postgres",
479 stringify!($type)
480 ))
481 .into()),
482 #[cfg(feature = "sqlx-sqlite")]
483 QueryResultRow::SqlxSqlite(row) => row
484 .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
485 .map_err(|e| sqlx_error_to_query_err(e).into())
486 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
487 #[cfg(feature = "rusqlite")]
488 QueryResultRow::Rusqlite(row) => row
489 .try_get::<Option<$type>, _>(idx)
490 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
491 #[cfg(feature = "mock")]
492 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
493 debug_print!("{:#?}", e.to_string());
494 err_null_idx_col(idx)
495 }),
496 #[cfg(feature = "proxy")]
497 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
498 debug_print!("{:#?}", e.to_string());
499 err_null_idx_col(idx)
500 }),
501 #[allow(unreachable_patterns)]
502 _ => unreachable!(),
503 }
504 }
505 }
506 };
507}
508
509macro_rules! try_getable_mysql {
510 ( $type: ty ) => {
511 impl TryGetable for $type {
512 #[allow(unused_variables)]
513 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
514 match &res.row {
515 #[cfg(feature = "sqlx-mysql")]
516 QueryResultRow::SqlxMySql(row) => row
517 .try_get::<Option<$type>, _>(idx.as_sqlx_mysql_index())
518 .map_err(|e| sqlx_error_to_query_err(e).into())
519 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
520 #[cfg(feature = "sqlx-postgres")]
521 QueryResultRow::SqlxPostgres(_) => Err(type_err(format!(
522 "{} unsupported by sqlx-postgres",
523 stringify!($type)
524 ))
525 .into()),
526 #[cfg(feature = "sqlx-sqlite")]
527 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
528 "{} unsupported by sqlx-sqlite",
529 stringify!($type)
530 ))
531 .into()),
532 #[cfg(feature = "rusqlite")]
533 QueryResultRow::Rusqlite(row) => Err(type_err(format!(
534 "{} unsupported by rusqlite",
535 stringify!($type)
536 ))
537 .into()),
538 #[cfg(feature = "mock")]
539 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
540 debug_print!("{:#?}", e.to_string());
541 err_null_idx_col(idx)
542 }),
543 #[cfg(feature = "proxy")]
544 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
545 debug_print!("{:#?}", e.to_string());
546 err_null_idx_col(idx)
547 }),
548 #[allow(unreachable_patterns)]
549 _ => unreachable!(),
550 }
551 }
552 }
553 };
554}
555
556#[allow(unused_macros)]
557macro_rules! try_getable_postgres {
558 ( $type: ty ) => {
559 impl TryGetable for $type {
560 #[allow(unused_variables)]
561 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
562 match &res.row {
563 #[cfg(feature = "sqlx-mysql")]
564 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
565 "{} unsupported by sqlx-mysql",
566 stringify!($type)
567 ))
568 .into()),
569 #[cfg(feature = "sqlx-postgres")]
570 QueryResultRow::SqlxPostgres(row) => row
571 .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
572 .map_err(|e| sqlx_error_to_query_err(e).into())
573 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
574 #[cfg(feature = "sqlx-sqlite")]
575 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
576 "{} unsupported by sqlx-sqlite",
577 stringify!($type)
578 ))
579 .into()),
580 #[cfg(feature = "rusqlite")]
581 QueryResultRow::Rusqlite(row) => Err(type_err(format!(
582 "{} unsupported by rusqlite",
583 stringify!($type)
584 ))
585 .into()),
586 #[cfg(feature = "mock")]
587 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
588 debug_print!("{:#?}", e.to_string());
589 err_null_idx_col(idx)
590 }),
591 #[cfg(feature = "proxy")]
592 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
593 debug_print!("{:#?}", e.to_string());
594 err_null_idx_col(idx)
595 }),
596 #[allow(unreachable_patterns)]
597 _ => unreachable!(),
598 }
599 }
600 }
601 };
602}
603
604#[allow(unused_macros)]
605macro_rules! try_getable_date_time {
606 ( $type: ty ) => {
607 impl TryGetable for $type {
608 #[allow(unused_variables)]
609 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
610 match &res.row {
611 #[cfg(feature = "sqlx-mysql")]
612 QueryResultRow::SqlxMySql(row) => {
613 use chrono::{DateTime, Utc};
614 row.try_get::<Option<DateTime<Utc>>, _>(idx.as_sqlx_mysql_index())
615 .map_err(|e| sqlx_error_to_query_err(e).into())
616 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
617 .map(|v| v.into())
618 }
619 #[cfg(feature = "sqlx-postgres")]
620 QueryResultRow::SqlxPostgres(row) => row
621 .try_get::<Option<$type>, _>(idx.as_sqlx_postgres_index())
622 .map_err(|e| sqlx_error_to_query_err(e).into())
623 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
624 #[cfg(feature = "sqlx-sqlite")]
625 QueryResultRow::SqlxSqlite(row) => row
626 .try_get::<Option<$type>, _>(idx.as_sqlx_sqlite_index())
627 .map_err(|e| sqlx_error_to_query_err(e).into())
628 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
629 #[cfg(feature = "rusqlite")]
630 QueryResultRow::Rusqlite(row) => row
631 .try_get::<Option<$type>, _>(idx)
632 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
633 .map(|v| v.into()),
634 #[cfg(feature = "mock")]
635 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
636 debug_print!("{:#?}", e.to_string());
637 err_null_idx_col(idx)
638 }),
639 #[cfg(feature = "proxy")]
640 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
641 debug_print!("{:#?}", e.to_string());
642 err_null_idx_col(idx)
643 }),
644 #[allow(unreachable_patterns)]
645 _ => unreachable!(),
646 }
647 }
648 }
649 };
650}
651
652try_getable_all!(bool);
653try_getable_all!(i8);
654try_getable_all!(i16);
655try_getable_all!(i32);
656try_getable_all!(i64);
657try_getable_unsigned!(u8);
658try_getable_unsigned!(u16);
659try_getable_mysql!(u64);
660try_getable_all!(f32);
661try_getable_all!(f64);
662try_getable_all!(Vec<u8>);
663
664#[cfg(feature = "with-json")]
665try_getable_all!(serde_json::Value);
666
667#[cfg(feature = "with-chrono")]
668try_getable_all!(chrono::NaiveDate);
669
670#[cfg(feature = "with-chrono")]
671try_getable_all!(chrono::NaiveTime);
672
673#[cfg(feature = "with-chrono")]
674try_getable_all!(chrono::NaiveDateTime);
675
676#[cfg(feature = "with-chrono")]
677try_getable_date_time!(chrono::DateTime<chrono::FixedOffset>);
678
679#[cfg(feature = "with-chrono")]
680try_getable_all!(chrono::DateTime<chrono::Utc>);
681
682#[cfg(feature = "with-chrono")]
683try_getable_all!(chrono::DateTime<chrono::Local>);
684
685#[cfg(feature = "with-time")]
686try_getable_all!(time::Date);
687
688#[cfg(feature = "with-time")]
689try_getable_all!(time::Time);
690
691#[cfg(feature = "with-time")]
692try_getable_all!(time::PrimitiveDateTime);
693
694#[cfg(feature = "with-time")]
695try_getable_all!(time::OffsetDateTime);
696
697#[cfg(feature = "with-rust_decimal")]
698use rust_decimal::Decimal;
699
700#[cfg(feature = "with-rust_decimal")]
701impl TryGetable for Decimal {
702 #[allow(unused_variables)]
703 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
704 match &res.row {
705 #[cfg(feature = "sqlx-mysql")]
706 QueryResultRow::SqlxMySql(row) => row
707 .try_get::<Option<Decimal>, _>(idx.as_sqlx_mysql_index())
708 .map_err(|e| sqlx_error_to_query_err(e).into())
709 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
710 #[cfg(feature = "sqlx-postgres")]
711 QueryResultRow::SqlxPostgres(row) => row
712 .try_get::<Option<Decimal>, _>(idx.as_sqlx_postgres_index())
713 .map_err(|e| sqlx_error_to_query_err(e).into())
714 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
715 #[cfg(feature = "sqlx-sqlite")]
716 QueryResultRow::SqlxSqlite(row) => {
717 let val: Option<f64> = row
718 .try_get(idx.as_sqlx_sqlite_index())
719 .map_err(sqlx_error_to_query_err)?;
720 match val {
721 Some(v) => Decimal::try_from(v).map_err(|e| {
722 DbErr::TryIntoErr {
723 from: "f64",
724 into: "Decimal",
725 source: Arc::new(e),
726 }
727 .into()
728 }),
729 None => Err(err_null_idx_col(idx)),
730 }
731 }
732 #[cfg(feature = "rusqlite")]
733 QueryResultRow::Rusqlite(row) => {
734 let val: Option<f64> = row.try_get(idx)?;
735 match val {
736 Some(v) => Decimal::try_from(v).map_err(|e| {
737 DbErr::TryIntoErr {
738 from: "f64",
739 into: "Decimal",
740 source: Arc::new(e),
741 }
742 .into()
743 }),
744 None => Err(err_null_idx_col(idx)),
745 }
746 }
747 #[cfg(feature = "mock")]
748 #[allow(unused_variables)]
749 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
750 debug_print!("{:#?}", e.to_string());
751 err_null_idx_col(idx)
752 }),
753 #[cfg(feature = "proxy")]
754 #[allow(unused_variables)]
755 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
756 debug_print!("{:#?}", e.to_string());
757 err_null_idx_col(idx)
758 }),
759 #[allow(unreachable_patterns)]
760 _ => unreachable!(),
761 }
762 }
763}
764
765#[cfg(feature = "with-bigdecimal")]
766use bigdecimal::BigDecimal;
767
768#[cfg(feature = "with-bigdecimal")]
769impl TryGetable for BigDecimal {
770 #[allow(unused_variables)]
771 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
772 match &res.row {
773 #[cfg(feature = "sqlx-mysql")]
774 QueryResultRow::SqlxMySql(row) => row
775 .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_mysql_index())
776 .map_err(|e| sqlx_error_to_query_err(e).into())
777 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
778 #[cfg(feature = "sqlx-postgres")]
779 QueryResultRow::SqlxPostgres(row) => row
780 .try_get::<Option<BigDecimal>, _>(idx.as_sqlx_postgres_index())
781 .map_err(|e| sqlx_error_to_query_err(e).into())
782 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
783 #[cfg(feature = "sqlx-sqlite")]
784 QueryResultRow::SqlxSqlite(row) => {
785 let val: Option<f64> = row
786 .try_get(idx.as_sqlx_sqlite_index())
787 .map_err(sqlx_error_to_query_err)?;
788 match val {
789 Some(v) => BigDecimal::try_from(v).map_err(|e| {
790 DbErr::TryIntoErr {
791 from: "f64",
792 into: "BigDecimal",
793 source: Arc::new(e),
794 }
795 .into()
796 }),
797 None => Err(err_null_idx_col(idx)),
798 }
799 }
800 #[cfg(feature = "rusqlite")]
801 QueryResultRow::Rusqlite(row) => {
802 let val: Option<f64> = row.try_get(idx)?;
803 match val {
804 Some(v) => BigDecimal::try_from(v).map_err(|e| {
805 DbErr::TryIntoErr {
806 from: "f64",
807 into: "BigDecimal",
808 source: Arc::new(e),
809 }
810 .into()
811 }),
812 None => Err(err_null_idx_col(idx)),
813 }
814 }
815 #[cfg(feature = "mock")]
816 #[allow(unused_variables)]
817 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
818 debug_print!("{:#?}", e.to_string());
819 err_null_idx_col(idx)
820 }),
821 #[cfg(feature = "proxy")]
822 #[allow(unused_variables)]
823 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
824 debug_print!("{:#?}", e.to_string());
825 err_null_idx_col(idx)
826 }),
827 #[allow(unreachable_patterns)]
828 _ => unreachable!(),
829 }
830 }
831}
832
833#[allow(unused_macros)]
834macro_rules! try_getable_uuid {
835 ( $type: ty, $conversion_fn: expr ) => {
836 #[allow(unused_variables, unreachable_code)]
837 impl TryGetable for $type {
838 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
839 let res: Result<uuid::Uuid, TryGetError> = match &res.row {
840 #[cfg(feature = "sqlx-mysql")]
841 QueryResultRow::SqlxMySql(row) => row
842 .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_mysql_index())
843 .map_err(|e| sqlx_error_to_query_err(e).into())
844 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
845 .or_else(|_| {
846 row.try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
849 .map_err(|e| sqlx_error_to_query_err(e).into())
850 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
851 .map(|bytes| {
852 String::from_utf8(bytes).map_err(|e| {
853 DbErr::TryIntoErr {
854 from: "Vec<u8>",
855 into: "String",
856 source: Arc::new(e),
857 }
858 .into()
859 })
860 })?
861 .and_then(|s| {
862 uuid::Uuid::parse_str(&s).map_err(|e| {
863 DbErr::TryIntoErr {
864 from: "String",
865 into: "uuid::Uuid",
866 source: Arc::new(e),
867 }
868 .into()
869 })
870 })
871 }),
872 #[cfg(feature = "sqlx-postgres")]
873 QueryResultRow::SqlxPostgres(row) => row
874 .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_postgres_index())
875 .map_err(|e| sqlx_error_to_query_err(e).into())
876 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
877 #[cfg(feature = "sqlx-sqlite")]
878 QueryResultRow::SqlxSqlite(row) => row
879 .try_get::<Option<uuid::Uuid>, _>(idx.as_sqlx_sqlite_index())
880 .map_err(|e| sqlx_error_to_query_err(e).into())
881 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
882 #[cfg(feature = "rusqlite")]
883 QueryResultRow::Rusqlite(row) => row
884 .try_get::<Option<uuid::Uuid>, _>(idx)
885 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
886 #[cfg(feature = "mock")]
887 #[allow(unused_variables)]
888 QueryResultRow::Mock(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
889 debug_print!("{:#?}", e.to_string());
890 err_null_idx_col(idx)
891 }),
892 #[cfg(feature = "proxy")]
893 #[allow(unused_variables)]
894 QueryResultRow::Proxy(row) => row.try_get::<uuid::Uuid, _>(idx).map_err(|e| {
895 debug_print!("{:#?}", e.to_string());
896 err_null_idx_col(idx)
897 }),
898 #[allow(unreachable_patterns)]
899 _ => unreachable!(),
900 };
901 res.map($conversion_fn)
902 }
903 }
904 };
905}
906
907#[cfg(feature = "with-uuid")]
908try_getable_uuid!(uuid::Uuid, Into::into);
909
910#[cfg(feature = "with-uuid")]
911try_getable_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
912
913#[cfg(feature = "with-uuid")]
914try_getable_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
915
916#[cfg(feature = "with-uuid")]
917try_getable_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
918
919#[cfg(feature = "with-uuid")]
920try_getable_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
921
922#[cfg(feature = "with-ipnetwork")]
923try_getable_postgres!(ipnetwork::IpNetwork);
924
925#[cfg(feature = "with-mac_address")]
926try_getable_postgres!(mac_address::MacAddress);
927
928impl TryGetable for u32 {
929 #[allow(unused_variables)]
930 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
931 match &res.row {
932 #[cfg(feature = "sqlx-mysql")]
933 QueryResultRow::SqlxMySql(row) => row
934 .try_get::<Option<u32>, _>(idx.as_sqlx_mysql_index())
935 .map_err(|e| sqlx_error_to_query_err(e).into())
936 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
937 #[cfg(feature = "sqlx-postgres")]
938 QueryResultRow::SqlxPostgres(row) => {
939 use sqlx::postgres::types::Oid;
940 match row.try_get::<Option<Oid>, _>(idx.as_sqlx_postgres_index()) {
943 Ok(opt) => opt.ok_or_else(|| err_null_idx_col(idx)).map(|oid| oid.0),
944 Err(_) => row
945 .try_get::<i32, _>(idx.as_sqlx_postgres_index())
947 .map_err(|e| sqlx_error_to_query_err(e).into())
948 .map(|v| {
949 v.try_into().map_err(|e| {
950 DbErr::TryIntoErr {
951 from: "i32",
952 into: "u32",
953 source: Arc::new(e),
954 }
955 .into()
956 })
957 })
958 .and_then(|r| r),
959 }
960 }
961 #[cfg(feature = "sqlx-sqlite")]
962 QueryResultRow::SqlxSqlite(row) => row
963 .try_get::<Option<u32>, _>(idx.as_sqlx_sqlite_index())
964 .map_err(|e| sqlx_error_to_query_err(e).into())
965 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
966 #[cfg(feature = "rusqlite")]
967 QueryResultRow::Rusqlite(row) => row
968 .try_get::<Option<u32>, _>(idx)
969 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
970 #[cfg(feature = "mock")]
971 #[allow(unused_variables)]
972 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
973 debug_print!("{:#?}", e.to_string());
974 err_null_idx_col(idx)
975 }),
976 #[cfg(feature = "proxy")]
977 #[allow(unused_variables)]
978 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
979 debug_print!("{:#?}", e.to_string());
980 err_null_idx_col(idx)
981 }),
982 #[allow(unreachable_patterns)]
983 _ => unreachable!(),
984 }
985 }
986}
987
988impl TryGetable for String {
989 #[allow(unused_variables)]
990 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
991 match &res.row {
992 #[cfg(feature = "sqlx-mysql")]
993 QueryResultRow::SqlxMySql(row) => row
994 .try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
995 .map_err(|e| sqlx_error_to_query_err(e).into())
996 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
997 .map(|bytes| {
998 String::from_utf8(bytes).map_err(|e| {
999 DbErr::TryIntoErr {
1000 from: "Vec<u8>",
1001 into: "String",
1002 source: Arc::new(e),
1003 }
1004 .into()
1005 })
1006 })?,
1007 #[cfg(feature = "sqlx-postgres")]
1008 QueryResultRow::SqlxPostgres(row) => row
1009 .try_get::<Option<String>, _>(idx.as_sqlx_postgres_index())
1010 .map_err(|e| sqlx_error_to_query_err(e).into())
1011 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1012 #[cfg(feature = "sqlx-sqlite")]
1013 QueryResultRow::SqlxSqlite(row) => row
1014 .try_get::<Option<String>, _>(idx.as_sqlx_sqlite_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 = "rusqlite")]
1018 QueryResultRow::Rusqlite(row) => row
1019 .try_get::<Option<String>, _>(idx)
1020 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1021 #[cfg(feature = "mock")]
1022 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1023 debug_print!("{:#?}", e.to_string());
1024 err_null_idx_col(idx)
1025 }),
1026 #[cfg(feature = "proxy")]
1027 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
1028 debug_print!("{:#?}", e.to_string());
1029 err_null_idx_col(idx)
1030 }),
1031 #[allow(unreachable_patterns)]
1032 _ => unreachable!(),
1033 }
1034 }
1035}
1036
1037#[allow(dead_code)]
1038fn err_null_idx_col<I: ColIdx>(idx: I) -> TryGetError {
1039 TryGetError::Null(format!("{idx:?}"))
1040}
1041
1042#[cfg(feature = "postgres-array")]
1043mod postgres_array {
1044 use super::*;
1045
1046 #[allow(unused_macros)]
1047 macro_rules! try_getable_postgres_array {
1048 ( $type: ty ) => {
1049 #[allow(unused_variables)]
1050 impl TryGetable for Vec<$type> {
1051 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1052 match &res.row {
1053 #[cfg(feature = "sqlx-mysql")]
1054 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
1055 "{} unsupported by sqlx-mysql",
1056 stringify!($type)
1057 ))
1058 .into()),
1059 #[cfg(feature = "sqlx-postgres")]
1060 QueryResultRow::SqlxPostgres(row) => row
1061 .try_get::<Option<Vec<$type>>, _>(idx.as_sqlx_postgres_index())
1062 .map_err(|e| sqlx_error_to_query_err(e).into())
1063 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1064 #[cfg(feature = "sqlx-sqlite")]
1065 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1066 "{} unsupported by sqlx-sqlite",
1067 stringify!($type)
1068 ))
1069 .into()),
1070 #[cfg(feature = "rusqlite")]
1071 QueryResultRow::Rusqlite(_) => Err(type_err(format!(
1072 "{} unsupported by rusqlite",
1073 stringify!($type)
1074 ))
1075 .into()),
1076 #[cfg(feature = "mock")]
1077 #[allow(unused_variables)]
1078 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1079 debug_print!("{:#?}", e.to_string());
1080 err_null_idx_col(idx)
1081 }),
1082 #[cfg(feature = "proxy")]
1083 #[allow(unused_variables)]
1084 QueryResultRow::Proxy(row) => row.try_get(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 };
1094 }
1095
1096 try_getable_postgres_array!(bool);
1097 try_getable_postgres_array!(i8);
1098 try_getable_postgres_array!(i16);
1099 try_getable_postgres_array!(i32);
1100 try_getable_postgres_array!(i64);
1101 try_getable_postgres_array!(f32);
1102 try_getable_postgres_array!(f64);
1103 try_getable_postgres_array!(String);
1104 try_getable_postgres_array!(Vec<u8>);
1105
1106 #[cfg(feature = "with-json")]
1107 try_getable_postgres_array!(serde_json::Value);
1108
1109 #[cfg(feature = "with-chrono")]
1110 try_getable_postgres_array!(chrono::NaiveDate);
1111
1112 #[cfg(feature = "with-chrono")]
1113 try_getable_postgres_array!(chrono::NaiveTime);
1114
1115 #[cfg(feature = "with-chrono")]
1116 try_getable_postgres_array!(chrono::NaiveDateTime);
1117
1118 #[cfg(feature = "with-chrono")]
1119 try_getable_postgres_array!(chrono::DateTime<chrono::FixedOffset>);
1120
1121 #[cfg(feature = "with-chrono")]
1122 try_getable_postgres_array!(chrono::DateTime<chrono::Utc>);
1123
1124 #[cfg(feature = "with-chrono")]
1125 try_getable_postgres_array!(chrono::DateTime<chrono::Local>);
1126
1127 #[cfg(feature = "with-time")]
1128 try_getable_postgres_array!(time::Date);
1129
1130 #[cfg(feature = "with-time")]
1131 try_getable_postgres_array!(time::Time);
1132
1133 #[cfg(feature = "with-time")]
1134 try_getable_postgres_array!(time::PrimitiveDateTime);
1135
1136 #[cfg(feature = "with-time")]
1137 try_getable_postgres_array!(time::OffsetDateTime);
1138
1139 #[cfg(feature = "with-rust_decimal")]
1140 try_getable_postgres_array!(rust_decimal::Decimal);
1141
1142 #[cfg(feature = "with-bigdecimal")]
1143 try_getable_postgres_array!(bigdecimal::BigDecimal);
1144
1145 #[cfg(feature = "with-ipnetwork")]
1146 try_getable_postgres_array!(ipnetwork::IpNetwork);
1147
1148 #[cfg(feature = "with-mac_address")]
1149 try_getable_postgres_array!(mac_address::MacAddress);
1150
1151 #[allow(unused_macros)]
1152 macro_rules! try_getable_postgres_array_uuid {
1153 ( $type: ty, $conversion_fn: expr ) => {
1154 #[allow(unused_variables, unreachable_code)]
1155 impl TryGetable for Vec<$type> {
1156 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1157 let res: Result<Vec<uuid::Uuid>, TryGetError> = match &res.row {
1158 #[cfg(feature = "sqlx-mysql")]
1159 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
1160 "{} unsupported by sqlx-mysql",
1161 stringify!($type)
1162 ))
1163 .into()),
1164 #[cfg(feature = "sqlx-postgres")]
1165 QueryResultRow::SqlxPostgres(row) => row
1166 .try_get::<Option<Vec<uuid::Uuid>>, _>(idx.as_sqlx_postgres_index())
1167 .map_err(|e| sqlx_error_to_query_err(e).into())
1168 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1169 #[cfg(feature = "sqlx-sqlite")]
1170 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1171 "{} unsupported by sqlx-sqlite",
1172 stringify!($type)
1173 ))
1174 .into()),
1175 #[cfg(feature = "rusqlite")]
1176 QueryResultRow::Rusqlite(_) => Err(type_err(format!(
1177 "{} unsupported by rusqlite",
1178 stringify!($type)
1179 ))
1180 .into()),
1181 #[cfg(feature = "mock")]
1182 QueryResultRow::Mock(row) => {
1183 row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1184 debug_print!("{:#?}", e.to_string());
1185 err_null_idx_col(idx)
1186 })
1187 }
1188 #[cfg(feature = "proxy")]
1189 QueryResultRow::Proxy(row) => {
1190 row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1191 debug_print!("{:#?}", e.to_string());
1192 err_null_idx_col(idx)
1193 })
1194 }
1195 #[allow(unreachable_patterns)]
1196 _ => unreachable!(),
1197 };
1198 res.map(|vec| vec.into_iter().map($conversion_fn).collect())
1199 }
1200 }
1201 };
1202 }
1203
1204 #[cfg(feature = "with-uuid")]
1205 try_getable_postgres_array_uuid!(uuid::Uuid, Into::into);
1206
1207 #[cfg(feature = "with-uuid")]
1208 try_getable_postgres_array_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
1209
1210 #[cfg(feature = "with-uuid")]
1211 try_getable_postgres_array_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
1212
1213 #[cfg(feature = "with-uuid")]
1214 try_getable_postgres_array_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
1215
1216 #[cfg(feature = "with-uuid")]
1217 try_getable_postgres_array_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
1218
1219 impl TryGetable for Vec<u32> {
1220 #[allow(unused_variables)]
1221 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1222 match &res.row {
1223 #[cfg(feature = "sqlx-mysql")]
1224 QueryResultRow::SqlxMySql(_) => {
1225 Err(type_err(format!("{} unsupported by sqlx-mysql", stringify!($type))).into())
1226 }
1227 #[cfg(feature = "sqlx-postgres")]
1228 QueryResultRow::SqlxPostgres(row) => {
1229 use sqlx::postgres::types::Oid;
1230 row.try_get::<Option<Vec<Oid>>, _>(idx.as_sqlx_postgres_index())
1233 .map_err(|e| sqlx_error_to_query_err(e).into())
1234 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
1235 .map(|oids| oids.into_iter().map(|oid| oid.0).collect())
1236 }
1237 #[cfg(feature = "sqlx-sqlite")]
1238 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1239 "{} unsupported by sqlx-sqlite",
1240 stringify!($type)
1241 ))
1242 .into()),
1243 #[cfg(feature = "rusqlite")]
1244 QueryResultRow::Rusqlite(_) => {
1245 Err(type_err(format!("{} unsupported by rusqlite", stringify!($type))).into())
1246 }
1247 #[cfg(feature = "mock")]
1248 #[allow(unused_variables)]
1249 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1250 debug_print!("{:#?}", e.to_string());
1251 err_null_idx_col(idx)
1252 }),
1253 #[cfg(feature = "proxy")]
1254 #[allow(unused_variables)]
1255 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
1256 debug_print!("{:#?}", e.to_string());
1257 err_null_idx_col(idx)
1258 }),
1259 #[allow(unreachable_patterns)]
1260 _ => unreachable!(),
1261 }
1262 }
1263 }
1264}
1265
1266#[cfg(feature = "postgres-vector")]
1267impl TryGetable for pgvector::Vector {
1268 #[allow(unused_variables)]
1269 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1270 match &res.row {
1271 #[cfg(feature = "sqlx-mysql")]
1272 QueryResultRow::SqlxMySql(_) => {
1273 Err(type_err("Vector unsupported by sqlx-mysql").into())
1274 }
1275 #[cfg(feature = "sqlx-postgres")]
1276 QueryResultRow::SqlxPostgres(row) => row
1277 .try_get::<Option<pgvector::Vector>, _>(idx.as_sqlx_postgres_index())
1278 .map_err(|e| sqlx_error_to_query_err(e).into())
1279 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1280 #[cfg(feature = "sqlx-sqlite")]
1281 QueryResultRow::SqlxSqlite(_) => {
1282 Err(type_err("Vector unsupported by sqlx-sqlite").into())
1283 }
1284 #[cfg(feature = "rusqlite")]
1285 QueryResultRow::Rusqlite(_) => Err(type_err("Vector unsupported by rusqlite").into()),
1286 #[cfg(feature = "mock")]
1287 QueryResultRow::Mock(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1288 debug_print!("{:#?}", e.to_string());
1289 err_null_idx_col(idx)
1290 }),
1291 #[cfg(feature = "proxy")]
1292 QueryResultRow::Proxy(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1293 debug_print!("{:#?}", e.to_string());
1294 err_null_idx_col(idx)
1295 }),
1296 #[allow(unreachable_patterns)]
1297 _ => unreachable!(),
1298 }
1299 }
1300}
1301
1302pub trait TryGetableMany: Sized {
1310 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>;
1313
1314 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError>;
1316
1317 fn find_by_statement<C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<Self, C>>
1373 where
1374 C: strum::IntoEnumIterator + sea_query::Iden,
1375 {
1376 SelectorRaw {
1377 stmt,
1378 selector: PhantomData,
1379 }
1380 }
1381}
1382
1383impl<T> TryGetableMany for T
1384where
1385 T: TryGetable,
1386{
1387 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1388 try_get_many_with_slice_len_of(1, cols)?;
1389 T::try_get(res, pre, &cols[0])
1390 }
1391
1392 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1393 T::try_get_by_index(res, 0)
1394 }
1395}
1396
1397impl<T> TryGetableMany for (T,)
1398where
1399 T: TryGetableMany,
1400{
1401 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1402 T::try_get_many(res, pre, cols).map(|r| (r,))
1403 }
1404
1405 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1406 T::try_get_many_by_index(res).map(|r| (r,))
1407 }
1408}
1409
1410macro_rules! impl_try_get_many {
1411 ( $LEN:expr, $($T:ident : $N:expr),+ $(,)? ) => {
1412 impl< $($T),+ > TryGetableMany for ( $($T),+ )
1413 where
1414 $($T: TryGetable),+
1415 {
1416 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1417 try_get_many_with_slice_len_of($LEN, cols)?;
1418 Ok((
1419 $($T::try_get(res, pre, &cols[$N])?),+
1420 ))
1421 }
1422
1423 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1424 Ok((
1425 $($T::try_get_by_index(res, $N)?),+
1426 ))
1427 }
1428 }
1429 };
1430}
1431
1432#[rustfmt::skip]
1433mod impl_try_get_many {
1434 use super::*;
1435
1436 impl_try_get_many!( 2, T0:0, T1:1);
1437 impl_try_get_many!( 3, T0:0, T1:1, T2:2);
1438 impl_try_get_many!( 4, T0:0, T1:1, T2:2, T3:3);
1439 impl_try_get_many!( 5, T0:0, T1:1, T2:2, T3:3, T4:4);
1440 impl_try_get_many!( 6, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
1441 impl_try_get_many!( 7, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
1442 impl_try_get_many!( 8, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
1443 impl_try_get_many!( 9, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
1444 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);
1445 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);
1446 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);
1447}
1448
1449fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
1450 if cols.len() < len {
1451 Err(type_err(format!(
1452 "Expect {} column names supplied but got slice of length {}",
1453 len,
1454 cols.len()
1455 ))
1456 .into())
1457 } else {
1458 Ok(())
1459 }
1460}
1461
1462pub trait TryGetableArray: Sized {
1468 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
1470}
1471
1472impl<T> TryGetable for Vec<T>
1473where
1474 T: TryGetableArray,
1475{
1476 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1477 T::try_get_by(res, index)
1478 }
1479}
1480
1481#[cfg(feature = "with-json")]
1485pub trait TryGetableFromJson: Sized
1486where
1487 for<'de> Self: serde::Deserialize<'de>,
1488{
1489 #[allow(unused_variables, unreachable_code)]
1491 fn try_get_from_json<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1492 match &res.row {
1493 #[cfg(feature = "sqlx-mysql")]
1494 QueryResultRow::SqlxMySql(row) => row
1495 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
1496 .map_err(|e| sqlx_error_to_query_err(e).into())
1497 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1498 #[cfg(feature = "sqlx-postgres")]
1499 QueryResultRow::SqlxPostgres(row) => row
1500 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
1501 .map_err(|e| sqlx_error_to_query_err(e).into())
1502 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1503 #[cfg(feature = "sqlx-sqlite")]
1504 QueryResultRow::SqlxSqlite(row) => row
1505 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
1506 .map_err(|e| sqlx_error_to_query_err(e).into())
1507 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1508 #[cfg(feature = "rusqlite")]
1509 QueryResultRow::Rusqlite(row) => row
1510 .try_get::<Option<serde_json::Value>, _>(idx)?
1511 .ok_or_else(|| err_null_idx_col(idx))
1512 .and_then(|json| {
1513 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1514 }),
1515 #[cfg(feature = "mock")]
1516 QueryResultRow::Mock(row) => row
1517 .try_get::<serde_json::Value, I>(idx)
1518 .map_err(|e| {
1519 debug_print!("{:#?}", e.to_string());
1520 err_null_idx_col(idx)
1521 })
1522 .and_then(|json| {
1523 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1524 }),
1525 #[cfg(feature = "proxy")]
1526 QueryResultRow::Proxy(row) => row
1527 .try_get::<serde_json::Value, I>(idx)
1528 .map_err(|e| {
1529 debug_print!("{:#?}", e.to_string());
1530 err_null_idx_col(idx)
1531 })
1532 .and_then(|json| {
1533 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1534 }),
1535 #[allow(unreachable_patterns)]
1536 _ => unreachable!(),
1537 }
1538 }
1539
1540 fn from_json_vec(value: serde_json::Value) -> Result<Vec<Self>, TryGetError> {
1542 match value {
1543 serde_json::Value::Array(values) => {
1544 let mut res = Vec::new();
1545 for item in values {
1546 res.push(serde_json::from_value(item).map_err(crate::error::json_err)?);
1547 }
1548 Ok(res)
1549 }
1550 _ => Err(TryGetError::DbErr(DbErr::Json(
1551 "Value is not an Array".to_owned(),
1552 ))),
1553 }
1554 }
1555}
1556
1557#[cfg(feature = "with-json")]
1558impl<T> TryGetable for T
1559where
1560 T: TryGetableFromJson,
1561{
1562 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1563 T::try_get_from_json(res, index)
1564 }
1565}
1566
1567#[cfg(feature = "with-json")]
1568impl<T> TryGetableArray for T
1569where
1570 T: TryGetableFromJson,
1571{
1572 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<T>, TryGetError> {
1573 T::from_json_vec(serde_json::Value::try_get_by(res, index)?)
1574 }
1575}
1576
1577pub trait TryFromU64: Sized {
1584 fn try_from_u64(n: u64) -> Result<Self, DbErr>;
1586}
1587
1588#[cfg(feature = "with-json")]
1589use serde::de::DeserializeOwned;
1590
1591#[cfg(feature = "with-json")]
1592impl<K, V> TryGetableFromJson for HashMap<K, V>
1593where
1594 K: DeserializeOwned + Eq + Hash,
1595 V: DeserializeOwned,
1596{
1597}
1598
1599#[cfg(feature = "with-json")]
1600impl<K, V> TryGetableFromJson for BTreeMap<K, V>
1601where
1602 K: DeserializeOwned + Ord,
1603 V: DeserializeOwned,
1604{
1605}
1606
1607macro_rules! try_from_u64_err {
1608 ( $type: ty ) => {
1609 impl TryFromU64 for $type {
1610 fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1611 Err(DbErr::ConvertFromU64(stringify!($type)))
1612 }
1613 }
1614 };
1615
1616 ( $($gen_type: ident),* ) => {
1617 impl<$( $gen_type, )*> TryFromU64 for ($( $gen_type, )*)
1618 where
1619 $( $gen_type: TryFromU64, )*
1620 {
1621 fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1622 Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*)))
1623 }
1624 }
1625 };
1626}
1627
1628#[rustfmt::skip]
1629mod try_from_u64_err {
1630 use super::*;
1631
1632 try_from_u64_err!(T0, T1);
1633 try_from_u64_err!(T0, T1, T2);
1634 try_from_u64_err!(T0, T1, T2, T3);
1635 try_from_u64_err!(T0, T1, T2, T3, T4);
1636 try_from_u64_err!(T0, T1, T2, T3, T4, T5);
1637 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6);
1638 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7);
1639 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
1640 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
1641 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
1642 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
1643}
1644
1645macro_rules! try_from_u64_numeric {
1646 ( $type: ty ) => {
1647 impl TryFromU64 for $type {
1648 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1649 use std::convert::TryInto;
1650 n.try_into().map_err(|e| DbErr::TryIntoErr {
1651 from: stringify!(u64),
1652 into: stringify!($type),
1653 source: Arc::new(e),
1654 })
1655 }
1656 }
1657 };
1658}
1659
1660try_from_u64_numeric!(i8);
1661try_from_u64_numeric!(i16);
1662try_from_u64_numeric!(i32);
1663try_from_u64_numeric!(i64);
1664try_from_u64_numeric!(u8);
1665try_from_u64_numeric!(u16);
1666try_from_u64_numeric!(u32);
1667try_from_u64_numeric!(u64);
1668
1669macro_rules! try_from_u64_string {
1670 ( $type: ty ) => {
1671 impl TryFromU64 for $type {
1672 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1673 Ok(n.to_string())
1674 }
1675 }
1676 };
1677}
1678
1679try_from_u64_string!(String);
1680
1681try_from_u64_err!(bool);
1682try_from_u64_err!(f32);
1683try_from_u64_err!(f64);
1684try_from_u64_err!(Vec<u8>);
1685
1686#[cfg(feature = "with-json")]
1687try_from_u64_err!(serde_json::Value);
1688
1689#[cfg(feature = "with-chrono")]
1690try_from_u64_err!(chrono::NaiveDate);
1691
1692#[cfg(feature = "with-chrono")]
1693try_from_u64_err!(chrono::NaiveTime);
1694
1695#[cfg(feature = "with-chrono")]
1696try_from_u64_err!(chrono::NaiveDateTime);
1697
1698#[cfg(feature = "with-chrono")]
1699try_from_u64_err!(chrono::DateTime<chrono::FixedOffset>);
1700
1701#[cfg(feature = "with-chrono")]
1702try_from_u64_err!(chrono::DateTime<chrono::Utc>);
1703
1704#[cfg(feature = "with-chrono")]
1705try_from_u64_err!(chrono::DateTime<chrono::Local>);
1706
1707#[cfg(feature = "with-time")]
1708try_from_u64_err!(time::Date);
1709
1710#[cfg(feature = "with-time")]
1711try_from_u64_err!(time::Time);
1712
1713#[cfg(feature = "with-time")]
1714try_from_u64_err!(time::PrimitiveDateTime);
1715
1716#[cfg(feature = "with-time")]
1717try_from_u64_err!(time::OffsetDateTime);
1718
1719#[cfg(feature = "with-rust_decimal")]
1720try_from_u64_err!(rust_decimal::Decimal);
1721
1722#[cfg(feature = "with-uuid")]
1723try_from_u64_err!(uuid::Uuid);
1724
1725#[cfg(feature = "with-ipnetwork")]
1726try_from_u64_err!(ipnetwork::IpNetwork);
1727
1728#[cfg(feature = "with-mac_address")]
1729try_from_u64_err!(mac_address::MacAddress);
1730
1731#[cfg(test)]
1732mod tests {
1733 use super::*;
1734 use crate::{MockRow, RuntimeErr};
1735 use sea_query::Value;
1736
1737 use crate::{QueryResult, TryGetable};
1738 use serde::{Deserialize, Serialize};
1739 use std::collections::{BTreeMap, HashMap};
1740
1741 #[test]
1742 fn from_try_get_error() {
1743 let try_get_error = TryGetError::DbErr(DbErr::Query(RuntimeErr::Internal(
1745 "expected error message".to_owned(),
1746 )));
1747 assert_eq!(
1748 DbErr::from(try_get_error),
1749 DbErr::Query(RuntimeErr::Internal("expected error message".to_owned()))
1750 );
1751
1752 let try_get_error = TryGetError::Null("column".to_owned());
1754 let expected = "A null value was encountered while decoding column".to_owned();
1755 assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected));
1756 }
1757
1758 #[test]
1759 fn build_with_query() {
1760 use sea_orm::{DbBackend, Statement};
1761 use sea_query::{
1762 ColumnRef, CommonTableExpression, Cycle, Expr, ExprTrait, JoinType, SelectStatement,
1763 UnionType, WithClause,
1764 };
1765
1766 let base_query = SelectStatement::new()
1767 .column("id")
1768 .expr(1i32)
1769 .column("next")
1770 .column("value")
1771 .from("table")
1772 .to_owned();
1773
1774 let cte_referencing = SelectStatement::new()
1775 .column("id")
1776 .expr(Expr::col("depth").add(1i32))
1777 .column("next")
1778 .column("value")
1779 .from("table")
1780 .join(
1781 JoinType::InnerJoin,
1782 "cte_traversal",
1783 Expr::col(("cte_traversal", "next")).equals(("table", "id")),
1784 )
1785 .to_owned();
1786
1787 let common_table_expression = CommonTableExpression::new()
1788 .query(
1789 base_query
1790 .clone()
1791 .union(UnionType::All, cte_referencing)
1792 .to_owned(),
1793 )
1794 .columns(["id", "depth", "next", "value"])
1795 .table_name("cte_traversal")
1796 .to_owned();
1797
1798 let select = SelectStatement::new()
1799 .column(ColumnRef::Asterisk(None))
1800 .from("cte_traversal")
1801 .to_owned();
1802
1803 let with_clause = WithClause::new()
1804 .recursive(true)
1805 .cte(common_table_expression)
1806 .cycle(Cycle::new_from_expr_set_using(
1807 Expr::column("id"),
1808 "looped",
1809 "traversal_path",
1810 ))
1811 .to_owned();
1812
1813 let with_query = select.with(with_clause).to_owned();
1814
1815 assert_eq!(
1816 DbBackend::MySql.build(&with_query),
1817 Statement::from_sql_and_values(
1818 DbBackend::MySql,
1819 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`",
1820 [1.into(), 1.into()]
1821 )
1822 );
1823 }
1824
1825 #[test]
1826 fn column_names_from_query_result() {
1827 let mut values = BTreeMap::new();
1828 values.insert("id".to_string(), Value::Int(Some(1)));
1829 values.insert("name".to_string(), Value::String(Some("Abc".to_owned())));
1830 let query_result = QueryResult {
1831 row: QueryResultRow::Mock(MockRow { values }),
1832 };
1833 assert_eq!(
1834 query_result.column_names(),
1835 vec!["id".to_owned(), "name".to_owned()]
1836 );
1837 }
1838
1839 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1840 struct Component {
1841 base_price: i32,
1842 component_type: String,
1843 }
1844
1845 #[test]
1846 fn json_deserialize_to_btreemap() {
1847 let json_value = serde_json::json!({
1848 "engine": {
1849 "base_price": 100,
1850 "component_type": "metal"
1851 }
1852 });
1853
1854 let values = BTreeMap::from([(
1855 "components".to_string(),
1856 Value::Json(Some(Box::new(json_value))),
1857 )]);
1858
1859 let row = QueryResultRow::Mock(MockRow { values });
1860
1861 let result: BTreeMap<String, Component> =
1862 TryGetable::try_get_by(&QueryResult { row }, "components").unwrap();
1863
1864 assert_eq!(
1865 result.get("engine"),
1866 Some(&Component {
1867 base_price: 100,
1868 component_type: "metal".to_owned(),
1869 })
1870 );
1871 }
1872
1873 #[test]
1874 fn json_deserialize_to_hashmap() {
1875 let json_value = serde_json::json!({
1876 "engine": {
1877 "base_price": 100,
1878 "component_type": "metal"
1879 }
1880 });
1881
1882 let values = BTreeMap::from([(
1883 "components".to_string(),
1884 Value::Json(Some(Box::new(json_value))),
1885 )]);
1886
1887 let row = QueryResultRow::Mock(MockRow { values });
1888
1889 let result: HashMap<String, Component> =
1890 TryGetable::try_get_by(&QueryResult { row }, "components").unwrap();
1891
1892 assert_eq!(
1893 result.get("engine"),
1894 Some(&Component {
1895 base_price: 100,
1896 component_type: "metal".to_owned(),
1897 })
1898 );
1899 }
1900}