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