1010#include " logger_bridge.hpp"
1111
1212#include < cstdint>
13+ #include < cctype>
1314#include < cstring> // For std::memcpy
1415#include < filesystem>
1516#include < iomanip> // std::setw, std::setfill
2829#define SQL_MAX_NUMERIC_LEN 16
2930#define SQL_SS_XML (-152 )
3031#define SQL_SS_UDT (-151 )
32+ #define SQL_TIME_TEXT_MAX_LEN 32
3133
3234#define STRINGIFY_FOR_CASE (x ) \
3335 case x: \
@@ -53,6 +55,69 @@ inline std::string GetEffectiveCharDecoding(const std::string& userEncoding) {
5355#endif
5456}
5557
58+ namespace PythonObjectCache {
59+ py::object get_time_class ();
60+ }
61+
62+ inline py::object ParseSqlTimeTextToPythonObject (const char * timeText, SQLLEN timeTextLen) {
63+ if (!timeText || timeTextLen <= 0 ) {
64+ return py::none ();
65+ }
66+
67+ size_t len = static_cast <size_t >(timeTextLen);
68+ if (timeTextLen == SQL_NO_TOTAL) {
69+ len = std::strlen (timeText);
70+ }
71+
72+ std::string value (timeText, len);
73+
74+ size_t start = value.find_first_not_of (" \t\r\n " );
75+ if (start == std::string::npos) {
76+ return py::none ();
77+ }
78+ size_t end = value.find_last_not_of (" \t\r\n " );
79+ value = value.substr (start, end - start + 1 );
80+
81+ size_t firstColon = value.find (' :' );
82+ size_t secondColon = (firstColon == std::string::npos) ? std::string::npos
83+ : value.find (' :' , firstColon + 1 );
84+ if (firstColon == std::string::npos || secondColon == std::string::npos) {
85+ ThrowStdException (" Failed to parse TIME/TIME2 value: missing ':' separators" );
86+ }
87+
88+ int hour = std::stoi (value.substr (0 , firstColon));
89+ int minute = std::stoi (value.substr (firstColon + 1 , secondColon - firstColon - 1 ));
90+
91+ size_t dotPos = value.find (' .' , secondColon + 1 );
92+ int second = 0 ;
93+ int microsecond = 0 ;
94+
95+ if (dotPos == std::string::npos) {
96+ second = std::stoi (value.substr (secondColon + 1 ));
97+ } else {
98+ second = std::stoi (value.substr (secondColon + 1 , dotPos - secondColon - 1 ));
99+ std::string frac = value.substr (dotPos + 1 );
100+
101+ size_t digitCount = 0 ;
102+ while (digitCount < frac.size () && std::isdigit (static_cast <unsigned char >(frac[digitCount]))) {
103+ ++digitCount;
104+ }
105+ frac = frac.substr (0 , digitCount);
106+
107+ if (frac.size () > 6 ) {
108+ frac = frac.substr (0 , 6 );
109+ }
110+ while (frac.size () < 6 ) {
111+ frac.push_back (' 0' );
112+ }
113+ if (!frac.empty ()) {
114+ microsecond = std::stoi (frac);
115+ }
116+ }
117+
118+ return PythonObjectCache::get_time_class ()(hour, minute, second, microsecond);
119+ }
120+
56121// -------------------------------------------------------------------------------------------------
57122// -------------------------------------------------------------------------------------------------
58123// Logging Infrastructure:
@@ -3244,9 +3309,23 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
32443309 }
32453310 break ;
32463311 }
3247- case SQL_TIME:
3248- case SQL_TYPE_TIME:
32493312 case SQL_SS_TIME2: {
3313+ char timeTextBuffer[SQL_TIME_TEXT_MAX_LEN] = {0 };
3314+ SQLLEN timeDataLen = 0 ;
3315+ ret = SQLGetData_ptr (hStmt, i, SQL_C_CHAR, &timeTextBuffer, sizeof (timeTextBuffer),
3316+ &timeDataLen);
3317+ if (SQL_SUCCEEDED (ret) && timeDataLen != SQL_NULL_DATA) {
3318+ row.append (ParseSqlTimeTextToPythonObject (timeTextBuffer, timeDataLen));
3319+ } else {
3320+ LOG (" SQLGetData: Error retrieving SQL_SS_TIME2 for column "
3321+ " %d - SQLRETURN=%d" ,
3322+ i, ret);
3323+ row.append (py::none ());
3324+ }
3325+ break ;
3326+ }
3327+ case SQL_TIME:
3328+ case SQL_TYPE_TIME: {
32503329 SQL_TIME_STRUCT timeValue;
32513330 ret =
32523331 SQLGetData_ptr (hStmt, i, SQL_C_TYPE_TIME, &timeValue, sizeof (timeValue), NULL );
@@ -3587,12 +3666,16 @@ SQLRETURN SQLBindColums(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& column
35873666 break ;
35883667 case SQL_TIME:
35893668 case SQL_TYPE_TIME:
3590- case SQL_SS_TIME2:
35913669 buffers.timeBuffers [col - 1 ].resize (fetchSize);
35923670 ret =
35933671 SQLBindCol_ptr (hStmt, col, SQL_C_TYPE_TIME, buffers.timeBuffers [col - 1 ].data (),
35943672 sizeof (SQL_TIME_STRUCT), buffers.indicators [col - 1 ].data ());
35953673 break ;
3674+ case SQL_SS_TIME2:
3675+ buffers.charBuffers [col - 1 ].resize (fetchSize * SQL_TIME_TEXT_MAX_LEN);
3676+ ret = SQLBindCol_ptr (hStmt, col, SQL_C_CHAR, buffers.charBuffers [col - 1 ].data (),
3677+ SQL_TIME_TEXT_MAX_LEN, buffers.indicators [col - 1 ].data ());
3678+ break ;
35963679 case SQL_GUID:
35973680 buffers.guidBuffers [col - 1 ].resize (fetchSize);
35983681 ret = SQLBindCol_ptr (hStmt, col, SQL_C_GUID, buffers.guidBuffers [col - 1 ].data (),
@@ -3896,8 +3979,7 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
38963979 break ;
38973980 }
38983981 case SQL_TIME:
3899- case SQL_TYPE_TIME:
3900- case SQL_SS_TIME2: {
3982+ case SQL_TYPE_TIME: {
39013983 PyObject* timeObj =
39023984 PythonObjectCache::get_time_class ()(buffers.timeBuffers [col - 1 ][i].hour ,
39033985 buffers.timeBuffers [col - 1 ][i].minute ,
@@ -3907,6 +3989,14 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
39073989 PyList_SET_ITEM (row, col - 1 , timeObj);
39083990 break ;
39093991 }
3992+ case SQL_SS_TIME2: {
3993+ const char * rawData = reinterpret_cast <const char *>(
3994+ &buffers.charBuffers [col - 1 ][i * SQL_TIME_TEXT_MAX_LEN]);
3995+ SQLLEN timeDataLen = buffers.indicators [col - 1 ][i];
3996+ py::object timeObj = ParseSqlTimeTextToPythonObject (rawData, timeDataLen);
3997+ PyList_SET_ITEM (row, col - 1 , timeObj.release ().ptr ());
3998+ break ;
3999+ }
39104000 case SQL_SS_TIMESTAMPOFFSET: {
39114001 SQLULEN rowIdx = i;
39124002 const DateTimeOffset& dtoValue = buffers.datetimeoffsetBuffers [col - 1 ][rowIdx];
@@ -4036,9 +4126,11 @@ size_t calculateRowSize(py::list& columnNames, SQLUSMALLINT numCols) {
40364126 break ;
40374127 case SQL_TIME:
40384128 case SQL_TYPE_TIME:
4039- case SQL_SS_TIME2:
40404129 rowSize += sizeof (SQL_TIME_STRUCT);
40414130 break ;
4131+ case SQL_SS_TIME2:
4132+ rowSize += SQL_TIME_TEXT_MAX_LEN;
4133+ break ;
40424134 case SQL_GUID:
40434135 rowSize += sizeof (SQLGUID);
40444136 break ;
0 commit comments