Skip to content

Commit a7b00a6

Browse files
committed
allow decoding money as f64
for precise money handling, you should still use decimal types but allowing decoding all mssql types to native rust types makes things easier
1 parent fb4b5dc commit a7b00a6

File tree

3 files changed

+35
-6
lines changed

3 files changed

+35
-6
lines changed

sqlx-core/src/mssql/types/decimal_tools.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub(crate) fn decode_money_bytes(bytes: &[u8]) -> Result<i64, BoxDynError> {
1313
};
1414
Ok(amount)
1515
}
16+
17+
/// Returns
1618
pub(crate) fn decode_numeric_bytes(bytes: &[u8]) -> Result<(i8, u128), BoxDynError> {
1719
if bytes.is_empty() {
1820
return Err(err_protocol!("numeric bytes cannot be empty").into());

sqlx-core/src/mssql/types/float.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use byteorder::{ByteOrder, LittleEndian};
22

3+
use super::decimal_tools::{decode_money_bytes, decode_numeric_bytes};
34
use crate::decode::Decode;
45
use crate::encode::{Encode, IsNull};
56
use crate::error::BoxDynError;
@@ -46,6 +47,9 @@ impl Type<Mssql> for f64 {
4647
| DataType::DecimalN
4748
| DataType::Numeric
4849
| DataType::NumericN
50+
| DataType::MoneyN
51+
| DataType::Money
52+
| DataType::SmallMoney
4953
)
5054
}
5155
}
@@ -74,6 +78,13 @@ impl Decode<'_, Mssql> for f64 {
7478
DataType::Numeric | DataType::NumericN | DataType::Decimal | DataType::DecimalN => {
7579
decode_numeric(value.as_bytes()?, precision, scale)
7680
}
81+
DataType::MoneyN | DataType::Money | DataType::SmallMoney => {
82+
let numerator = decode_money_bytes(value.as_bytes()?)?;
83+
let denominator = 10_000;
84+
let integer_part = (numerator / denominator) as f64;
85+
let fractional_part = (numerator % denominator) as f64 / denominator as f64;
86+
Ok(integer_part + fractional_part)
87+
}
7788
_ => Err(err_protocol!(
7889
"Decoding {:?} as a float failed because type {:?} is not implemented",
7990
value,
@@ -86,17 +97,16 @@ impl Decode<'_, Mssql> for f64 {
8697

8798
#[allow(clippy::cast_precision_loss)]
8899
fn decode_numeric(bytes: &[u8], _precision: u8, mut scale: u8) -> Result<f64, BoxDynError> {
89-
let sign = if bytes[0] == 0 { -1. } else { 1. };
90-
let rest = &bytes[1..];
91-
let mut fixed_bytes = [0u8; 16];
92-
fixed_bytes[0..rest.len()].copy_from_slice(rest);
93-
let mut numerator = u128::from_le_bytes(fixed_bytes);
100+
let (sign, mut numerator) = decode_numeric_bytes(bytes)?;
101+
94102
while numerator % 10 == 0 && scale > 0 {
95103
numerator /= 10;
96104
scale -= 1;
97105
}
98106
let denominator = 10u128.pow(scale as u32);
99107
let integer_part = (numerator / denominator) as f64;
100108
let fractional_part = (numerator % denominator) as f64 / denominator as f64;
101-
Ok(sign * (integer_part + fractional_part))
109+
let absolute = integer_part + fractional_part;
110+
let positive = sign == 1;
111+
Ok(if positive { absolute } else { -absolute })
102112
}

tests/mssql/types.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,23 @@ mod decimal {
253253
"CAST('214748.3647' AS SMALLMONEY)" == Decimal::from_str_exact("214748.3647").unwrap(),
254254
"CAST('-214748.3648' AS SMALLMONEY)" == Decimal::from_str_exact("-214748.3648").unwrap(),
255255
));
256+
257+
test_type!(money_precision_tests_f64<f64>(
258+
Mssql,
259+
"CAST('0.0000' AS MONEY)" == 0.0000,
260+
"CAST('0.0001' AS MONEY)" == 0.0001,
261+
"CAST('-0.0001' AS MONEY)" == -0.0001,
262+
"CAST('0.9999' AS MONEY)" == 0.9999,
263+
"CAST('-0.9999' AS MONEY)" == -0.9999,
264+
"CAST('1.0000' AS MONEY)" == 1.0000,
265+
"CAST('-1.0000' AS MONEY)" == -1.0000,
266+
"CAST('2.15' AS MONEY)" == 2.15,
267+
"CAST('214748.3647' AS SMALLMONEY)" == 214748.3647,
268+
"CAST('922337203685477.5807' AS MONEY)" == 922337203685477.5807,
269+
"CAST('-922337203685477.5808' AS MONEY)" == -922337203685477.5808,
270+
"CAST('214748.3647' AS SMALLMONEY)" == 214748.3647,
271+
"CAST('-214748.3648' AS SMALLMONEY)" == -214748.3648,
272+
));
256273
}
257274

258275
#[cfg(feature = "bigdecimal")]

0 commit comments

Comments
 (0)