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