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