1#![allow(clippy::result_large_err)]
36
37use crate::wit_bindgen;
38use std::sync::Arc;
39
40#[doc(hidden)]
41pub mod wit {
45 #![allow(missing_docs)]
46 use crate::wit_bindgen;
47
48 wit_bindgen::generate!({
49 runtime_path: "crate::wit_bindgen::rt",
50 world: "spin-sdk-pg",
51 path: "wit",
52 generate_all,
53 });
54
55 pub use spin::postgres::postgres;
56}
57
58#[doc(inline)]
59pub use wit::postgres::{
60 Column, DbDataType, DbError, DbValue, Error as PgError, ParameterValue, QueryError,
61 RangeBoundKind,
62};
63
64pub use wit::postgres::Interval;
66
67use chrono::{Datelike, Timelike};
68
69pub struct Connection(wit::postgres::Connection);
137
138#[derive(Default)]
140pub struct OpenOptions {
141 pub ca_root: Option<Certificate>,
144}
145
146pub enum Certificate {
148 FilePath(String),
150 Text(String),
152}
153
154impl Certificate {
155 fn load(self) -> Result<String, Error> {
156 match self {
157 Certificate::FilePath(path) => std::fs::read_to_string(path)
158 .map_err(|e| Error::PgError(PgError::Other(e.to_string()))),
159 Certificate::Text(text) => Ok(text),
160 }
161 }
162}
163
164impl Connection {
165 pub async fn open(address: impl Into<String>) -> Result<Self, Error> {
173 let inner = wit::postgres::Connection::open_async(address.into()).await?;
174 Ok(Self(inner))
175 }
176
177 pub async fn open_with_options(
184 address: impl AsRef<str>,
185 options: OpenOptions,
186 ) -> Result<Self, Error> {
187 let builder = wit::postgres::ConnectionBuilder::new(address.as_ref());
188 let OpenOptions { ca_root } = options;
189
190 if let Some(ca_root) = ca_root {
191 let ca_root_text = ca_root.load()?;
192 builder.set_ca_root(&ca_root_text)?;
193 }
194
195 let inner = builder.build_async().await?;
196 Ok(Self(inner))
197 }
198
199 pub async fn query(
204 &self,
205 statement: impl Into<String>,
206 params: impl Into<Vec<ParameterValue>>,
207 ) -> Result<QueryResult, Error> {
208 let (columns, rows, result) = self.0.query_async(statement.into(), params.into()).await?;
209 Ok(QueryResult {
210 columns: Arc::new(columns),
211 rows,
212 result,
213 })
214 }
215
216 pub async fn execute(
221 &self,
222 statement: impl Into<String>,
223 params: impl Into<Vec<ParameterValue>>,
224 ) -> Result<u64, Error> {
225 self.0
226 .execute_async(statement.into(), params.into())
227 .await
228 .map_err(Error::PgError)
229 }
230
231 pub fn into_inner(self) -> wit::postgres::Connection {
233 self.0
234 }
235}
236
237pub struct QueryResult {
239 columns: Arc<Vec<Column>>,
240 rows: wit_bindgen::StreamReader<Vec<DbValue>>,
241 result: wit_bindgen::FutureReader<Result<(), PgError>>,
242}
243
244impl QueryResult {
245 pub fn columns(&self) -> &[Column] {
247 &self.columns
248 }
249
250 pub async fn next(&mut self) -> Option<Row> {
260 self.rows.next().await.map(|r| Row {
261 columns: self.columns.clone(),
262 result: r,
263 })
264 }
265
266 pub async fn result(self) -> Result<(), Error> {
268 self.result.await.map_err(Error::PgError)
269 }
270
271 pub async fn collect(mut self) -> Result<Vec<Row>, Error> {
276 let mut rows = vec![];
277 while let Some(row) = self.next().await {
278 rows.push(row);
279 }
280 self.result.await.map_err(Error::PgError)?;
281 Ok(rows)
282 }
283
284 pub fn rows(&mut self) -> &mut wit_bindgen::StreamReader<Vec<DbValue>> {
295 &mut self.rows
296 }
297
298 #[allow(
300 clippy::type_complexity,
301 reason = "sorry clippy that's just what the inner bits are"
302 )]
303 pub fn into_inner(
304 self,
305 ) -> (
306 Vec<Column>,
307 wit_bindgen::StreamReader<Vec<DbValue>>,
308 wit_bindgen::FutureReader<Result<(), PgError>>,
309 ) {
310 ((*self.columns).clone(), self.rows, self.result)
311 }
312}
313
314pub struct Row {
321 columns: Arc<Vec<wit::postgres::Column>>,
322 result: Vec<DbValue>,
323}
324
325impl Row {
326 pub fn get<T: Decode>(&self, column: &str) -> Option<T> {
360 let i = self.columns.iter().position(|c| c.name == column)?;
361 let db_value = self.result.get(i)?;
362 Decode::decode(db_value).ok()
363 }
364}
365
366impl std::ops::Index<usize> for Row {
367 type Output = DbValue;
368
369 fn index(&self, index: usize) -> &Self::Output {
370 &self.result[index]
371 }
372}
373
374#[derive(Debug, thiserror::Error)]
376pub enum Error {
377 #[error("error value decoding: {0}")]
379 Decode(String),
380 #[error(transparent)]
382 PgError(#[from] PgError),
383}
384
385pub trait Decode: Sized {
387 fn decode(value: &DbValue) -> Result<Self, Error>;
389}
390
391impl<T> Decode for Option<T>
392where
393 T: Decode,
394{
395 fn decode(value: &DbValue) -> Result<Self, Error> {
396 match value {
397 DbValue::DbNull => Ok(None),
398 v => Ok(Some(T::decode(v)?)),
399 }
400 }
401}
402
403impl Decode for bool {
404 fn decode(value: &DbValue) -> Result<Self, Error> {
405 match value {
406 DbValue::Boolean(boolean) => Ok(*boolean),
407 _ => Err(Error::Decode(format_decode_err("BOOL", value))),
408 }
409 }
410}
411
412impl Decode for i16 {
413 fn decode(value: &DbValue) -> Result<Self, Error> {
414 match value {
415 DbValue::Int16(n) => Ok(*n),
416 _ => Err(Error::Decode(format_decode_err("SMALLINT", value))),
417 }
418 }
419}
420
421impl Decode for i32 {
422 fn decode(value: &DbValue) -> Result<Self, Error> {
423 match value {
424 DbValue::Int32(n) => Ok(*n),
425 _ => Err(Error::Decode(format_decode_err("INT", value))),
426 }
427 }
428}
429
430impl Decode for i64 {
431 fn decode(value: &DbValue) -> Result<Self, Error> {
432 match value {
433 DbValue::Int64(n) => Ok(*n),
434 _ => Err(Error::Decode(format_decode_err("BIGINT", value))),
435 }
436 }
437}
438
439impl Decode for f32 {
440 fn decode(value: &DbValue) -> Result<Self, Error> {
441 match value {
442 DbValue::Floating32(n) => Ok(*n),
443 _ => Err(Error::Decode(format_decode_err("REAL", value))),
444 }
445 }
446}
447
448impl Decode for f64 {
449 fn decode(value: &DbValue) -> Result<Self, Error> {
450 match value {
451 DbValue::Floating64(n) => Ok(*n),
452 _ => Err(Error::Decode(format_decode_err("DOUBLE PRECISION", value))),
453 }
454 }
455}
456
457impl Decode for Vec<u8> {
458 fn decode(value: &DbValue) -> Result<Self, Error> {
459 match value {
460 DbValue::Binary(n) => Ok(n.to_owned()),
461 _ => Err(Error::Decode(format_decode_err("BYTEA", value))),
462 }
463 }
464}
465
466impl Decode for String {
467 fn decode(value: &DbValue) -> Result<Self, Error> {
468 match value {
469 DbValue::Str(s) => Ok(s.to_owned()),
470 _ => Err(Error::Decode(format_decode_err(
471 "CHAR, VARCHAR, TEXT",
472 value,
473 ))),
474 }
475 }
476}
477
478impl Decode for chrono::NaiveDate {
479 fn decode(value: &DbValue) -> Result<Self, Error> {
480 match value {
481 DbValue::Date((year, month, day)) => {
482 let naive_date =
483 chrono::NaiveDate::from_ymd_opt(*year, (*month).into(), (*day).into())
484 .ok_or_else(|| {
485 Error::Decode(format!(
486 "invalid date y={}, m={}, d={}",
487 year, month, day
488 ))
489 })?;
490 Ok(naive_date)
491 }
492 _ => Err(Error::Decode(format_decode_err("DATE", value))),
493 }
494 }
495}
496
497impl Decode for chrono::NaiveTime {
498 fn decode(value: &DbValue) -> Result<Self, Error> {
499 match value {
500 DbValue::Time((hour, minute, second, nanosecond)) => {
501 let naive_time = chrono::NaiveTime::from_hms_nano_opt(
502 (*hour).into(),
503 (*minute).into(),
504 (*second).into(),
505 *nanosecond,
506 )
507 .ok_or_else(|| {
508 Error::Decode(format!(
509 "invalid time {}:{}:{}:{}",
510 hour, minute, second, nanosecond
511 ))
512 })?;
513 Ok(naive_time)
514 }
515 _ => Err(Error::Decode(format_decode_err("TIME", value))),
516 }
517 }
518}
519
520impl Decode for chrono::NaiveDateTime {
521 fn decode(value: &DbValue) -> Result<Self, Error> {
522 match value {
523 DbValue::Datetime((year, month, day, hour, minute, second, nanosecond)) => {
524 let naive_date =
525 chrono::NaiveDate::from_ymd_opt(*year, (*month).into(), (*day).into())
526 .ok_or_else(|| {
527 Error::Decode(format!(
528 "invalid date y={}, m={}, d={}",
529 year, month, day
530 ))
531 })?;
532 let naive_time = chrono::NaiveTime::from_hms_nano_opt(
533 (*hour).into(),
534 (*minute).into(),
535 (*second).into(),
536 *nanosecond,
537 )
538 .ok_or_else(|| {
539 Error::Decode(format!(
540 "invalid time {}:{}:{}:{}",
541 hour, minute, second, nanosecond
542 ))
543 })?;
544 let dt = chrono::NaiveDateTime::new(naive_date, naive_time);
545 Ok(dt)
546 }
547 _ => Err(Error::Decode(format_decode_err("DATETIME", value))),
548 }
549 }
550}
551
552impl Decode for chrono::Duration {
553 fn decode(value: &DbValue) -> Result<Self, Error> {
554 match value {
555 DbValue::Timestamp(n) => Ok(chrono::Duration::seconds(*n)),
556 _ => Err(Error::Decode(format_decode_err("BIGINT", value))),
557 }
558 }
559}
560
561#[cfg(feature = "postgres4-types")]
562impl Decode for uuid::Uuid {
563 fn decode(value: &DbValue) -> Result<Self, Error> {
564 match value {
565 DbValue::Uuid(s) => uuid::Uuid::parse_str(s).map_err(|e| Error::Decode(e.to_string())),
566 _ => Err(Error::Decode(format_decode_err("UUID", value))),
567 }
568 }
569}
570
571#[cfg(feature = "json")]
572impl Decode for serde_json::Value {
573 fn decode(value: &DbValue) -> Result<Self, Error> {
574 from_jsonb(value)
575 }
576}
577
578#[cfg(feature = "json")]
580pub fn from_jsonb<'a, T: serde::Deserialize<'a>>(value: &'a DbValue) -> Result<T, Error> {
581 match value {
582 DbValue::Jsonb(j) => serde_json::from_slice(j).map_err(|e| Error::Decode(e.to_string())),
583 _ => Err(Error::Decode(format_decode_err("JSONB", value))),
584 }
585}
586
587#[cfg(feature = "postgres4-types")]
588impl Decode for rust_decimal::Decimal {
589 fn decode(value: &DbValue) -> Result<Self, Error> {
590 match value {
591 DbValue::Decimal(s) => {
592 rust_decimal::Decimal::from_str_exact(s).map_err(|e| Error::Decode(e.to_string()))
593 }
594 _ => Err(Error::Decode(format_decode_err("NUMERIC", value))),
595 }
596 }
597}
598
599#[cfg(feature = "postgres4-types")]
600fn bound_type_from_wit(kind: RangeBoundKind) -> postgres_range::BoundType {
601 match kind {
602 RangeBoundKind::Inclusive => postgres_range::BoundType::Inclusive,
603 RangeBoundKind::Exclusive => postgres_range::BoundType::Exclusive,
604 }
605}
606
607#[cfg(feature = "postgres4-types")]
608impl Decode for postgres_range::Range<i32> {
609 fn decode(value: &DbValue) -> Result<Self, Error> {
610 match value {
611 DbValue::RangeInt32((lbound, ubound)) => {
612 let lower = lbound.map(|(value, kind)| {
613 postgres_range::RangeBound::new(value, bound_type_from_wit(kind))
614 });
615 let upper = ubound.map(|(value, kind)| {
616 postgres_range::RangeBound::new(value, bound_type_from_wit(kind))
617 });
618 Ok(postgres_range::Range::new(lower, upper))
619 }
620 _ => Err(Error::Decode(format_decode_err("INT4RANGE", value))),
621 }
622 }
623}
624
625#[cfg(feature = "postgres4-types")]
626impl Decode for postgres_range::Range<i64> {
627 fn decode(value: &DbValue) -> Result<Self, Error> {
628 match value {
629 DbValue::RangeInt64((lbound, ubound)) => {
630 let lower = lbound.map(|(value, kind)| {
631 postgres_range::RangeBound::new(value, bound_type_from_wit(kind))
632 });
633 let upper = ubound.map(|(value, kind)| {
634 postgres_range::RangeBound::new(value, bound_type_from_wit(kind))
635 });
636 Ok(postgres_range::Range::new(lower, upper))
637 }
638 _ => Err(Error::Decode(format_decode_err("INT8RANGE", value))),
639 }
640 }
641}
642
643#[cfg(feature = "postgres4-types")]
646impl Decode
647 for (
648 Option<(rust_decimal::Decimal, RangeBoundKind)>,
649 Option<(rust_decimal::Decimal, RangeBoundKind)>,
650 )
651{
652 fn decode(value: &DbValue) -> Result<Self, Error> {
653 fn parse(
654 value: &str,
655 kind: RangeBoundKind,
656 ) -> Result<(rust_decimal::Decimal, RangeBoundKind), Error> {
657 let dec = rust_decimal::Decimal::from_str_exact(value)
658 .map_err(|e| Error::Decode(e.to_string()))?;
659 Ok((dec, kind))
660 }
661
662 match value {
663 DbValue::RangeDecimal((lbound, ubound)) => {
664 let lower = lbound
665 .as_ref()
666 .map(|(value, kind)| parse(value, *kind))
667 .transpose()?;
668 let upper = ubound
669 .as_ref()
670 .map(|(value, kind)| parse(value, *kind))
671 .transpose()?;
672 Ok((lower, upper))
673 }
674 _ => Err(Error::Decode(format_decode_err("NUMERICRANGE", value))),
675 }
676 }
677}
678
679impl Decode for Vec<Option<i32>> {
682 fn decode(value: &DbValue) -> Result<Self, Error> {
683 match value {
684 DbValue::ArrayInt32(a) => Ok(a.to_vec()),
685 _ => Err(Error::Decode(format_decode_err("INT4[]", value))),
686 }
687 }
688}
689
690impl Decode for Vec<Option<i64>> {
691 fn decode(value: &DbValue) -> Result<Self, Error> {
692 match value {
693 DbValue::ArrayInt64(a) => Ok(a.to_vec()),
694 _ => Err(Error::Decode(format_decode_err("INT8[]", value))),
695 }
696 }
697}
698
699impl Decode for Vec<Option<String>> {
700 fn decode(value: &DbValue) -> Result<Self, Error> {
701 match value {
702 DbValue::ArrayStr(a) => Ok(a.to_vec()),
703 _ => Err(Error::Decode(format_decode_err("TEXT[]", value))),
704 }
705 }
706}
707
708#[cfg(feature = "postgres4-types")]
709fn map_decimal(s: &Option<String>) -> Result<Option<rust_decimal::Decimal>, Error> {
710 s.as_ref()
711 .map(|s| rust_decimal::Decimal::from_str_exact(s))
712 .transpose()
713 .map_err(|e| Error::Decode(e.to_string()))
714}
715
716#[cfg(feature = "postgres4-types")]
717impl Decode for Vec<Option<rust_decimal::Decimal>> {
718 fn decode(value: &DbValue) -> Result<Self, Error> {
719 match value {
720 DbValue::ArrayDecimal(a) => {
721 let decs = a.iter().map(map_decimal).collect::<Result<_, _>>()?;
722 Ok(decs)
723 }
724 _ => Err(Error::Decode(format_decode_err("NUMERIC[]", value))),
725 }
726 }
727}
728
729impl Decode for Interval {
730 fn decode(value: &DbValue) -> Result<Self, Error> {
731 match value {
732 DbValue::Interval(i) => Ok(*i),
733 _ => Err(Error::Decode(format_decode_err("INTERVAL", value))),
734 }
735 }
736}
737
738macro_rules! impl_parameter_value_conversions {
739 ($($ty:ty => $id:ident),*) => {
740 $(
741 impl From<$ty> for ParameterValue {
742 fn from(v: $ty) -> ParameterValue {
743 ParameterValue::$id(v)
744 }
745 }
746 )*
747 };
748}
749
750impl_parameter_value_conversions! {
751 i8 => Int8,
752 i16 => Int16,
753 i32 => Int32,
754 i64 => Int64,
755 f32 => Floating32,
756 f64 => Floating64,
757 bool => Boolean,
758 String => Str,
759 Vec<u8> => Binary,
760 Vec<Option<i32>> => ArrayInt32,
761 Vec<Option<i64>> => ArrayInt64,
762 Vec<Option<String>> => ArrayStr
763}
764
765impl From<chrono::NaiveDateTime> for ParameterValue {
766 fn from(v: chrono::NaiveDateTime) -> ParameterValue {
767 ParameterValue::Datetime((
768 v.year(),
769 v.month() as u8,
770 v.day() as u8,
771 v.hour() as u8,
772 v.minute() as u8,
773 v.second() as u8,
774 v.nanosecond(),
775 ))
776 }
777}
778
779impl From<chrono::NaiveTime> for ParameterValue {
780 fn from(v: chrono::NaiveTime) -> ParameterValue {
781 ParameterValue::Time((
782 v.hour() as u8,
783 v.minute() as u8,
784 v.second() as u8,
785 v.nanosecond(),
786 ))
787 }
788}
789
790impl From<chrono::NaiveDate> for ParameterValue {
791 fn from(v: chrono::NaiveDate) -> ParameterValue {
792 ParameterValue::Date((v.year(), v.month() as u8, v.day() as u8))
793 }
794}
795
796impl From<chrono::TimeDelta> for ParameterValue {
797 fn from(v: chrono::TimeDelta) -> ParameterValue {
798 ParameterValue::Timestamp(v.num_seconds())
799 }
800}
801
802#[cfg(feature = "postgres4-types")]
803impl From<uuid::Uuid> for ParameterValue {
804 fn from(v: uuid::Uuid) -> ParameterValue {
805 ParameterValue::Uuid(v.to_string())
806 }
807}
808
809#[cfg(feature = "json")]
810impl TryFrom<serde_json::Value> for ParameterValue {
811 type Error = serde_json::Error;
812
813 fn try_from(v: serde_json::Value) -> Result<ParameterValue, Self::Error> {
814 jsonb(&v)
815 }
816}
817
818#[cfg(feature = "json")]
820pub fn jsonb<T: serde::Serialize>(value: &T) -> Result<ParameterValue, serde_json::Error> {
821 let json = serde_json::to_vec(value)?;
822 Ok(ParameterValue::Jsonb(json))
823}
824
825#[cfg(feature = "postgres4-types")]
826impl From<rust_decimal::Decimal> for ParameterValue {
827 fn from(v: rust_decimal::Decimal) -> ParameterValue {
828 ParameterValue::Decimal(v.to_string())
829 }
830}
831
832#[allow(
836 clippy::type_complexity,
837 reason = "I sure hope 'blame Alex' works here too"
838)]
839fn range_bounds_to_wit<T, U>(
840 range: impl std::ops::RangeBounds<T>,
841 f: impl Fn(&T) -> U,
842) -> (Option<(U, RangeBoundKind)>, Option<(U, RangeBoundKind)>) {
843 (
844 range_bound_to_wit(range.start_bound(), &f),
845 range_bound_to_wit(range.end_bound(), &f),
846 )
847}
848
849fn range_bound_to_wit<T, U>(
850 bound: std::ops::Bound<&T>,
851 f: &dyn Fn(&T) -> U,
852) -> Option<(U, RangeBoundKind)> {
853 match bound {
854 std::ops::Bound::Included(v) => Some((f(v), RangeBoundKind::Inclusive)),
855 std::ops::Bound::Excluded(v) => Some((f(v), RangeBoundKind::Exclusive)),
856 std::ops::Bound::Unbounded => None,
857 }
858}
859
860#[cfg(feature = "postgres4-types")]
861fn pg_range_bound_to_wit<S: postgres_range::BoundSided, T: Copy>(
862 bound: &postgres_range::RangeBound<S, T>,
863) -> (T, RangeBoundKind) {
864 let kind = match &bound.type_ {
865 postgres_range::BoundType::Inclusive => RangeBoundKind::Inclusive,
866 postgres_range::BoundType::Exclusive => RangeBoundKind::Exclusive,
867 };
868 (bound.value, kind)
869}
870
871impl From<std::ops::Range<i32>> for ParameterValue {
872 fn from(v: std::ops::Range<i32>) -> ParameterValue {
873 ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
874 }
875}
876
877impl From<std::ops::RangeInclusive<i32>> for ParameterValue {
878 fn from(v: std::ops::RangeInclusive<i32>) -> ParameterValue {
879 ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
880 }
881}
882
883impl From<std::ops::RangeFrom<i32>> for ParameterValue {
884 fn from(v: std::ops::RangeFrom<i32>) -> ParameterValue {
885 ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
886 }
887}
888
889impl From<std::ops::RangeTo<i32>> for ParameterValue {
890 fn from(v: std::ops::RangeTo<i32>) -> ParameterValue {
891 ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
892 }
893}
894
895impl From<std::ops::RangeToInclusive<i32>> for ParameterValue {
896 fn from(v: std::ops::RangeToInclusive<i32>) -> ParameterValue {
897 ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
898 }
899}
900
901#[cfg(feature = "postgres4-types")]
902impl From<postgres_range::Range<i32>> for ParameterValue {
903 fn from(v: postgres_range::Range<i32>) -> ParameterValue {
904 let lbound = v.lower().map(pg_range_bound_to_wit);
905 let ubound = v.upper().map(pg_range_bound_to_wit);
906 ParameterValue::RangeInt32((lbound, ubound))
907 }
908}
909
910impl From<std::ops::Range<i64>> for ParameterValue {
911 fn from(v: std::ops::Range<i64>) -> ParameterValue {
912 ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
913 }
914}
915
916impl From<std::ops::RangeInclusive<i64>> for ParameterValue {
917 fn from(v: std::ops::RangeInclusive<i64>) -> ParameterValue {
918 ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
919 }
920}
921
922impl From<std::ops::RangeFrom<i64>> for ParameterValue {
923 fn from(v: std::ops::RangeFrom<i64>) -> ParameterValue {
924 ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
925 }
926}
927
928impl From<std::ops::RangeTo<i64>> for ParameterValue {
929 fn from(v: std::ops::RangeTo<i64>) -> ParameterValue {
930 ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
931 }
932}
933
934impl From<std::ops::RangeToInclusive<i64>> for ParameterValue {
935 fn from(v: std::ops::RangeToInclusive<i64>) -> ParameterValue {
936 ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
937 }
938}
939
940#[cfg(feature = "postgres4-types")]
941impl From<postgres_range::Range<i64>> for ParameterValue {
942 fn from(v: postgres_range::Range<i64>) -> ParameterValue {
943 let lbound = v.lower().map(pg_range_bound_to_wit);
944 let ubound = v.upper().map(pg_range_bound_to_wit);
945 ParameterValue::RangeInt64((lbound, ubound))
946 }
947}
948
949#[cfg(feature = "postgres4-types")]
950impl From<std::ops::Range<rust_decimal::Decimal>> for ParameterValue {
951 fn from(v: std::ops::Range<rust_decimal::Decimal>) -> ParameterValue {
952 ParameterValue::RangeDecimal(range_bounds_to_wit(v, |d| d.to_string()))
953 }
954}
955
956impl From<Vec<i32>> for ParameterValue {
957 fn from(v: Vec<i32>) -> ParameterValue {
958 ParameterValue::ArrayInt32(v.into_iter().map(Some).collect())
959 }
960}
961
962impl From<Vec<i64>> for ParameterValue {
963 fn from(v: Vec<i64>) -> ParameterValue {
964 ParameterValue::ArrayInt64(v.into_iter().map(Some).collect())
965 }
966}
967
968impl From<Vec<String>> for ParameterValue {
969 fn from(v: Vec<String>) -> ParameterValue {
970 ParameterValue::ArrayStr(v.into_iter().map(Some).collect())
971 }
972}
973
974#[cfg(feature = "postgres4-types")]
975impl From<Vec<Option<rust_decimal::Decimal>>> for ParameterValue {
976 fn from(v: Vec<Option<rust_decimal::Decimal>>) -> ParameterValue {
977 let strs = v
978 .into_iter()
979 .map(|optd| optd.map(|d| d.to_string()))
980 .collect();
981 ParameterValue::ArrayDecimal(strs)
982 }
983}
984
985#[cfg(feature = "postgres4-types")]
986impl From<Vec<rust_decimal::Decimal>> for ParameterValue {
987 fn from(v: Vec<rust_decimal::Decimal>) -> ParameterValue {
988 let strs = v.into_iter().map(|d| Some(d.to_string())).collect();
989 ParameterValue::ArrayDecimal(strs)
990 }
991}
992
993impl From<Interval> for ParameterValue {
994 fn from(v: Interval) -> ParameterValue {
995 ParameterValue::Interval(v)
996 }
997}
998
999impl<T: Into<ParameterValue>> From<Option<T>> for ParameterValue {
1000 fn from(o: Option<T>) -> ParameterValue {
1001 match o {
1002 Some(v) => v.into(),
1003 None => ParameterValue::DbNull,
1004 }
1005 }
1006}
1007
1008fn format_decode_err(types: &str, value: &DbValue) -> String {
1009 format!("Expected {} from the DB but got {:?}", types, value)
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014 use chrono::NaiveDateTime;
1015
1016 use super::*;
1017
1018 #[test]
1019 fn boolean() {
1020 assert!(bool::decode(&DbValue::Boolean(true)).unwrap());
1021 assert!(bool::decode(&DbValue::Int32(0)).is_err());
1022 assert!(Option::<bool>::decode(&DbValue::DbNull).unwrap().is_none());
1023 }
1024
1025 #[test]
1026 fn int16() {
1027 assert_eq!(i16::decode(&DbValue::Int16(0)).unwrap(), 0);
1028 assert!(i16::decode(&DbValue::Int32(0)).is_err());
1029 assert!(Option::<i16>::decode(&DbValue::DbNull).unwrap().is_none());
1030 }
1031
1032 #[test]
1033 fn int32() {
1034 assert_eq!(i32::decode(&DbValue::Int32(0)).unwrap(), 0);
1035 assert!(i32::decode(&DbValue::Boolean(false)).is_err());
1036 assert!(Option::<i32>::decode(&DbValue::DbNull).unwrap().is_none());
1037 }
1038
1039 #[test]
1040 fn int64() {
1041 assert_eq!(i64::decode(&DbValue::Int64(0)).unwrap(), 0);
1042 assert!(i64::decode(&DbValue::Boolean(false)).is_err());
1043 assert!(Option::<i64>::decode(&DbValue::DbNull).unwrap().is_none());
1044 }
1045
1046 #[test]
1047 fn floating32() {
1048 assert!(f32::decode(&DbValue::Floating32(0.0)).is_ok());
1049 assert!(f32::decode(&DbValue::Boolean(false)).is_err());
1050 assert!(Option::<f32>::decode(&DbValue::DbNull).unwrap().is_none());
1051 }
1052
1053 #[test]
1054 fn floating64() {
1055 assert!(f64::decode(&DbValue::Floating64(0.0)).is_ok());
1056 assert!(f64::decode(&DbValue::Boolean(false)).is_err());
1057 assert!(Option::<f64>::decode(&DbValue::DbNull).unwrap().is_none());
1058 }
1059
1060 #[test]
1061 fn str() {
1062 assert_eq!(
1063 String::decode(&DbValue::Str(String::from("foo"))).unwrap(),
1064 String::from("foo")
1065 );
1066
1067 assert!(String::decode(&DbValue::Int32(0)).is_err());
1068 assert!(
1069 Option::<String>::decode(&DbValue::DbNull)
1070 .unwrap()
1071 .is_none()
1072 );
1073 }
1074
1075 #[test]
1076 fn binary() {
1077 assert!(Vec::<u8>::decode(&DbValue::Binary(vec![0, 0])).is_ok());
1078 assert!(Vec::<u8>::decode(&DbValue::Boolean(false)).is_err());
1079 assert!(
1080 Option::<Vec<u8>>::decode(&DbValue::DbNull)
1081 .unwrap()
1082 .is_none()
1083 );
1084 }
1085
1086 #[test]
1087 fn date() {
1088 assert_eq!(
1089 chrono::NaiveDate::decode(&DbValue::Date((1, 2, 4))).unwrap(),
1090 chrono::NaiveDate::from_ymd_opt(1, 2, 4).unwrap()
1091 );
1092 assert_ne!(
1093 chrono::NaiveDate::decode(&DbValue::Date((1, 2, 4))).unwrap(),
1094 chrono::NaiveDate::from_ymd_opt(1, 2, 5).unwrap()
1095 );
1096 assert!(
1097 Option::<chrono::NaiveDate>::decode(&DbValue::DbNull)
1098 .unwrap()
1099 .is_none()
1100 );
1101 }
1102
1103 #[test]
1104 fn time() {
1105 assert_eq!(
1106 chrono::NaiveTime::decode(&DbValue::Time((1, 2, 3, 4))).unwrap(),
1107 chrono::NaiveTime::from_hms_nano_opt(1, 2, 3, 4).unwrap()
1108 );
1109 assert_ne!(
1110 chrono::NaiveTime::decode(&DbValue::Time((1, 2, 3, 4))).unwrap(),
1111 chrono::NaiveTime::from_hms_nano_opt(1, 2, 4, 5).unwrap()
1112 );
1113 assert!(
1114 Option::<chrono::NaiveTime>::decode(&DbValue::DbNull)
1115 .unwrap()
1116 .is_none()
1117 );
1118 }
1119
1120 #[test]
1121 fn datetime() {
1122 let date = chrono::NaiveDate::from_ymd_opt(1, 2, 3).unwrap();
1123 let mut time = chrono::NaiveTime::from_hms_nano_opt(4, 5, 6, 7).unwrap();
1124 assert_eq!(
1125 chrono::NaiveDateTime::decode(&DbValue::Datetime((1, 2, 3, 4, 5, 6, 7))).unwrap(),
1126 chrono::NaiveDateTime::new(date, time)
1127 );
1128
1129 time = chrono::NaiveTime::from_hms_nano_opt(4, 5, 6, 8).unwrap();
1130 assert_ne!(
1131 NaiveDateTime::decode(&DbValue::Datetime((1, 2, 3, 4, 5, 6, 7))).unwrap(),
1132 chrono::NaiveDateTime::new(date, time)
1133 );
1134 assert!(
1135 Option::<chrono::NaiveDateTime>::decode(&DbValue::DbNull)
1136 .unwrap()
1137 .is_none()
1138 );
1139 }
1140
1141 #[test]
1142 fn timestamp() {
1143 assert_eq!(
1144 chrono::Duration::decode(&DbValue::Timestamp(1)).unwrap(),
1145 chrono::Duration::seconds(1),
1146 );
1147 assert_ne!(
1148 chrono::Duration::decode(&DbValue::Timestamp(2)).unwrap(),
1149 chrono::Duration::seconds(1)
1150 );
1151 assert!(
1152 Option::<chrono::Duration>::decode(&DbValue::DbNull)
1153 .unwrap()
1154 .is_none()
1155 );
1156 }
1157
1158 #[test]
1159 #[cfg(feature = "postgres4-types")]
1160 fn uuid() {
1161 let uuid_str = "12341234-1234-1234-1234-123412341234";
1162 assert_eq!(
1163 uuid::Uuid::try_parse(uuid_str).unwrap(),
1164 uuid::Uuid::decode(&DbValue::Uuid(uuid_str.to_owned())).unwrap(),
1165 );
1166 assert!(
1167 Option::<uuid::Uuid>::decode(&DbValue::DbNull)
1168 .unwrap()
1169 .is_none()
1170 );
1171 }
1172
1173 #[derive(Debug, serde::Deserialize, PartialEq)]
1174 struct JsonTest {
1175 hello: String,
1176 }
1177
1178 #[test]
1179 #[cfg(feature = "json")]
1180 fn jsonb() {
1181 let json_val = serde_json::json!({
1182 "hello": "world"
1183 });
1184 let dbval = DbValue::Jsonb(r#"{"hello":"world"}"#.into());
1185
1186 assert_eq!(json_val, serde_json::Value::decode(&dbval).unwrap(),);
1187
1188 let json_struct = JsonTest {
1189 hello: "world".to_owned(),
1190 };
1191 assert_eq!(json_struct, from_jsonb(&dbval).unwrap());
1192 }
1193
1194 #[test]
1195 #[cfg(feature = "postgres4-types")]
1196 fn ranges() {
1197 let i32_range = postgres_range::Range::<i32>::decode(&DbValue::RangeInt32((
1198 Some((45, RangeBoundKind::Inclusive)),
1199 Some((89, RangeBoundKind::Exclusive)),
1200 )))
1201 .unwrap();
1202 assert_eq!(45, i32_range.lower().unwrap().value);
1203 assert_eq!(
1204 postgres_range::BoundType::Inclusive,
1205 i32_range.lower().unwrap().type_
1206 );
1207 assert_eq!(89, i32_range.upper().unwrap().value);
1208 assert_eq!(
1209 postgres_range::BoundType::Exclusive,
1210 i32_range.upper().unwrap().type_
1211 );
1212
1213 let i32_range_from = postgres_range::Range::<i32>::decode(&DbValue::RangeInt32((
1214 Some((45, RangeBoundKind::Inclusive)),
1215 None,
1216 )))
1217 .unwrap();
1218 assert!(i32_range_from.upper().is_none());
1219
1220 let i64_range = postgres_range::Range::<i64>::decode(&DbValue::RangeInt64((
1221 Some((4567456745674567, RangeBoundKind::Inclusive)),
1222 Some((890189018901890189, RangeBoundKind::Exclusive)),
1223 )))
1224 .unwrap();
1225 assert_eq!(4567456745674567, i64_range.lower().unwrap().value);
1226 assert_eq!(890189018901890189, i64_range.upper().unwrap().value);
1227
1228 #[allow(clippy::type_complexity)]
1229 let (dec_lbound, dec_ubound): (
1230 Option<(rust_decimal::Decimal, RangeBoundKind)>,
1231 Option<(rust_decimal::Decimal, RangeBoundKind)>,
1232 ) = Decode::decode(&DbValue::RangeDecimal((
1233 Some(("4567.8901".to_owned(), RangeBoundKind::Inclusive)),
1234 Some(("8901.2345678901".to_owned(), RangeBoundKind::Exclusive)),
1235 )))
1236 .unwrap();
1237 assert_eq!(
1238 rust_decimal::Decimal::from_i128_with_scale(45678901, 4),
1239 dec_lbound.unwrap().0
1240 );
1241 assert_eq!(
1242 rust_decimal::Decimal::from_i128_with_scale(89012345678901, 10),
1243 dec_ubound.unwrap().0
1244 );
1245 }
1246
1247 #[test]
1248 #[cfg(feature = "postgres4-types")]
1249 fn arrays() {
1250 let v32 = vec![Some(123), None, Some(456)];
1251 let i32_arr = Vec::<Option<i32>>::decode(&DbValue::ArrayInt32(v32.clone())).unwrap();
1252 assert_eq!(v32, i32_arr);
1253
1254 let v64 = vec![Some(123), None, Some(456)];
1255 let i64_arr = Vec::<Option<i64>>::decode(&DbValue::ArrayInt64(v64.clone())).unwrap();
1256 assert_eq!(v64, i64_arr);
1257
1258 let vdec = vec![Some("1.23".to_owned()), None];
1259 let dec_arr =
1260 Vec::<Option<rust_decimal::Decimal>>::decode(&DbValue::ArrayDecimal(vdec)).unwrap();
1261 assert_eq!(
1262 vec![
1263 Some(rust_decimal::Decimal::from_i128_with_scale(123, 2)),
1264 None
1265 ],
1266 dec_arr
1267 );
1268
1269 let vstr = vec![Some("alice".to_owned()), None, Some("bob".to_owned())];
1270 let str_arr = Vec::<Option<String>>::decode(&DbValue::ArrayStr(vstr.clone())).unwrap();
1271 assert_eq!(vstr, str_arr);
1272 }
1273}