From 3baee86354e2db5100bc857b4a155e69ecdcecfe Mon Sep 17 00:00:00 2001 From: Laurents Meyer Date: Wed, 25 Sep 2019 10:13:27 +0200 Subject: [PATCH 1/2] Explicitly cast double values down to float in Row.GetFloat(). Supports casting to float in Pomelo.EntityFramworkCore.MySql for MySQL < 8.0.17. Signed-off-by: Laurents Meyer --- src/MySqlConnector/Core/Row.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs index 68a980556..e889d79c6 100644 --- a/src/MySqlConnector/Core/Row.cs +++ b/src/MySqlConnector/Core/Row.cs @@ -353,7 +353,8 @@ public double GetDouble(int ordinal) public float GetFloat(int ordinal) { var value = GetValue(ordinal); - return value is decimal decimalValue ? (float) decimalValue : + return value is double doubleValue ? (float) doubleValue : + value is decimal decimalValue ? (float) decimalValue : (float) value; } From 4e97fee2d10a513adfba42bc86709c190c400ef1 Mon Sep 17 00:00:00 2001 From: Laurents Meyer Date: Wed, 25 Sep 2019 14:56:13 +0200 Subject: [PATCH 2/2] Adjust tests for new Row.GetFloat() behavior. Add explicit range checks, to ensure no significant loss of information, when casting from double to float. Signed-off-by: Laurents Meyer --- src/MySqlConnector/Core/Row.cs | 12 +++++++++--- tests/Conformance.Tests/GetValueConversionTests.cs | 8 ++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs index e889d79c6..27c6edb43 100644 --- a/src/MySqlConnector/Core/Row.cs +++ b/src/MySqlConnector/Core/Row.cs @@ -353,9 +353,15 @@ public double GetDouble(int ordinal) public float GetFloat(int ordinal) { var value = GetValue(ordinal); - return value is double doubleValue ? (float) doubleValue : - value is decimal decimalValue ? (float) decimalValue : - (float) value; + + // Loss of precision is expected, significant loss of information is not. + // Use explicit range checks to guard against that. + return value switch + { + double doubleValue => (doubleValue >= float.MinValue && doubleValue <= float.MaxValue ? (float) doubleValue : throw new InvalidCastException("The value cannot be safely cast to Single.")), + decimal decimalValue => (float) decimalValue, + _ => (float) value + }; } public MySqlDateTime GetMySqlDateTime(int ordinal) diff --git a/tests/Conformance.Tests/GetValueConversionTests.cs b/tests/Conformance.Tests/GetValueConversionTests.cs index 2770ce101..283e6b37f 100644 --- a/tests/Conformance.Tests/GetValueConversionTests.cs +++ b/tests/Conformance.Tests/GetValueConversionTests.cs @@ -82,5 +82,13 @@ public GetValueConversionTests(SelectValueFixture fixture) // the minimum date permitted by MySQL is 1000-01-01; override the minimum value for DateTime tests public override void GetDateTime_for_minimum_Date() => TestGetValue(DbType.Date, ValueKind.Minimum, x => x.GetDateTime(0), new DateTime(1000, 1, 1)); public override void GetDateTime_for_minimum_DateTime() => TestGetValue(DbType.Date, ValueKind.Minimum, x => x.GetDateTime(0), new DateTime(1000, 1, 1)); + + // The GetFloat() implementation allows for conversions from double to float. + // The minimum tests for float and double do not test for the smallest possible value (as the tests for integer values do), + // but test for the largest value smaller than 0 (Epsilon). + // If double.Epsilon is converted to float, it will result in 0. + public override void GetFloat_throws_for_minimum_Double() => TestGetValue(DbType.Double, ValueKind.Minimum, x => x.GetFloat(0), 0); + public override void GetFloat_throws_for_one_Double() => TestGetValue(DbType.Double, ValueKind.One, x => x.GetFloat(0), 1); + public override void GetFloat_throws_for_zero_Double() => TestGetValue(DbType.Double, ValueKind.Zero, x => x.GetFloat(0), 0); } }