diff --git a/sqlx-core/src/mysql/protocol/row.rs b/sqlx-core/src/mysql/protocol/row.rs index e85464d5..d266f5c6 100644 --- a/sqlx-core/src/mysql/protocol/row.rs +++ b/sqlx-core/src/mysql/protocol/row.rs @@ -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, diff --git a/sqlx-core/src/mysql/types/float.rs b/sqlx-core/src/mysql/types/float.rs index 831e7ad5..f708424f 100644 --- a/sqlx-core/src/mysql/types/float.rs +++ b/sqlx-core/src/mysql/types/float.rs @@ -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 for MySql { fn type_info() -> MySqlTypeInfo { MySqlTypeInfo::new(TypeId::FLOAT) @@ -23,6 +36,10 @@ impl Decode 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 for MySql { fn type_info() -> MySqlTypeInfo { MySqlTypeInfo::new(TypeId::DOUBLE) diff --git a/tests/mysql-types.rs b/tests/mysql-types.rs index 4fa15a50..01d9192a 100644 --- a/tests/mysql-types.rs +++ b/tests/mysql-types.rs @@ -18,11 +18,11 @@ macro_rules! test { .fetch_one(&mut conn) .await?; - assert_eq!(row.get::(0), 1); - let value = row.get::<$ty, _>("_1"); - assert!($value == value); + assert_eq!(row.get::(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::("_1"); + assert_eq!(value, ret); + + Ok(()) +}