diesel/pg/types/floats/
mod.rs1use crate::deserialize::{self, Defaultable, FromSql, FromSqlRow};
2use crate::expression::AsExpression;
3use crate::pg::{Pg, PgValue};
4use crate::serialize::{self, IsNull, Output, ToSql};
5use crate::sql_types;
6use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
7use std::error::Error;
8
9#[cfg(feature = "quickcheck")]
10mod quickcheck_impls;
11
12#[derive(Debug, Default, Clone, PartialEq, Eq, AsExpression, FromSqlRow)]
13#[diesel(sql_type = sql_types::Numeric)]
14pub enum PgNumeric {
17 Positive {
19 weight: i16,
21 scale: u16,
23 digits: Vec<i16>,
25 },
26 Negative {
28 weight: i16,
30 scale: u16,
32 digits: Vec<i16>,
34 },
35 #[default]
37 NaN,
38}
39
40#[derive(Debug, Clone, Copy)]
41#[allow(dead_code)] struct InvalidNumericSign(u16);
43
44impl ::std::fmt::Display for InvalidNumericSign {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> ::std::fmt::Result {
46 f.write_str("sign for numeric field was not one of 0, 0x4000, 0xC000")
47 }
48}
49
50impl Error for InvalidNumericSign {}
51
52#[cfg(feature = "postgres_backend")]
53impl FromSql<sql_types::Numeric, Pg> for PgNumeric {
54 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
55 let mut bytes = bytes.as_bytes();
56 let digit_count = bytes.read_u16::<NetworkEndian>()?;
57 let mut digits = Vec::with_capacity(digit_count as usize);
58 let weight = bytes.read_i16::<NetworkEndian>()?;
59 let sign = bytes.read_u16::<NetworkEndian>()?;
60 let scale = bytes.read_u16::<NetworkEndian>()?;
61 for _ in 0..digit_count {
62 digits.push(bytes.read_i16::<NetworkEndian>()?);
63 }
64
65 match sign {
66 0 => Ok(PgNumeric::Positive {
67 weight,
68 scale,
69 digits,
70 }),
71 0x4000 => Ok(PgNumeric::Negative {
72 weight,
73 scale,
74 digits,
75 }),
76 0xC000 => Ok(PgNumeric::NaN),
77 invalid => Err(Box::new(InvalidNumericSign(invalid))),
78 }
79 }
80}
81
82#[cfg(feature = "postgres_backend")]
83impl ToSql<sql_types::Numeric, Pg> for PgNumeric {
84 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
85 let sign = match *self {
86 PgNumeric::Positive { .. } => 0,
87 PgNumeric::Negative { .. } => 0x4000,
88 PgNumeric::NaN => 0xC000,
89 };
90 let empty_vec = Vec::new();
91 let digits = match *self {
92 PgNumeric::Positive { ref digits, .. } | PgNumeric::Negative { ref digits, .. } => {
93 digits
94 }
95 PgNumeric::NaN => &empty_vec,
96 };
97 let weight = match *self {
98 PgNumeric::Positive { weight, .. } | PgNumeric::Negative { weight, .. } => weight,
99 PgNumeric::NaN => 0,
100 };
101 let scale = match *self {
102 PgNumeric::Positive { scale, .. } | PgNumeric::Negative { scale, .. } => scale,
103 PgNumeric::NaN => 0,
104 };
105 out.write_u16::<NetworkEndian>(digits.len().try_into()?)?;
106 out.write_i16::<NetworkEndian>(weight)?;
107 out.write_u16::<NetworkEndian>(sign)?;
108 out.write_u16::<NetworkEndian>(scale)?;
109 for digit in digits.iter() {
110 out.write_i16::<NetworkEndian>(*digit)?;
111 }
112
113 Ok(IsNull::No)
114 }
115}
116
117#[cfg(feature = "postgres_backend")]
118impl Defaultable for PgNumeric {
119 fn default_value() -> Self {
120 Self::default()
121 }
122}
123
124#[cfg(feature = "postgres_backend")]
125impl FromSql<sql_types::Float, Pg> for f32 {
126 fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
127 let mut bytes = value.as_bytes();
128
129 if bytes.len() < 4 {
130 return deserialize::Result::Err(
131 "Received less than 4 bytes while decoding an f32. \
132 Was a numeric accidentally marked as float?"
133 .into(),
134 );
135 }
136
137 if bytes.len() > 4 {
138 return deserialize::Result::Err(
139 "Received more than 4 bytes while decoding an f32. \
140 Was a double accidentally marked as float?"
141 .into(),
142 );
143 }
144
145 bytes
146 .read_f32::<NetworkEndian>()
147 .map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
148 }
149}
150
151#[cfg(feature = "postgres_backend")]
152impl FromSql<sql_types::Double, Pg> for f64 {
153 fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
154 let mut bytes = value.as_bytes();
155
156 if bytes.len() < 8 {
157 return deserialize::Result::Err(
158 "Received less than 8 bytes while decoding an f64. \
159 Was a float accidentally marked as double?"
160 .into(),
161 );
162 }
163
164 if bytes.len() > 8 {
165 return deserialize::Result::Err(
166 "Received more than 8 bytes while decoding an f64. \
167 Was a numeric accidentally marked as double?"
168 .into(),
169 );
170 }
171
172 bytes
173 .read_f64::<NetworkEndian>()
174 .map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
175 }
176}
177
178#[cfg(feature = "postgres_backend")]
179impl ToSql<sql_types::Float, Pg> for f32 {
180 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
181 out.write_f32::<NetworkEndian>(*self)
182 .map(|_| IsNull::No)
183 .map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
184 }
185}
186
187#[cfg(feature = "postgres_backend")]
188impl ToSql<sql_types::Double, Pg> for f64 {
189 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
190 out.write_f64::<NetworkEndian>(*self)
191 .map(|_| IsNull::No)
192 .map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
193 }
194}