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