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 match row.try_get::<Option<Oid>, _>(idx.as_sqlx_postgres_index()) {
828 Ok(opt) => opt.ok_or_else(|| err_null_idx_col(idx)).map(|oid| oid.0),
829 Err(_) => row
830 .try_get::<i32, _>(idx.as_sqlx_postgres_index())
832 .map_err(|e| sqlx_error_to_query_err(e).into())
833 .map(|v| {
834 v.try_into().map_err(|e| {
835 DbErr::TryIntoErr {
836 from: "i32",
837 into: "u32",
838 source: Arc::new(e),
839 }
840 .into()
841 })
842 })
843 .and_then(|r| r),
844 }
845 }
846 #[cfg(feature = "sqlx-sqlite")]
847 QueryResultRow::SqlxSqlite(row) => row
848 .try_get::<Option<u32>, _>(idx.as_sqlx_sqlite_index())
849 .map_err(|e| sqlx_error_to_query_err(e).into())
850 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
851 #[cfg(feature = "mock")]
852 #[allow(unused_variables)]
853 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
854 debug_print!("{:#?}", e.to_string());
855 err_null_idx_col(idx)
856 }),
857 #[cfg(feature = "proxy")]
858 #[allow(unused_variables)]
859 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
860 debug_print!("{:#?}", e.to_string());
861 err_null_idx_col(idx)
862 }),
863 #[allow(unreachable_patterns)]
864 _ => unreachable!(),
865 }
866 }
867}
868
869impl TryGetable for String {
870 #[allow(unused_variables)]
871 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
872 match &res.row {
873 #[cfg(feature = "sqlx-mysql")]
874 QueryResultRow::SqlxMySql(row) => row
875 .try_get::<Option<Vec<u8>>, _>(idx.as_sqlx_mysql_index())
876 .map_err(|e| sqlx_error_to_query_err(e).into())
877 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
878 .map(|bytes| {
879 String::from_utf8(bytes).map_err(|e| {
880 DbErr::TryIntoErr {
881 from: "Vec<u8>",
882 into: "String",
883 source: Arc::new(e),
884 }
885 .into()
886 })
887 })?,
888 #[cfg(feature = "sqlx-postgres")]
889 QueryResultRow::SqlxPostgres(row) => row
890 .try_get::<Option<String>, _>(idx.as_sqlx_postgres_index())
891 .map_err(|e| sqlx_error_to_query_err(e).into())
892 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
893 #[cfg(feature = "sqlx-sqlite")]
894 QueryResultRow::SqlxSqlite(row) => row
895 .try_get::<Option<String>, _>(idx.as_sqlx_sqlite_index())
896 .map_err(|e| sqlx_error_to_query_err(e).into())
897 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
898 #[cfg(feature = "mock")]
899 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
900 debug_print!("{:#?}", e.to_string());
901 err_null_idx_col(idx)
902 }),
903 #[cfg(feature = "proxy")]
904 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
905 debug_print!("{:#?}", e.to_string());
906 err_null_idx_col(idx)
907 }),
908 #[allow(unreachable_patterns)]
909 _ => unreachable!(),
910 }
911 }
912}
913
914#[allow(dead_code)]
915fn err_null_idx_col<I: ColIdx>(idx: I) -> TryGetError {
916 TryGetError::Null(format!("{idx:?}"))
917}
918
919#[cfg(feature = "postgres-array")]
920mod postgres_array {
921 use super::*;
922
923 #[allow(unused_macros)]
924 macro_rules! try_getable_postgres_array {
925 ( $type: ty ) => {
926 #[allow(unused_variables)]
927 impl TryGetable for Vec<$type> {
928 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
929 match &res.row {
930 #[cfg(feature = "sqlx-mysql")]
931 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
932 "{} unsupported by sqlx-mysql",
933 stringify!($type)
934 ))
935 .into()),
936 #[cfg(feature = "sqlx-postgres")]
937 QueryResultRow::SqlxPostgres(row) => row
938 .try_get::<Option<Vec<$type>>, _>(idx.as_sqlx_postgres_index())
939 .map_err(|e| sqlx_error_to_query_err(e).into())
940 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
941 #[cfg(feature = "sqlx-sqlite")]
942 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
943 "{} unsupported by sqlx-sqlite",
944 stringify!($type)
945 ))
946 .into()),
947 #[cfg(feature = "mock")]
948 #[allow(unused_variables)]
949 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
950 debug_print!("{:#?}", e.to_string());
951 err_null_idx_col(idx)
952 }),
953 #[cfg(feature = "proxy")]
954 #[allow(unused_variables)]
955 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
956 debug_print!("{:#?}", e.to_string());
957 err_null_idx_col(idx)
958 }),
959 #[allow(unreachable_patterns)]
960 _ => unreachable!(),
961 }
962 }
963 }
964 };
965 }
966
967 try_getable_postgres_array!(bool);
968 try_getable_postgres_array!(i8);
969 try_getable_postgres_array!(i16);
970 try_getable_postgres_array!(i32);
971 try_getable_postgres_array!(i64);
972 try_getable_postgres_array!(f32);
973 try_getable_postgres_array!(f64);
974 try_getable_postgres_array!(String);
975 try_getable_postgres_array!(Vec<u8>);
976
977 #[cfg(feature = "with-json")]
978 try_getable_postgres_array!(serde_json::Value);
979
980 #[cfg(feature = "with-chrono")]
981 try_getable_postgres_array!(chrono::NaiveDate);
982
983 #[cfg(feature = "with-chrono")]
984 try_getable_postgres_array!(chrono::NaiveTime);
985
986 #[cfg(feature = "with-chrono")]
987 try_getable_postgres_array!(chrono::NaiveDateTime);
988
989 #[cfg(feature = "with-chrono")]
990 try_getable_postgres_array!(chrono::DateTime<chrono::FixedOffset>);
991
992 #[cfg(feature = "with-chrono")]
993 try_getable_postgres_array!(chrono::DateTime<chrono::Utc>);
994
995 #[cfg(feature = "with-chrono")]
996 try_getable_postgres_array!(chrono::DateTime<chrono::Local>);
997
998 #[cfg(feature = "with-time")]
999 try_getable_postgres_array!(time::Date);
1000
1001 #[cfg(feature = "with-time")]
1002 try_getable_postgres_array!(time::Time);
1003
1004 #[cfg(feature = "with-time")]
1005 try_getable_postgres_array!(time::PrimitiveDateTime);
1006
1007 #[cfg(feature = "with-time")]
1008 try_getable_postgres_array!(time::OffsetDateTime);
1009
1010 #[cfg(feature = "with-rust_decimal")]
1011 try_getable_postgres_array!(rust_decimal::Decimal);
1012
1013 #[cfg(feature = "with-bigdecimal")]
1014 try_getable_postgres_array!(bigdecimal::BigDecimal);
1015
1016 #[cfg(feature = "with-ipnetwork")]
1017 try_getable_postgres_array!(ipnetwork::IpNetwork);
1018
1019 #[allow(unused_macros)]
1020 macro_rules! try_getable_postgres_array_uuid {
1021 ( $type: ty, $conversion_fn: expr ) => {
1022 #[allow(unused_variables, unreachable_code)]
1023 impl TryGetable for Vec<$type> {
1024 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1025 let res: Result<Vec<uuid::Uuid>, TryGetError> = match &res.row {
1026 #[cfg(feature = "sqlx-mysql")]
1027 QueryResultRow::SqlxMySql(_) => Err(type_err(format!(
1028 "{} unsupported by sqlx-mysql",
1029 stringify!($type)
1030 ))
1031 .into()),
1032 #[cfg(feature = "sqlx-postgres")]
1033 QueryResultRow::SqlxPostgres(row) => row
1034 .try_get::<Option<Vec<uuid::Uuid>>, _>(idx.as_sqlx_postgres_index())
1035 .map_err(|e| sqlx_error_to_query_err(e).into())
1036 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1037 #[cfg(feature = "sqlx-sqlite")]
1038 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1039 "{} unsupported by sqlx-sqlite",
1040 stringify!($type)
1041 ))
1042 .into()),
1043 #[cfg(feature = "mock")]
1044 QueryResultRow::Mock(row) => {
1045 row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1046 debug_print!("{:#?}", e.to_string());
1047 err_null_idx_col(idx)
1048 })
1049 }
1050 #[cfg(feature = "proxy")]
1051 QueryResultRow::Proxy(row) => {
1052 row.try_get::<Vec<uuid::Uuid>, _>(idx).map_err(|e| {
1053 debug_print!("{:#?}", e.to_string());
1054 err_null_idx_col(idx)
1055 })
1056 }
1057 #[allow(unreachable_patterns)]
1058 _ => unreachable!(),
1059 };
1060 res.map(|vec| vec.into_iter().map($conversion_fn).collect())
1061 }
1062 }
1063 };
1064 }
1065
1066 #[cfg(feature = "with-uuid")]
1067 try_getable_postgres_array_uuid!(uuid::Uuid, Into::into);
1068
1069 #[cfg(feature = "with-uuid")]
1070 try_getable_postgres_array_uuid!(uuid::fmt::Braced, uuid::Uuid::braced);
1071
1072 #[cfg(feature = "with-uuid")]
1073 try_getable_postgres_array_uuid!(uuid::fmt::Hyphenated, uuid::Uuid::hyphenated);
1074
1075 #[cfg(feature = "with-uuid")]
1076 try_getable_postgres_array_uuid!(uuid::fmt::Simple, uuid::Uuid::simple);
1077
1078 #[cfg(feature = "with-uuid")]
1079 try_getable_postgres_array_uuid!(uuid::fmt::Urn, uuid::Uuid::urn);
1080
1081 impl TryGetable for Vec<u32> {
1082 #[allow(unused_variables)]
1083 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1084 match &res.row {
1085 #[cfg(feature = "sqlx-mysql")]
1086 QueryResultRow::SqlxMySql(_) => {
1087 Err(type_err(format!("{} unsupported by sqlx-mysql", stringify!($type))).into())
1088 }
1089 #[cfg(feature = "sqlx-postgres")]
1090 QueryResultRow::SqlxPostgres(row) => {
1091 use sqlx::postgres::types::Oid;
1092 row.try_get::<Option<Vec<Oid>>, _>(idx.as_sqlx_postgres_index())
1095 .map_err(|e| sqlx_error_to_query_err(e).into())
1096 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)))
1097 .map(|oids| oids.into_iter().map(|oid| oid.0).collect())
1098 }
1099 #[cfg(feature = "sqlx-sqlite")]
1100 QueryResultRow::SqlxSqlite(_) => Err(type_err(format!(
1101 "{} unsupported by sqlx-sqlite",
1102 stringify!($type)
1103 ))
1104 .into()),
1105 #[cfg(feature = "mock")]
1106 #[allow(unused_variables)]
1107 QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| {
1108 debug_print!("{:#?}", e.to_string());
1109 err_null_idx_col(idx)
1110 }),
1111 #[cfg(feature = "proxy")]
1112 #[allow(unused_variables)]
1113 QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| {
1114 debug_print!("{:#?}", e.to_string());
1115 err_null_idx_col(idx)
1116 }),
1117 #[allow(unreachable_patterns)]
1118 _ => unreachable!(),
1119 }
1120 }
1121 }
1122}
1123
1124#[cfg(feature = "postgres-vector")]
1125impl TryGetable for pgvector::Vector {
1126 #[allow(unused_variables)]
1127 fn try_get_by<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1128 match &res.row {
1129 #[cfg(feature = "sqlx-mysql")]
1130 QueryResultRow::SqlxMySql(_) => {
1131 Err(type_err("Vector unsupported by sqlx-mysql").into())
1132 }
1133 #[cfg(feature = "sqlx-postgres")]
1134 QueryResultRow::SqlxPostgres(row) => row
1135 .try_get::<Option<pgvector::Vector>, _>(idx.as_sqlx_postgres_index())
1136 .map_err(|e| sqlx_error_to_query_err(e).into())
1137 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))),
1138 #[cfg(feature = "sqlx-sqlite")]
1139 QueryResultRow::SqlxSqlite(_) => {
1140 Err(type_err("Vector unsupported by sqlx-sqlite").into())
1141 }
1142 #[cfg(feature = "mock")]
1143 QueryResultRow::Mock(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1144 debug_print!("{:#?}", e.to_string());
1145 err_null_idx_col(idx)
1146 }),
1147 #[cfg(feature = "proxy")]
1148 QueryResultRow::Proxy(row) => row.try_get::<pgvector::Vector, _>(idx).map_err(|e| {
1149 debug_print!("{:#?}", e.to_string());
1150 err_null_idx_col(idx)
1151 }),
1152 #[allow(unreachable_patterns)]
1153 _ => unreachable!(),
1154 }
1155 }
1156}
1157
1158pub trait TryGetableMany: Sized {
1162 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>;
1164
1165 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError>;
1167
1168 fn find_by_statement<C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<Self, C>>
1226 where
1227 C: strum::IntoEnumIterator + sea_query::Iden,
1228 {
1229 SelectorRaw {
1230 stmt,
1231 selector: PhantomData,
1232 }
1233 }
1234}
1235
1236impl<T> TryGetableMany for T
1237where
1238 T: TryGetable,
1239{
1240 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1241 try_get_many_with_slice_len_of(1, cols)?;
1242 T::try_get(res, pre, &cols[0])
1243 }
1244
1245 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1246 T::try_get_by_index(res, 0)
1247 }
1248}
1249
1250impl<T> TryGetableMany for (T,)
1251where
1252 T: TryGetableMany,
1253{
1254 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1255 T::try_get_many(res, pre, cols).map(|r| (r,))
1256 }
1257
1258 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1259 T::try_get_many_by_index(res).map(|r| (r,))
1260 }
1261}
1262
1263macro_rules! impl_try_get_many {
1264 ( $LEN:expr, $($T:ident : $N:expr),+ $(,)? ) => {
1265 impl< $($T),+ > TryGetableMany for ( $($T),+ )
1266 where
1267 $($T: TryGetable),+
1268 {
1269 fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
1270 try_get_many_with_slice_len_of($LEN, cols)?;
1271 Ok((
1272 $($T::try_get(res, pre, &cols[$N])?),+
1273 ))
1274 }
1275
1276 fn try_get_many_by_index(res: &QueryResult) -> Result<Self, TryGetError> {
1277 Ok((
1278 $($T::try_get_by_index(res, $N)?),+
1279 ))
1280 }
1281 }
1282 };
1283}
1284
1285#[rustfmt::skip]
1286mod impl_try_get_many {
1287 use super::*;
1288
1289 impl_try_get_many!( 2, T0:0, T1:1);
1290 impl_try_get_many!( 3, T0:0, T1:1, T2:2);
1291 impl_try_get_many!( 4, T0:0, T1:1, T2:2, T3:3);
1292 impl_try_get_many!( 5, T0:0, T1:1, T2:2, T3:3, T4:4);
1293 impl_try_get_many!( 6, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
1294 impl_try_get_many!( 7, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
1295 impl_try_get_many!( 8, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
1296 impl_try_get_many!( 9, T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
1297 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);
1298 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);
1299 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);
1300}
1301
1302fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> {
1303 if cols.len() < len {
1304 Err(type_err(format!(
1305 "Expect {} column names supplied but got slice of length {}",
1306 len,
1307 cols.len()
1308 ))
1309 .into())
1310 } else {
1311 Ok(())
1312 }
1313}
1314
1315pub trait TryGetableArray: Sized {
1321 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
1323}
1324
1325impl<T> TryGetable for Vec<T>
1326where
1327 T: TryGetableArray,
1328{
1329 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1330 T::try_get_by(res, index)
1331 }
1332}
1333
1334#[cfg(feature = "with-json")]
1338pub trait TryGetableFromJson: Sized
1339where
1340 for<'de> Self: serde::Deserialize<'de>,
1341{
1342 #[allow(unused_variables, unreachable_code)]
1344 fn try_get_from_json<I: ColIdx>(res: &QueryResult, idx: I) -> Result<Self, TryGetError> {
1345 match &res.row {
1346 #[cfg(feature = "sqlx-mysql")]
1347 QueryResultRow::SqlxMySql(row) => row
1348 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_mysql_index())
1349 .map_err(|e| sqlx_error_to_query_err(e).into())
1350 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1351 #[cfg(feature = "sqlx-postgres")]
1352 QueryResultRow::SqlxPostgres(row) => row
1353 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_postgres_index())
1354 .map_err(|e| sqlx_error_to_query_err(e).into())
1355 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1356 #[cfg(feature = "sqlx-sqlite")]
1357 QueryResultRow::SqlxSqlite(row) => row
1358 .try_get::<Option<sqlx::types::Json<Self>>, _>(idx.as_sqlx_sqlite_index())
1359 .map_err(|e| sqlx_error_to_query_err(e).into())
1360 .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)),
1361 #[cfg(feature = "mock")]
1362 QueryResultRow::Mock(row) => row
1363 .try_get::<serde_json::Value, I>(idx)
1364 .map_err(|e| {
1365 debug_print!("{:#?}", e.to_string());
1366 err_null_idx_col(idx)
1367 })
1368 .and_then(|json| {
1369 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1370 }),
1371 #[cfg(feature = "proxy")]
1372 QueryResultRow::Proxy(row) => row
1373 .try_get::<serde_json::Value, I>(idx)
1374 .map_err(|e| {
1375 debug_print!("{:#?}", e.to_string());
1376 err_null_idx_col(idx)
1377 })
1378 .and_then(|json| {
1379 serde_json::from_value(json).map_err(|e| crate::error::json_err(e).into())
1380 }),
1381 #[allow(unreachable_patterns)]
1382 _ => unreachable!(),
1383 }
1384 }
1385
1386 fn from_json_vec(value: serde_json::Value) -> Result<Vec<Self>, TryGetError> {
1388 match value {
1389 serde_json::Value::Array(values) => {
1390 let mut res = Vec::new();
1391 for item in values {
1392 res.push(serde_json::from_value(item).map_err(crate::error::json_err)?);
1393 }
1394 Ok(res)
1395 }
1396 _ => Err(TryGetError::DbErr(DbErr::Json(
1397 "Value is not an Array".to_owned(),
1398 ))),
1399 }
1400 }
1401}
1402
1403#[cfg(feature = "with-json")]
1404impl<T> TryGetable for T
1405where
1406 T: TryGetableFromJson,
1407{
1408 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
1409 T::try_get_from_json(res, index)
1410 }
1411}
1412
1413#[cfg(feature = "with-json")]
1414impl<T> TryGetableArray for T
1415where
1416 T: TryGetableFromJson,
1417{
1418 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<T>, TryGetError> {
1419 T::from_json_vec(serde_json::Value::try_get_by(res, index)?)
1420 }
1421}
1422
1423pub trait TryFromU64: Sized {
1426 fn try_from_u64(n: u64) -> Result<Self, DbErr>;
1428}
1429
1430macro_rules! try_from_u64_err {
1431 ( $type: ty ) => {
1432 impl TryFromU64 for $type {
1433 fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1434 Err(DbErr::ConvertFromU64(stringify!($type)))
1435 }
1436 }
1437 };
1438
1439 ( $($gen_type: ident),* ) => {
1440 impl<$( $gen_type, )*> TryFromU64 for ($( $gen_type, )*)
1441 where
1442 $( $gen_type: TryFromU64, )*
1443 {
1444 fn try_from_u64(_: u64) -> Result<Self, DbErr> {
1445 Err(DbErr::ConvertFromU64(stringify!($($gen_type,)*)))
1446 }
1447 }
1448 };
1449}
1450
1451#[rustfmt::skip]
1452mod try_from_u64_err {
1453 use super::*;
1454
1455 try_from_u64_err!(T0, T1);
1456 try_from_u64_err!(T0, T1, T2);
1457 try_from_u64_err!(T0, T1, T2, T3);
1458 try_from_u64_err!(T0, T1, T2, T3, T4);
1459 try_from_u64_err!(T0, T1, T2, T3, T4, T5);
1460 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6);
1461 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7);
1462 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
1463 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
1464 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
1465 try_from_u64_err!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
1466}
1467
1468macro_rules! try_from_u64_numeric {
1469 ( $type: ty ) => {
1470 impl TryFromU64 for $type {
1471 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1472 use std::convert::TryInto;
1473 n.try_into().map_err(|e| DbErr::TryIntoErr {
1474 from: stringify!(u64),
1475 into: stringify!($type),
1476 source: Arc::new(e),
1477 })
1478 }
1479 }
1480 };
1481}
1482
1483try_from_u64_numeric!(i8);
1484try_from_u64_numeric!(i16);
1485try_from_u64_numeric!(i32);
1486try_from_u64_numeric!(i64);
1487try_from_u64_numeric!(u8);
1488try_from_u64_numeric!(u16);
1489try_from_u64_numeric!(u32);
1490try_from_u64_numeric!(u64);
1491
1492macro_rules! try_from_u64_string {
1493 ( $type: ty ) => {
1494 impl TryFromU64 for $type {
1495 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
1496 Ok(n.to_string())
1497 }
1498 }
1499 };
1500}
1501
1502try_from_u64_string!(String);
1503
1504try_from_u64_err!(bool);
1505try_from_u64_err!(f32);
1506try_from_u64_err!(f64);
1507try_from_u64_err!(Vec<u8>);
1508
1509#[cfg(feature = "with-json")]
1510try_from_u64_err!(serde_json::Value);
1511
1512#[cfg(feature = "with-chrono")]
1513try_from_u64_err!(chrono::NaiveDate);
1514
1515#[cfg(feature = "with-chrono")]
1516try_from_u64_err!(chrono::NaiveTime);
1517
1518#[cfg(feature = "with-chrono")]
1519try_from_u64_err!(chrono::NaiveDateTime);
1520
1521#[cfg(feature = "with-chrono")]
1522try_from_u64_err!(chrono::DateTime<chrono::FixedOffset>);
1523
1524#[cfg(feature = "with-chrono")]
1525try_from_u64_err!(chrono::DateTime<chrono::Utc>);
1526
1527#[cfg(feature = "with-chrono")]
1528try_from_u64_err!(chrono::DateTime<chrono::Local>);
1529
1530#[cfg(feature = "with-time")]
1531try_from_u64_err!(time::Date);
1532
1533#[cfg(feature = "with-time")]
1534try_from_u64_err!(time::Time);
1535
1536#[cfg(feature = "with-time")]
1537try_from_u64_err!(time::PrimitiveDateTime);
1538
1539#[cfg(feature = "with-time")]
1540try_from_u64_err!(time::OffsetDateTime);
1541
1542#[cfg(feature = "with-rust_decimal")]
1543try_from_u64_err!(rust_decimal::Decimal);
1544
1545#[cfg(feature = "with-uuid")]
1546try_from_u64_err!(uuid::Uuid);
1547
1548#[cfg(feature = "with-ipnetwork")]
1549try_from_u64_err!(ipnetwork::IpNetwork);
1550
1551#[cfg(test)]
1552mod tests {
1553 use super::*;
1554 use crate::RuntimeErr;
1555 use sea_query::Value;
1556 use std::collections::BTreeMap;
1557
1558 #[test]
1559 fn from_try_get_error() {
1560 let try_get_error = TryGetError::DbErr(DbErr::Query(RuntimeErr::Internal(
1562 "expected error message".to_owned(),
1563 )));
1564 assert_eq!(
1565 DbErr::from(try_get_error),
1566 DbErr::Query(RuntimeErr::Internal("expected error message".to_owned()))
1567 );
1568
1569 let try_get_error = TryGetError::Null("column".to_owned());
1571 let expected = "A null value was encountered while decoding column".to_owned();
1572 assert_eq!(DbErr::from(try_get_error), DbErr::Type(expected));
1573 }
1574
1575 #[test]
1576 fn build_with_query() {
1577 use sea_orm::{DbBackend, Statement};
1578 use sea_query::{
1579 ColumnRef, CommonTableExpression, Cycle, Expr, ExprTrait, JoinType, SelectStatement,
1580 UnionType, WithClause,
1581 };
1582
1583 let base_query = SelectStatement::new()
1584 .column("id")
1585 .expr(1i32)
1586 .column("next")
1587 .column("value")
1588 .from("table")
1589 .to_owned();
1590
1591 let cte_referencing = SelectStatement::new()
1592 .column("id")
1593 .expr(Expr::col("depth").add(1i32))
1594 .column("next")
1595 .column("value")
1596 .from("table")
1597 .join(
1598 JoinType::InnerJoin,
1599 "cte_traversal",
1600 Expr::col(("cte_traversal", "next")).equals(("table", "id")),
1601 )
1602 .to_owned();
1603
1604 let common_table_expression = CommonTableExpression::new()
1605 .query(
1606 base_query
1607 .clone()
1608 .union(UnionType::All, cte_referencing)
1609 .to_owned(),
1610 )
1611 .columns(["id", "depth", "next", "value"])
1612 .table_name("cte_traversal")
1613 .to_owned();
1614
1615 let select = SelectStatement::new()
1616 .column(ColumnRef::Asterisk(None))
1617 .from("cte_traversal")
1618 .to_owned();
1619
1620 let with_clause = WithClause::new()
1621 .recursive(true)
1622 .cte(common_table_expression)
1623 .cycle(Cycle::new_from_expr_set_using(
1624 Expr::column("id"),
1625 "looped",
1626 "traversal_path",
1627 ))
1628 .to_owned();
1629
1630 let with_query = select.with(with_clause).to_owned();
1631
1632 assert_eq!(
1633 DbBackend::MySql.build(&with_query),
1634 Statement::from_sql_and_values(
1635 DbBackend::MySql,
1636 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`"#,
1637 [1.into(), 1.into()]
1638 )
1639 );
1640 }
1641
1642 #[test]
1643 fn column_names_from_query_result() {
1644 let mut values = BTreeMap::new();
1645 values.insert("id".to_string(), Value::Int(Some(1)));
1646 values.insert("name".to_string(), Value::String(Some("Abc".to_owned())));
1647 let query_result = QueryResult {
1648 row: QueryResultRow::Mock(crate::MockRow { values }),
1649 };
1650 assert_eq!(
1651 query_result.column_names(),
1652 vec!["id".to_owned(), "name".to_owned()]
1653 );
1654 }
1655}