|
9 | 9 | #include "connection/connection_pool.h" |
10 | 10 | #include "logger_bridge.hpp" |
11 | 11 |
|
| 12 | +#include <cctype> |
12 | 13 | #include <cstdint> |
13 | 14 | #include <cstring> // For std::memcpy |
14 | 15 | #include <filesystem> |
|
23 | 24 | // These constants are not exposed via sql.h, hence define them here |
24 | 25 | #define SQL_SS_TIME2 (-154) |
25 | 26 | #define SQL_SS_TIMESTAMPOFFSET (-155) |
| 27 | +#define SQL_C_SS_TIME2 (0x4000) |
26 | 28 | #define SQL_C_SS_TIMESTAMPOFFSET (0x4001) |
27 | 29 | #define MAX_DIGITS_IN_NUMERIC 64 |
28 | 30 | #define SQL_MAX_NUMERIC_LEN 16 |
@@ -66,6 +68,10 @@ inline std::string GetEffectiveCharDecoding(const std::string& userEncoding) { |
66 | 68 | #endif |
67 | 69 | } |
68 | 70 |
|
| 71 | +namespace PythonObjectCache { |
| 72 | +py::object get_time_class(); |
| 73 | +} |
| 74 | + |
69 | 75 | //------------------------------------------------------------------------------------------------- |
70 | 76 | //------------------------------------------------------------------------------------------------- |
71 | 77 | // Logging Infrastructure: |
@@ -3076,7 +3082,7 @@ SQLSMALLINT MapVariantCTypeToSQLType(SQLLEN variantCType) { |
3076 | 3082 | case SQL_C_TIME: |
3077 | 3083 | case SQL_C_TYPE_TIME: |
3078 | 3084 | case SQL_SS_VARIANT_TIME: |
3079 | | - return SQL_TYPE_TIME; |
| 3085 | + return SQL_SS_TIME2; |
3080 | 3086 | case SQL_C_TIMESTAMP: |
3081 | 3087 | case SQL_C_TYPE_TIMESTAMP: |
3082 | 3088 | return SQL_TYPE_TIMESTAMP; |
@@ -3481,19 +3487,20 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p |
3481 | 3487 | } |
3482 | 3488 | break; |
3483 | 3489 | } |
3484 | | - case SQL_TIME: |
3485 | 3490 | case SQL_TYPE_TIME: |
3486 | 3491 | case SQL_SS_TIME2: { |
3487 | | - SQL_TIME_STRUCT timeValue; |
3488 | | - ret = |
3489 | | - SQLGetData_ptr(hStmt, i, SQL_C_TYPE_TIME, &timeValue, sizeof(timeValue), NULL); |
3490 | | - if (SQL_SUCCEEDED(ret)) { |
3491 | | - row.append(PythonObjectCache::get_time_class()(timeValue.hour, timeValue.minute, |
3492 | | - timeValue.second)); |
| 3492 | + SQL_SS_TIME2_STRUCT t2 = {}; |
| 3493 | + SQLLEN indicator = 0; |
| 3494 | + ret = SQLGetData_ptr(hStmt, i, SQL_C_SS_TIME2, &t2, sizeof(t2), &indicator); |
| 3495 | + if (SQL_SUCCEEDED(ret) && indicator != SQL_NULL_DATA) { |
| 3496 | + row.append(PythonObjectCache::get_time_class()( |
| 3497 | + t2.hour, t2.minute, t2.second, t2.fraction / 1000)); // ns to µs |
3493 | 3498 | } else { |
3494 | | - LOG("SQLGetData: Error retrieving SQL_TYPE_TIME for column " |
3495 | | - "%d - SQLRETURN=%d", |
3496 | | - i, ret); |
| 3499 | + if (!SQL_SUCCEEDED(ret)) { |
| 3500 | + LOG("SQLGetData: Error retrieving SQL_SS_TIME2 for column " |
| 3501 | + "%d - SQLRETURN=%d", |
| 3502 | + i, ret); |
| 3503 | + } |
3497 | 3504 | row.append(py::none()); |
3498 | 3505 | } |
3499 | 3506 | break; |
@@ -3668,7 +3675,7 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p |
3668 | 3675 | default: |
3669 | 3676 | std::ostringstream errorString; |
3670 | 3677 | errorString << "Unsupported data type for column - " << columnName << ", Type - " |
3671 | | - << dataType << ", column ID - " << i; |
| 3678 | + << effectiveDataType << ", column ID - " << i; |
3672 | 3679 | LOG("SQLGetData: %s", errorString.str().c_str()); |
3673 | 3680 | ThrowStdException(errorString.str()); |
3674 | 3681 | break; |
@@ -3822,13 +3829,11 @@ SQLRETURN SQLBindColums(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& column |
3822 | 3829 | SQLBindCol_ptr(hStmt, col, SQL_C_TYPE_DATE, buffers.dateBuffers[col - 1].data(), |
3823 | 3830 | sizeof(SQL_DATE_STRUCT), buffers.indicators[col - 1].data()); |
3824 | 3831 | break; |
3825 | | - case SQL_TIME: |
3826 | | - case SQL_TYPE_TIME: |
3827 | 3832 | case SQL_SS_TIME2: |
3828 | 3833 | buffers.timeBuffers[col - 1].resize(fetchSize); |
3829 | 3834 | ret = |
3830 | | - SQLBindCol_ptr(hStmt, col, SQL_C_TYPE_TIME, buffers.timeBuffers[col - 1].data(), |
3831 | | - sizeof(SQL_TIME_STRUCT), buffers.indicators[col - 1].data()); |
| 3835 | + SQLBindCol_ptr(hStmt, col, SQL_C_SS_TIME2, buffers.timeBuffers[col - 1].data(), |
| 3836 | + sizeof(SQL_SS_TIME2_STRUCT), buffers.indicators[col - 1].data()); |
3832 | 3837 | break; |
3833 | 3838 | case SQL_GUID: |
3834 | 3839 | buffers.guidBuffers[col - 1].resize(fetchSize); |
@@ -4132,13 +4137,11 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum |
4132 | 4137 | PyList_SET_ITEM(row, col - 1, dateObj); |
4133 | 4138 | break; |
4134 | 4139 | } |
4135 | | - case SQL_TIME: |
4136 | | - case SQL_TYPE_TIME: |
4137 | 4140 | case SQL_SS_TIME2: { |
| 4141 | + const SQL_SS_TIME2_STRUCT& t2 = buffers.timeBuffers[col - 1][i]; |
4138 | 4142 | PyObject* timeObj = |
4139 | | - PythonObjectCache::get_time_class()(buffers.timeBuffers[col - 1][i].hour, |
4140 | | - buffers.timeBuffers[col - 1][i].minute, |
4141 | | - buffers.timeBuffers[col - 1][i].second) |
| 4143 | + PythonObjectCache::get_time_class()(t2.hour, t2.minute, t2.second, |
| 4144 | + t2.fraction / 1000) // ns to µs |
4142 | 4145 | .release() |
4143 | 4146 | .ptr(); |
4144 | 4147 | PyList_SET_ITEM(row, col - 1, timeObj); |
@@ -4271,10 +4274,8 @@ size_t calculateRowSize(py::list& columnNames, SQLUSMALLINT numCols) { |
4271 | 4274 | case SQL_TYPE_DATE: |
4272 | 4275 | rowSize += sizeof(SQL_DATE_STRUCT); |
4273 | 4276 | break; |
4274 | | - case SQL_TIME: |
4275 | | - case SQL_TYPE_TIME: |
4276 | 4277 | case SQL_SS_TIME2: |
4277 | | - rowSize += sizeof(SQL_TIME_STRUCT); |
| 4278 | + rowSize += sizeof(SQL_SS_TIME2_STRUCT); |
4278 | 4279 | break; |
4279 | 4280 | case SQL_GUID: |
4280 | 4281 | rowSize += sizeof(SQLGUID); |
@@ -4969,9 +4970,9 @@ SQLRETURN FetchArrowBatch_wrap( |
4969 | 4970 | case SQL_SS_TIME2: { |
4970 | 4971 | buffers.timeBuffers[idxCol].resize(1); |
4971 | 4972 | ret = SQLGetData_ptr( |
4972 | | - hStmt, idxCol + 1, SQL_C_TYPE_TIME, |
| 4973 | + hStmt, idxCol + 1, SQL_C_SS_TIME2, |
4973 | 4974 | buffers.timeBuffers[idxCol].data(), |
4974 | | - sizeof(SQL_TIME_STRUCT), |
| 4975 | + sizeof(SQL_SS_TIME2_STRUCT), |
4975 | 4976 | buffers.indicators[idxCol].data() |
4976 | 4977 | ); |
4977 | 4978 | if (!SQL_SUCCEEDED(ret)) { |
@@ -5228,9 +5229,7 @@ SQLRETURN FetchArrowBatch_wrap( |
5228 | 5229 | case SQL_TIME: |
5229 | 5230 | case SQL_TYPE_TIME: |
5230 | 5231 | case SQL_SS_TIME2: { |
5231 | | - // NOTE: SQL_SS_TIME2 supports fractional seconds, but SQL_C_TYPE_TIME does not. |
5232 | | - // To fully support SQL_SS_TIME2, the corresponding c-type should be used. |
5233 | | - const SQL_TIME_STRUCT& timeValue = buffers.timeBuffers[idxCol][idxRowSql]; |
| 5232 | + const SQL_SS_TIME2_STRUCT& timeValue = buffers.timeBuffers[idxCol][idxRowSql]; |
5234 | 5233 | arrowColumnProducer->timeSecondVal[idxRowArrow] = |
5235 | 5234 | static_cast<int32_t>(timeValue.hour) * 3600 + |
5236 | 5235 | static_cast<int32_t>(timeValue.minute) * 60 + |
@@ -5713,6 +5712,8 @@ PYBIND11_MODULE(ddbc_bindings, m) { |
5713 | 5712 | // Expose architecture-specific constants |
5714 | 5713 | m.attr("ARCHITECTURE") = ARCHITECTURE; |
5715 | 5714 |
|
| 5715 | + m.attr("SQL_NO_TOTAL") = static_cast<int>(SQL_NO_TOTAL); |
| 5716 | + |
5716 | 5717 | // Expose the C++ functions to Python |
5717 | 5718 | m.def("ThrowStdException", &ThrowStdException); |
5718 | 5719 | m.def("GetDriverPathCpp", &GetDriverPathCpp, "Get the path to the ODBC driver"); |
|
0 commit comments