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