Merge pull request #101 from launchbadge/ab/mysql-floats

fix and test floats in MySQL
This commit is contained in:
Ryan Leckey 2020-01-30 12:27:53 -08:00 committed by GitHub
commit 97eec46a3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 5 deletions

View File

@ -104,8 +104,8 @@ impl Row {
let size = match columns[column_idx] {
TypeId::TINY_INT => 1,
TypeId::SMALL_INT => 2,
TypeId::INT => 4,
TypeId::BIG_INT => 8,
TypeId::INT | TypeId::FLOAT => 4,
TypeId::BIG_INT | TypeId::DOUBLE => 8,
TypeId::DATE => 5,
TypeId::TIME => 1 + buffer[index] as usize,

View File

@ -5,6 +5,19 @@ use crate::mysql::types::MySqlTypeInfo;
use crate::mysql::MySql;
use crate::types::HasSqlType;
/// The equivalent MySQL type for `f32` is `FLOAT`.
///
/// ### Note
/// While we added support for `f32` as `FLOAT` for completeness, we don't recommend using
/// it for any real-life applications as it cannot precisely represent some fractional values,
/// and may be implicitly widened to `DOUBLE` in some cases, resulting in a slightly different
/// value:
///
/// ```rust
/// // Widening changes the equivalent decimal value, these two expressions are not equal
/// // (This is expected behavior for floating points and happens both in Rust and in MySQL)
/// assert_ne!(10.2f32 as f64, 10.2f64);
/// ```
impl HasSqlType<f32> for MySql {
fn type_info() -> MySqlTypeInfo {
MySqlTypeInfo::new(TypeId::FLOAT)
@ -23,6 +36,10 @@ impl Decode<MySql> for f32 {
}
}
/// The equivalent MySQL type for `f64` is `DOUBLE`.
///
/// Note that `DOUBLE` is a floating-point type and cannot represent some fractional values
/// exactly.
impl HasSqlType<f64> for MySql {
fn type_info() -> MySqlTypeInfo {
MySqlTypeInfo::new(TypeId::DOUBLE)

View File

@ -18,11 +18,11 @@ macro_rules! test {
.fetch_one(&mut conn)
.await?;
assert_eq!(row.get::<i32, _>(0), 1);
let value = row.get::<$ty, _>("_1");
assert!($value == value);
assert_eq!(row.get::<i32, _>(0), 1, "value returned from server: {:?}", value);
assert_eq!($value, value);
)+
Ok(())
@ -44,6 +44,9 @@ test!(mysql_long: i32: "2141512" == 2141512_i32);
test!(mysql_longlong_unsigned: u64: "2141512" == 2141512_u64);
test!(mysql_longlong: i64: "2141512" == 2141512_i64);
// `DOUBLE` can be compared with decimal literals just fine but the same can't be said for `FLOAT`
test!(mysql_double: f64: "3.14159265" == 3.14159265f64);
test!(mysql_string: String: "'helloworld'" == "helloworld");
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
@ -69,3 +72,23 @@ async fn mysql_bytes() -> anyhow::Result<()> {
Ok(())
}
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
async fn mysql_float() -> anyhow::Result<()> {
let mut conn = connect().await?;
let value = 10.2f32;
let row = sqlx::query("SELECT ? as _1")
.bind(value)
.fetch_one(&mut conn)
.await?;
// comparison between FLOAT and literal doesn't work as expected
// we get implicit widening to DOUBLE which gives a slightly different value
// however, round-trip does work as expected
let ret = row.get::<f32, _>("_1");
assert_eq!(value, ret);
Ok(())
}