From 8ff6c0fb2738d00a0595d25ed93c5c33245dd0db Mon Sep 17 00:00:00 2001 From: Lawrin Novitsky Date: Sun, 12 Jan 2025 17:31:40 +0100 Subject: [PATCH] ODBC-449 Fractional seconds could be lost with SQLExecDirect That happend that with text protocol fractional part wasn't internally converted to microseconds as it's expected by functions converting data to ODBC structures. Natuarally, binary protocol, i.e. (SQLPrepare + SQLExecute) were not affected. The commit contains the testcase. --- driver/interface/ResultSet.cpp | 22 +++++++++++++- driver/ma_helper.cpp | 1 + test/datetime.c | 53 +++++++++++++++++++++++++++++++++- test/relative.c | 40 ------------------------- 4 files changed, 74 insertions(+), 42 deletions(-) diff --git a/driver/interface/ResultSet.cpp b/driver/interface/ResultSet.cpp index 67cee614..15c29454 100644 --- a/driver/interface/ResultSet.cpp +++ b/driver/interface/ResultSet.cpp @@ -342,7 +342,27 @@ namespace mariadb time->second= static_cast(std::stoll(str.substr(offset + 6, 2))); time->second_part= 0; if (str[offset + 8] == '.') { - time->second_part= static_cast(std::stoll(str.substr(offset + 9, std::min(str.length() - offset - 9, static_cast(6))))); + auto fractPartLen= std::min(str.length() - offset - 9, std::size_t(6)); + time->second_part= static_cast(std::stoll(str.substr(offset + 9, fractPartLen))); + // Need to make it microseconds + switch (fractPartLen) { + case 1: + time->second_part*= 10000; + break; + case 2: + time->second_part*= 10000; + break; + case 3: + time->second_part*= 1000; + break; + case 4: + time->second_part*= 100; + break; + case 5: + time->second_part*= 10; + default: + break; + } } } diff --git a/driver/ma_helper.cpp b/driver/ma_helper.cpp index d7b27f87..2de93e3d 100644 --- a/driver/ma_helper.cpp +++ b/driver/ma_helper.cpp @@ -742,6 +742,7 @@ SQLRETURN MADB_CopyMadbTimestamp(MADB_Stmt *Stmt, MYSQL_TIME *tm, SQLPOINTER Dat ts->year= tm->year; ts->month= tm->month; ts->day= tm->day; + // Microseconds of MYSQL_TIME to nanoseconds of SQL_TIMESTAMP_STRUCT ts->fraction= tm->second_part * 1000; } ts->hour= tm->hour; diff --git a/test/datetime.c b/test/datetime.c index fc167b58..45866cf9 100644 --- a/test/datetime.c +++ b/test/datetime.c @@ -1523,7 +1523,7 @@ ODBC_TEST(t_odbc199_time2timestamp) SQL_TIME_STRUCT t; SQL_TIMESTAMP_STRUCT ts= {0}, ts1= {0}; time_t sec_time; - struct tm * cur_tm; + struct tm *cur_tm; sec_time= time(NULL); cur_tm= localtime(&sec_time); @@ -1641,6 +1641,56 @@ ODBC_TEST(t_odbc345) } +ODBC_TEST(t_odbc449) +{ + SQL_TIMESTAMP_STRUCT ts= {0}, r1= {0}; + unsigned long fractional[]= {0,100000000,120000000,20000000,23000000,3000000,0}; + char asStr[32]; + size_t rowNum= 0; + + ts.year= 2025; + ts.month= 1; + ts.day= 1; + ts.hour= 12; + ts.minute= 0; + ts.second= 0; + ts.fraction= 123000000; + + OK_SIMPLE_STMT(Stmt, "DROP TABLE IF EXISTS t_odbc449"); + OK_SIMPLE_STMT(Stmt, "CREATE TABLE t_odbc449 (dtf DATETIME(3) NOT NULL)"); + + CHECK_STMT_RC(Stmt, SQLExecDirect(Stmt, "INSERT INTO t_odbc449(dtf) VALUES ('2025-01-01 12:00:00.123'),('2025-01-01 12:00:00.1'),\ +('2025-01-01 12:00:00.12'),('2025-01-01 12:00:00.02'),('2025-01-01 12:00:00.023'),('2025-01-01 12:00:00.003')", SQL_NTS));; + + OK_SIMPLE_STMT(Stmt, "SELECT dtf FROM t_odbc449"); + + CHECK_STMT_RC(Stmt, SQLBindCol(Stmt, 1, SQL_C_TIMESTAMP, &r1,sizeof(r1), NULL)); + + while (SQL_SUCCEEDED(SQLFetch(Stmt))) + { + is_num(ts.year, r1.year); + is_num(ts.month, r1.month); + is_num(ts.day, r1.day); + is_num(ts.hour, r1.hour); + is_num(ts.minute, r1.minute); + is_num(ts.second, r1.second); + is_num(ts.fraction, r1.fraction); + + if (!rowNum) + { + IS_STR("2025-01-01 12:00:00.123", my_fetch_str(Stmt, asStr, 1), sizeof("2025-01-01 12:00:00.123")); + } + ++rowNum; + ts.fraction= fractional[rowNum]; + } + is_num(6, rowNum); + CHECK_STMT_RC(Stmt, SQLCloseCursor(Stmt)); + + OK_SIMPLE_STMT(Stmt, "DROP TABLE t_odbc449"); + + return OK; +} + MA_ODBC_TESTS my_tests[]= { {my_ts, "my_ts", NORMAL}, @@ -1669,6 +1719,7 @@ MA_ODBC_TESTS my_tests[]= {t_odbc148, "t_odbc148_datatypes_values_len", NORMAL}, {t_odbc199_time2timestamp, "t_odbc199_time2timestamp", NORMAL}, {t_odbc345, "t_odbc345", NORMAL}, + {t_odbc449, "t_odbc449", NORMAL}, {NULL, NULL} }; diff --git a/test/relative.c b/test/relative.c index 6f01d39f..71beb9cf 100644 --- a/test/relative.c +++ b/test/relative.c @@ -787,48 +787,8 @@ ODBC_TEST(t_rows_fetched_ptr1) } -ODBC_TEST(bench) -{ - SQLINTEGER id= 0; - SQLCHAR val[32]; - size_t i; - - for (i= 0; i < 500; ++i) { - SQLExecDirect(Stmt, (SQLCHAR*)"SELECT * FROM 1000rows", SQL_NTS); - - while (SQLFetch(Stmt) != SQL_NO_DATA) { - SQLGetData(Stmt, 1, SQL_INTEGER, &id, 0, NULL); - SQLGetData(Stmt, 2, SQL_VARCHAR, &val, sizeof(val), NULL); - } - SQLFreeStmt(Stmt, SQL_CLOSE); - } - return OK; -} - - -ODBC_TEST(bench1) -{ - size_t i; - - for (i= 0; i < 200; ++i) { - SQLPrepare(Stmt, (SQLCHAR*)"DO ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?", - SQL_NTS); - for (int i = 1; i <= 1000; i++) { - SQLBindParameter(Stmt, i, SQL_PARAM_INPUT, SQL_C_LONG, - SQL_INTEGER, 0, 0, &i, 0, NULL); - } - // Execute query - SQLExecute(Stmt); - SQLFreeStmt(Stmt, SQL_CLOSE); - } - return OK; -} - - MA_ODBC_TESTS my_tests[]= { - //{bench, "bench"}, - //{bench1, "bench1"}, {t_relative, "t_relative"}, {t_relative1, "t_relative1"}, {t_relative2, "t_relative2"},