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