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