Bug #100218 TIME(n) column deserializes milliseconds incorrectly with prepared command

Submitted: 14 Jul 2020 15:00 Modified: 17 Jul 2020 19:55
Reporter: Bradley Grainger (OCA) Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / NET Severity:S3 (Non-critical)
Version:8.0.21 OS:Windows (10)
Assigned to: CPU Architecture:Any

[14 Jul 2020 15:00] Bradley Grainger
Description:
This is a follow-up to bug #91770. A regression has been introduced in 8.0.21.

If MySqlCommand.Prepare() is called for a statement that SELECTs a TIME(n) column, trailing zeros in the "milliseconds" part (when it is written as a decimal integer) are lost on the returned result. For example, a milliseconds value of "500" will be returned as "5", and "560" as "56".

The microseconds should be set correctly whether or not MySqlCommand.Prepare is called.

How to repeat:
Run the following C# code:

// NOTE: MUST have IgnorePrepare=false in connection string
using (var connection = new MySqlConnection("...;IgnorePrepare=false"))
{
	connection.Open();
	using (var command = new MySqlCommand(@"
DROP TABLE IF EXISTS test_time;
CREATE TABLE test_time(data TIME(3) NOT NULL);
", connection))
		command.ExecuteNonQuery();

	using (var command = new MySqlCommand(@"INSERT INTO test_time VALUES(@data);", connection))
	{
		// prints: 1.02:03:04.5600000
		var ts = new TimeSpan(1, 2, 3, 4, 560);
		Console.WriteLine(ts);

		command.Parameters.AddWithValue("@data", ts);
		command.ExecuteNonQuery();
	}

	using (var command = new MySqlCommand(@"SELECT data FROM test_time;", connection))
	{
		// *** this causes the bug ***
		command.Prepare();

		var result = (TimeSpan)command.ExecuteScalar();

		// prints 1.02:03:04.0560000, expected 1.02:03:04.56000000
		Console.WriteLine(result);
	}
}

Suggested fix:
The bug was introduced here: https://github.com/mysql/mysql-connector-net/commit/810188dc14508b164b91a0ed71406a568070b6...

Instead of converting the microseconds to a string, it should multiply it by 10 (to convert to Ticks) then add it to the TimeSpan value.
[14 Jul 2020 15:02] Bradley Grainger
Here's a variant of the bug for TIME(6) that deserializes microseconds incorrectly:

using (var connection = new MySqlConnection("...;IgnorePrepare=false"))
{
	connection.Open();
	using (var command = new MySqlCommand(@"
DROP TABLE IF EXISTS test_time;
CREATE TABLE test_time(data TIME(6) NOT NULL);
", connection))
		command.ExecuteNonQuery();

	using (var command = new MySqlCommand(@"INSERT INTO test_time VALUES(@data);", connection))
	{
		// prints: 1.02:03:04.0005600
		var ts = new TimeSpan(1, 2, 3, 4) + TimeSpan.FromTicks(5600);
		Console.WriteLine(ts);

		command.Parameters.AddWithValue("@data", ts);
		command.ExecuteNonQuery();
	}

	using (var command = new MySqlCommand(@"SELECT data FROM test_time;", connection))
	{
		// *** this causes the bug ***
		command.Prepare();

		var result = (TimeSpan)command.ExecuteScalar();

		// prints 1.02:03:04.0560000, expected 1.02:03:04.0005600
		Console.WriteLine(result);
	}
}
[15 Jul 2020 8:18] MySQL Verification Team
Hello Bradley,

Thank you for the report and test case.
Verified as described.

regards,
Umesh
[17 Jul 2020 19:55] Christine Cole
Posted by developer:
 
Fixed as of the upcoming MySQL Connector/NET 8.0.22 release, and here's the proposed changelog entry from the documentation team:

Microseconds were deserialized incorrectly when MySqlCommand.Prepare() was
called for a statement that selects a TIME(n) column, resulting in a loss
of trailing zeros in the returned result. Now, the MySqlTime class
calculates ticks, rather than converting the microseconds to a string.

Thank you for the bug report.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.040 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.