Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
Prevent float <-> decimal conversion on pg
Browse files Browse the repository at this point in the history
It's unstable and will lead to tears.
  • Loading branch information
Julius de Bruijn committed May 12, 2021
1 parent 9d1fc75 commit 5acae5a
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 76 deletions.
Binary file modified db/test.db
Binary file not shown.
45 changes: 18 additions & 27 deletions src/connector/postgres/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,36 +511,28 @@ impl<'a> ToSql for Value<'a> {
(Value::Integer(integer), &PostgresType::OID) => integer.map(|integer| (integer as u32).to_sql(ty, out)),
(Value::Integer(integer), _) => integer.map(|integer| (integer as i64).to_sql(ty, out)),
(Value::Float(float), &PostgresType::FLOAT8) => float.map(|float| (float as f64).to_sql(ty, out)),
#[cfg(feature = "bigdecimal")]
(Value::Float(float), &PostgresType::NUMERIC) => float
.map(|float| BigDecimal::from_f32(float).unwrap())
.map(DecimalWrapper)
.map(|dw| dw.to_sql(ty, out)),
(Value::Float(_), &PostgresType::NUMERIC) => {
let kind = ErrorKind::conversion(format!(
"Writing a float to a {} column is unstable.",
PostgresType::NUMERIC
));
Err(Error::builder(kind).build())?
}
(Value::Float(float), _) => float.map(|float| float.to_sql(ty, out)),
(Value::Double(double), &PostgresType::FLOAT4) => double.map(|double| (double as f32).to_sql(ty, out)),
#[cfg(feature = "bigdecimal")]
(Value::Double(double), &PostgresType::NUMERIC) => double
.map(|double| BigDecimal::from_f64(double).unwrap())
.map(DecimalWrapper)
.map(|dw| dw.to_sql(ty, out)),
(Value::Double(_), &PostgresType::NUMERIC) => {
let kind = ErrorKind::conversion(format!(
"Writing a double to a {} column is unstable.",
PostgresType::NUMERIC
));
Err(Error::builder(kind).build())?
}
(Value::Double(double), _) => double.map(|double| double.to_sql(ty, out)),
#[cfg(feature = "bigdecimal")]
(Value::Numeric(decimal), &PostgresType::FLOAT4) => decimal.as_ref().map(|decimal| {
let f = decimal.to_string().parse::<f32>().expect("decimal to f32 conversion");
f.to_sql(ty, out)
}),
#[cfg(feature = "bigdecimal")]
(Value::Numeric(decimal), &PostgresType::FLOAT8) => decimal.as_ref().map(|decimal| {
let f = decimal.to_string().parse::<f64>().expect("decimal to f64 conversion");
f.to_sql(ty, out)
}),
#[cfg(feature = "bigdecimal")]
(Value::Array(values), &PostgresType::FLOAT4_ARRAY) => values.as_ref().map(|values| {
let mut floats = Vec::with_capacity(values.len());

for value in values.iter() {
let float = match value {
Value::Numeric(n) => n.as_ref().and_then(|n| n.to_string().parse::<f32>().ok()),
Value::Float(f) => *f,
Value::Double(d) => d.map(|d| d as f32),
v => {
Expand All @@ -558,13 +550,11 @@ impl<'a> ToSql for Value<'a> {

floats.to_sql(ty, out)
}),
#[cfg(feature = "bigdecimal")]
(Value::Array(values), &PostgresType::FLOAT8_ARRAY) => values.as_ref().map(|values| {
let mut floats = Vec::with_capacity(values.len());

for value in values.iter() {
let float = match value {
Value::Numeric(n) => n.as_ref().and_then(|n| n.to_string().parse::<f64>().ok()),
Value::Float(f) => f.map(|f| f as f64),
Value::Double(d) => *d,
v => {
Expand Down Expand Up @@ -598,9 +588,10 @@ impl<'a> ToSql for Value<'a> {
.as_ref()
.map(|decimal| DecimalWrapper(decimal.clone()).to_sql(ty, out)),
#[cfg(feature = "bigdecimal")]
(Value::Numeric(float), _) => float
.as_ref()
.map(|float| DecimalWrapper(float.clone()).to_sql(ty, out)),
(Value::Numeric(_), typ) => {
let kind = ErrorKind::conversion(format!("Writing a decimal to a {} column is unstable.", typ));
Err(Error::builder(kind).build())?
}
#[cfg(feature = "uuid")]
(Value::Text(string), &PostgresType::UUID) => string.as_ref().map(|string| {
let parsed_uuid: Uuid = string.parse()?;
Expand Down
30 changes: 1 addition & 29 deletions src/tests/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1896,7 +1896,6 @@ async fn ints_read_write_to_numeric(api: &mut dyn TestApi) -> crate::Result<()>
let table = api.create_table("id int, value numeric(12,2)").await?;

let insert = Insert::multi_into(&table, &["id", "value"])
.values(vec![Value::integer(1), Value::double(1234.5)])
.values(vec![Value::integer(2), Value::integer(1234)])
.values(vec![Value::integer(3), Value::integer(12345)]);

Expand All @@ -1907,37 +1906,10 @@ async fn ints_read_write_to_numeric(api: &mut dyn TestApi) -> crate::Result<()>

for (i, row) in rows.into_iter().enumerate() {
match i {
0 => assert_eq!(Value::numeric(BigDecimal::from_str("1234.5").unwrap()), row["value"]),
1 => assert_eq!(Value::numeric(BigDecimal::from_str("1234.0").unwrap()), row["value"]),
0 => assert_eq!(Value::numeric(BigDecimal::from_str("1234.0").unwrap()), row["value"]),
_ => assert_eq!(Value::numeric(BigDecimal::from_str("12345.0").unwrap()), row["value"]),
}
}

Ok(())
}

#[cfg(feature = "bigdecimal")]
#[test_each_connector(tags("postgresql"))]
async fn bigdecimal_read_write_to_floating(api: &mut dyn TestApi) -> crate::Result<()> {
use bigdecimal::BigDecimal;
use std::str::FromStr;

let table = api.create_table("id int, a float4, b float8").await?;
let val = BigDecimal::from_str("0.1").unwrap();

let insert = Insert::multi_into(&table, &["id", "a", "b"]).values(vec![
Value::integer(1),
Value::numeric(val.clone()),
Value::numeric(val.clone()),
]);

api.conn().execute(insert.into()).await?;

let select = Select::from_table(&table);
let row = api.conn().select(select).await?.into_single()?;

assert_eq!(Value::float(0.1), row["a"]);
assert_eq!(Value::double(0.1), row["b"]);

Ok(())
}
20 changes: 0 additions & 20 deletions src/tests/types/postgres/bigdecimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,3 @@ test_type!(money_array(
Value::Array(None),
Value::array(vec![BigDecimal::from_str("1.12")?, BigDecimal::from_str("1.12")?])
));

test_type!(float4(
postgresql,
"float4",
(Value::Numeric(None), Value::Float(None)),
(
Value::numeric(BigDecimal::from_str("1.123456")?),
Value::float(1.123456)
)
));

test_type!(float8(
postgresql,
"float8",
(Value::Numeric(None), Value::Double(None)),
(
Value::numeric(BigDecimal::from_str("1.123456")?),
Value::double(1.123456)
)
));

0 comments on commit 5acae5a

Please sign in to comment.