pgrx/datum/numeric_support/
convert_primitive.rs1use super::error::Error;
13use crate::datum::{AnyNumeric, FromDatum, Numeric};
14use crate::{direct_function_call, pg_sys};
15use core::str::FromStr;
16use pgrx_pg_sys::errcodes::PgSqlErrorCode;
17use pgrx_pg_sys::panic::CaughtError;
18use pgrx_pg_sys::PgTryBuilder;
19
20macro_rules! anynumeric_to_primitive {
21 ($ty:ty, $as_:ty, $pg_func:ident) => {
22 impl TryFrom<AnyNumeric> for $ty {
23 type Error = Error;
24
25 #[inline]
26 fn try_from(value: AnyNumeric) -> Result<Self, Self::Error> {
27 to_primitive_helper::<$as_>(&value, pg_sys::$pg_func).map(|v| v as $ty)
28 }
29 }
30 };
31}
32
33anynumeric_to_primitive!(isize, i64, numeric_int8);
34anynumeric_to_primitive!(i64, i64, numeric_int8);
35anynumeric_to_primitive!(i32, i32, numeric_int4);
36anynumeric_to_primitive!(i16, i16, numeric_int2);
37anynumeric_to_primitive!(i8, i16, numeric_int2);
38anynumeric_to_primitive!(f32, f32, numeric_float4);
39anynumeric_to_primitive!(f64, f64, numeric_float8);
40
41macro_rules! anynumeric_to_oversized_primitive {
42 ($ty:ty, $as_:ty, $pg_func:ident) => {
43 impl TryFrom<AnyNumeric> for $ty {
46 type Error = Error;
47
48 fn try_from(value: AnyNumeric) -> Result<Self, Self::Error> {
49 match to_primitive_helper::<$as_>(&value, pg_sys::$pg_func) {
50 Ok(value) => Ok(value as $ty),
51 Err(Error::OutOfRange(_)) => <$ty>::from_str(value.to_string().as_str())
52 .map_err(|e| Error::OutOfRange(format!("{e}"))),
53 Err(e) => Err(e),
54 }
55 }
56 }
57 };
58}
59anynumeric_to_oversized_primitive!(i128, i64, numeric_int8);
60anynumeric_to_oversized_primitive!(usize, i64, numeric_int8);
61anynumeric_to_oversized_primitive!(u128, i64, numeric_int8);
62anynumeric_to_oversized_primitive!(u64, i64, numeric_int8);
63anynumeric_to_oversized_primitive!(u32, i32, numeric_int4);
64anynumeric_to_oversized_primitive!(u16, i16, numeric_int2);
65anynumeric_to_oversized_primitive!(u8, i16, numeric_int2);
66
67macro_rules! numeric_to_primitive {
68 ($ty:ty, $as_:ty, $pg_func:ident) => {
69 impl<const P: u32, const S: u32> TryFrom<Numeric<P, S>> for $ty {
70 type Error = Error;
71
72 #[inline]
73 fn try_from(value: Numeric<P, S>) -> Result<Self, Self::Error> {
74 to_primitive_helper::<$as_>(&value, pg_sys::$pg_func).map(|v| v as $ty)
75 }
76 }
77 };
78}
79
80numeric_to_primitive!(isize, i64, numeric_int8);
81numeric_to_primitive!(i64, i64, numeric_int8);
82numeric_to_primitive!(i32, i32, numeric_int4);
83numeric_to_primitive!(i16, i16, numeric_int2);
84numeric_to_primitive!(i8, i16, numeric_int2);
85numeric_to_primitive!(f32, f32, numeric_float4);
86numeric_to_primitive!(f64, f64, numeric_float8);
87
88macro_rules! numeric_to_oversized_primitive {
89 ($ty:ty, $as_:ty, $pg_func:ident) => {
90 impl<const P: u32, const S: u32> TryFrom<Numeric<P, S>> for $ty {
93 type Error = Error;
94
95 fn try_from(value: Numeric<P, S>) -> Result<Self, Self::Error> {
96 match to_primitive_helper::<$as_>(&value, pg_sys::$pg_func) {
97 Ok(value) => Ok(value as $ty),
98 Err(Error::OutOfRange(_)) => <$ty>::from_str(value.to_string().as_str())
99 .map_err(|e| Error::OutOfRange(format!("{e}"))),
100 Err(e) => Err(e),
101 }
102 }
103 }
104 };
105}
106numeric_to_oversized_primitive!(i128, i64, numeric_int8);
107numeric_to_oversized_primitive!(usize, i64, numeric_int8);
108numeric_to_oversized_primitive!(u128, i64, numeric_int8);
109numeric_to_oversized_primitive!(u64, i64, numeric_int8);
110numeric_to_oversized_primitive!(u32, i32, numeric_int4);
111numeric_to_oversized_primitive!(u16, i16, numeric_int2);
112numeric_to_oversized_primitive!(u8, i16, numeric_int2);
113
114fn to_primitive_helper<T: FromDatum>(
115 value: &AnyNumeric,
116 func: unsafe fn(pg_sys::FunctionCallInfo) -> pg_sys::Datum,
117) -> Result<T, Error> {
118 let datum = value.as_datum();
119 PgTryBuilder::new(|| unsafe {
120 Ok(direct_function_call::<T>(func, &[datum]).unwrap_unchecked())
122 })
123 .catch_when(PgSqlErrorCode::ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE, |e| {
124 if let CaughtError::PostgresError(ref ereport) = e {
125 Err(Error::OutOfRange(ereport.message().to_string()))
126 } else {
127 e.rethrow()
128 }
129 })
130 .catch_when(PgSqlErrorCode::ERRCODE_FEATURE_NOT_SUPPORTED, |e| {
131 if let CaughtError::PostgresError(ref ereport) = e {
132 Err(Error::ConversionNotSupported(ereport.message().to_string()))
133 } else {
134 e.rethrow()
135 }
136 })
137 .execute()
138}