| Bug #60646 | Fractional parts of second is ignored in SQLGetData | ||
|---|---|---|---|
| Submitted: | 25 Mar 2011 17:35 | Modified: | 1 Mar 2012 13:56 |
| Reporter: | Vladislav Vaintroub | Email Updates: | |
| Status: | Closed | Impact on me: | |
| Category: | Connector / ODBC | Severity: | S3 (Non-critical) |
| Version: | 5.1.9 | OS: | Any |
| Assigned to: | Lawrenty Novitsky | CPU Architecture: | Any |
[20 May 2011 7:06]
Bogdan Degtyariov
Vlad, Thanks for the test and the proposed solution. You really know how to make it easier for us :) The patch needs a bit of changing because SQLGetData() is not using str_to_ts() function (it calls str_to_time_st(), which works in the similar way). Well, developers on MySQL side have to do at least some part of the job :) Thanks again.
[5 Aug 2011 9:02]
Lawrenty Novitsky
will attach diff in a second. parameters part(60648) will follow
[5 Aug 2011 9:03]
Lawrenty Novitsky
the patch
Attachment: bug60646.diff (application/octet-stream, text), 15.25 KiB.
[1 Mar 2012 13:56]
Lawrenty Novitsky
The patch has been pushed to 5.1 as rev#1030(i don't know why, but log message for that revision is just wrong), with some changes applied in revisions 1032, 1034
[18 Apr 2012 23:52]
John Russell
Added to changelog for 5.1.11: The fraction member in SQL_TIMESTAMP_STRUCT was always set to 0 when a timestamp was retrieved using SQLGetData(). The fix causes the fraction member to be correctly set, with a value representing nanoseconds. This issue did not occur when a result was retrieved as a string (SQLGetData() with SQL_C_CHAR).

Description: MySQL server supports fractional parts of second for temporal datatypes in some specific circumctances, e.g in literals. Also, ODBC supports fractional seconds with SQL_TIMESTAMP_STRUCT, its SQLUINTEGER fraction member has nanoseconds precision. However, this 'fraction' member in SQL_TIMESTAMP_STRUCT is always set to 0 when timestamp is retrieved using SQLGetData, How to repeat: Here is a test case using odbctap format, for easy integration into the ODBC test suite It is using select time('literal') with literal having non-zero fractional part (0.5 secs). The result, retrieved using SQLGetData with SQL_C_TIMESTAMP has ts.fraction set to zero, while it should be 500000000 (nanoseconds). If result is retrieved as string ( SQLGetData with SQL_C_CHAR), the fractional part is correct. Test: DECLARE_TEST(t_fractional_get_data) { char buf[100]; SQL_TIMESTAMP_STRUCT ts; SQLLEN len; ok_sql(hstmt, "SELECT time('00:00:00.5')"); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_TIMESTAMP, &ts, sizeof(ts), &len)); is_num(ts.fraction,500000000); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, buf, sizeof(buf), &len)); return OK; } Suggested fix: The problem is in str_to_ts() function in utility.c It assumes that timestamp string after will be no longer than 14 characters after canonicalization (stripping non-digits and converting 2-digit years to 4-digit). However, with fractional part, there might be up to 9 digits more. A naive (unoptimized) fix might be following 1) extend the 'buff' buffer from 15 characters to 24 characters 2) change if ( length < 14 ) strfill(to,14 - length,'0'); to if ( length < 23 ) strfill(to,23 - length,'0'); 3) instead of ts->fraction= 0; use ts->fraction= atoi(buff+14); an optimized for common case fix (no fractional part) could avoid atoi() (e.g if length after canonicalization is <=14, then fractional part is 0 anyway)