Skip to content

Commit c7d1915

Browse files
committed
add Money support to mssql
1 parent d5ed627 commit c7d1915

File tree

5 files changed

+87
-24
lines changed

5 files changed

+87
-24
lines changed

Cargo.lock

Lines changed: 12 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sqlx-core/src/mssql/options/parse.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ impl FromStr for MssqlConnectOptions {
2828
/// - `strict`: Requires encryption and validates the server certificate.
2929
/// - `mandatory` or `true` or `yes`: Requires encryption but doesn't validate the server certificate.
3030
/// - `optional` or `false` or `no`: Uses encryption if available, falls back to unencrypted.
31+
/// - `not_supported`: No encryption.
3132
/// - `sslrootcert` or `ssl-root-cert` or `ssl-ca`: Path to the root certificate for validating the server's SSL certificate.
3233
/// - `trust_server_certificate`: When true, skips validation of the server's SSL certificate. Use with caution as it makes the connection vulnerable to man-in-the-middle attacks.
3334
/// - `hostname_in_certificate`: The hostname expected in the server's SSL certificate. Use this when the server's hostname doesn't match the certificate.

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ impl Type<Mssql> for BigDecimal {
2323
fn compatible(ty: &MssqlTypeInfo) -> bool {
2424
matches!(
2525
ty.0.ty,
26-
DataType::Numeric | DataType::NumericN | DataType::Decimal | DataType::DecimalN
26+
DataType::Numeric
27+
| DataType::NumericN
28+
| DataType::Decimal
29+
| DataType::DecimalN
30+
| DataType::MoneyN
31+
| DataType::Money
32+
| DataType::SmallMoney
2733
)
2834
}
2935
}
@@ -58,15 +64,32 @@ impl Encode<'_, Mssql> for BigDecimal {
5864
impl Decode<'_, Mssql> for BigDecimal {
5965
fn decode(value: MssqlValueRef<'_>) -> Result<Self, BoxDynError> {
6066
let ty = value.type_info.0.ty;
61-
if !matches!(
62-
ty,
63-
DataType::Decimal | DataType::DecimalN | DataType::Numeric | DataType::NumericN
64-
) {
65-
return Err(err_protocol!("expected numeric type, got {:?}", value.type_info.0).into());
67+
match ty {
68+
DataType::Decimal | DataType::DecimalN | DataType::Numeric | DataType::NumericN => {
69+
let precision = value.type_info.0.precision;
70+
let scale = value.type_info.0.scale;
71+
decode_numeric(value.as_bytes()?, precision, scale)
72+
}
73+
DataType::MoneyN | DataType::Money | DataType::SmallMoney => {
74+
// Money is stored as an 8-byte integer representing the amount in ten-thousandths of a currency unit
75+
let bytes = value.as_bytes()?;
76+
// println!("bytes: {:?}", bytes);
77+
if bytes.len() != 8 && bytes.len() != 4 {
78+
return Err(
79+
err_protocol!("expected 8/4 bytes for Money, got {}", bytes.len()).into(),
80+
);
81+
}
82+
let amount = if bytes.len() == 8 {
83+
let amount_h = i32::from_le_bytes(bytes[0..4].try_into()?) as i64;
84+
let amount_l = u32::from_le_bytes(bytes[4..8].try_into()?) as i64;
85+
(amount_h << 32) | amount_l
86+
} else {
87+
i32::from_le_bytes(bytes.try_into()?) as i64
88+
};
89+
Ok(BigDecimal::new(BigInt::from(amount), 4))
90+
}
91+
_ => Err(err_protocol!("expected numeric type, got {:?}", value.type_info.0).into()),
6692
}
67-
let precision = value.type_info.0.precision;
68-
let scale = value.type_info.0.scale;
69-
decode_numeric(value.as_bytes()?, precision, scale)
7093
}
7194
}
7295

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ impl Type<Mssql> for Decimal {
2121
fn compatible(ty: &MssqlTypeInfo) -> bool {
2222
matches!(
2323
ty.0.ty,
24-
DataType::Numeric | DataType::NumericN | DataType::Decimal | DataType::DecimalN
24+
DataType::Numeric
25+
| DataType::NumericN
26+
| DataType::Decimal
27+
| DataType::DecimalN
28+
| DataType::MoneyN
29+
| DataType::Money
30+
| DataType::SmallMoney
2531
)
2632
}
2733
}
@@ -49,15 +55,32 @@ impl Encode<'_, Mssql> for Decimal {
4955
impl Decode<'_, Mssql> for Decimal {
5056
fn decode(value: MssqlValueRef<'_>) -> Result<Self, BoxDynError> {
5157
let ty = value.type_info.0.ty;
52-
if !matches!(
53-
ty,
54-
DataType::Decimal | DataType::DecimalN | DataType::Numeric | DataType::NumericN
55-
) {
56-
return Err(err_protocol!("expected numeric type, got {:?}", value.type_info.0).into());
58+
match ty {
59+
DataType::Decimal | DataType::DecimalN | DataType::Numeric | DataType::NumericN => {
60+
let precision = value.type_info.0.precision;
61+
let scale = value.type_info.0.scale;
62+
decode_numeric(value.as_bytes()?, precision, scale)
63+
}
64+
DataType::MoneyN | DataType::Money | DataType::SmallMoney => {
65+
// Money is stored as an 8-byte integer representing the amount in ten-thousandths of a currency unit
66+
let bytes = value.as_bytes()?;
67+
// println!("bytes: {:?}", bytes);
68+
if bytes.len() != 8 && bytes.len() != 4 {
69+
return Err(
70+
err_protocol!("expected 8/4 bytes for Money, got {}", bytes.len()).into(),
71+
);
72+
}
73+
let amount: i64 = if bytes.len() == 8 {
74+
let amount_h = i32::from_le_bytes(bytes[0..4].try_into()?) as i64;
75+
let amount_l = u32::from_le_bytes(bytes[4..8].try_into()?) as i64;
76+
(amount_h << 32) | amount_l
77+
} else {
78+
i32::from_le_bytes(bytes.try_into()?) as i64
79+
};
80+
Ok(Decimal::new(amount, 4))
81+
}
82+
_ => Err(err_protocol!("expected numeric type, got {:?}", value.type_info.0).into()),
5783
}
58-
let precision = value.type_info.0.precision;
59-
let scale = value.type_info.0.scale;
60-
decode_numeric(value.as_bytes()?, precision, scale)
6184
}
6285
}
6386

sqlx-macros/src/database/mssql.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ impl_database_ext! {
2525

2626
#[cfg(feature = "chrono")]
2727
sqlx::types::chrono::DateTime<sqlx::types::chrono::FixedOffset>,
28+
29+
#[cfg(feature = "bigdecimal")]
30+
sqlx::types::BigDecimal,
31+
32+
#[cfg(feature = "decimal")]
33+
sqlx::types::Decimal,
34+
35+
#[cfg(feature = "json")]
36+
sqlx::types::JsonValue,
37+
2838
},
2939
ParamChecking::Weak,
3040
feature-types: _info => None,

0 commit comments

Comments
 (0)