@@ -2995,7 +2995,7 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
29952995 // Note: wcharEncoding parameter is reserved for future use
29962996 // Currently WCHAR data always uses UTF-16LE for Windows compatibility
29972997 (void )wcharEncoding; // Suppress unused parameter warning
2998- #if ! defined(__APPLE__) && !defined(__linux__ )
2998+ #if defined(_WIN32 )
29992999 // On Windows, VARCHAR is fetched as SQL_C_WCHAR, so charEncoding is unused.
30003000 (void )charEncoding;
30013001#endif
@@ -3120,11 +3120,17 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
31203120 } else if (dataLen == 0 ) {
31213121 row.append (py::str (" " ));
31223122 } else if (dataLen == SQL_NO_TOTAL) {
3123+ // SQL_NO_TOTAL means the driver has data but
3124+ // cannot report its total length. The buffer
3125+ // may contain a truncated prefix — fall back to
3126+ // the streaming LOB path to retrieve the full
3127+ // value.
31233128 LOG (" SQLGetData: Cannot determine data length "
31243129 " (SQL_NO_TOTAL) for column %d (SQL_CHAR), "
3125- " returning NULL " ,
3130+ " falling back to LOB streaming " ,
31263131 i);
3127- row.append (py::none ());
3132+ row.append (FetchLobColumnData (hStmt, i, SQL_C_CHAR, false , false ,
3133+ charEncoding));
31283134 } else if (dataLen < 0 ) {
31293135 LOG (" SQLGetData: Unexpected negative data length "
31303136 " for column %d - dataType=%d, dataLen=%ld" ,
@@ -3179,11 +3185,17 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
31793185 } else if (dataLen == 0 ) {
31803186 row.append (py::str (" " ));
31813187 } else if (dataLen == SQL_NO_TOTAL) {
3188+ // SQL_NO_TOTAL means the driver has data but
3189+ // cannot report its total length. The buffer
3190+ // may contain a truncated prefix — fall back to
3191+ // the streaming LOB path to retrieve the full
3192+ // value.
31823193 LOG (" SQLGetData: Cannot determine data length "
31833194 " (SQL_NO_TOTAL) for column %d (VARCHAR via WCHAR), "
3184- " returning NULL " ,
3195+ " falling back to LOB streaming " ,
31853196 i);
3186- row.append (py::none ());
3197+ row.append (
3198+ FetchLobColumnData (hStmt, i, SQL_C_WCHAR, true , false , " utf-16le" ));
31873199 } else if (dataLen < 0 ) {
31883200 LOG (" SQLGetData: Unexpected negative data length "
31893201 " for column %d (VARCHAR via WCHAR) - dataLen=%ld" ,
@@ -3251,11 +3263,17 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
32513263 } else if (dataLen == 0 ) {
32523264 row.append (py::str (" " ));
32533265 } else if (dataLen == SQL_NO_TOTAL) {
3266+ // SQL_NO_TOTAL means the driver has data but
3267+ // cannot report its total length. The buffer
3268+ // may contain a truncated prefix — fall back to
3269+ // the streaming LOB path to retrieve the full
3270+ // value.
32543271 LOG (" SQLGetData: Cannot determine NVARCHAR data "
32553272 " length (SQL_NO_TOTAL) for column %d, "
3256- " returning NULL " ,
3273+ " falling back to LOB streaming " ,
32573274 i);
3258- row.append (py::none ());
3275+ row.append (
3276+ FetchLobColumnData (hStmt, i, SQL_C_WCHAR, true , false , " utf-16le" ));
32593277 } else if (dataLen < 0 ) {
32603278 LOG (" SQLGetData: Unexpected negative data length "
32613279 " for column %d (NVARCHAR) - dataLen=%ld" ,
@@ -3975,11 +3993,62 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
39753993 continue ;
39763994 }
39773995 if (dataLen == SQL_NO_TOTAL) {
3978- LOG (" Cannot determine the length of the data. Returning NULL "
3979- " value instead. Column ID - {}" ,
3980- col);
3981- Py_INCREF (Py_None);
3982- PyList_SET_ITEM (row, col - 1 , Py_None);
3996+ // SQL_NO_TOTAL means the driver has data but cannot report
3997+ // its total length (common for variable-length columns).
3998+ // The bound buffer may contain a truncated prefix — fall
3999+ // back to LOB streaming to retrieve the full value instead
4000+ // of silently returning NULL.
4001+ const ColumnInfoExt& noTotalColInfo = columnInfosExt[col - 1 ];
4002+ LOG (" SQLGetData: SQL_NO_TOTAL for column %d (dataType=%d), "
4003+ " falling back to LOB streaming" ,
4004+ col, noTotalColInfo.dataType );
4005+ switch (noTotalColInfo.dataType ) {
4006+ case SQL_CHAR:
4007+ case SQL_VARCHAR:
4008+ case SQL_LONGVARCHAR:
4009+ #if defined(__APPLE__) || defined(__linux__)
4010+ PyList_SET_ITEM (row, col - 1 ,
4011+ FetchLobColumnData (hStmt, col, SQL_C_CHAR, false , false ,
4012+ noTotalColInfo.charEncoding )
4013+ .release ()
4014+ .ptr ());
4015+ #else
4016+ PyList_SET_ITEM (
4017+ row, col - 1 ,
4018+ FetchLobColumnData (hStmt, col, SQL_C_WCHAR, true , false , " utf-16le" )
4019+ .release ()
4020+ .ptr ());
4021+ #endif
4022+ break ;
4023+ case SQL_WCHAR:
4024+ case SQL_WVARCHAR:
4025+ case SQL_WLONGVARCHAR:
4026+ case SQL_SS_XML:
4027+ PyList_SET_ITEM (
4028+ row, col - 1 ,
4029+ FetchLobColumnData (hStmt, col, SQL_C_WCHAR, true , false , " utf-16le" )
4030+ .release ()
4031+ .ptr ());
4032+ break ;
4033+ case SQL_BINARY:
4034+ case SQL_VARBINARY:
4035+ case SQL_LONGVARBINARY:
4036+ case SQL_SS_UDT:
4037+ PyList_SET_ITEM (row, col - 1 ,
4038+ FetchLobColumnData (hStmt, col, SQL_C_BINARY, false , true )
4039+ .release ()
4040+ .ptr ());
4041+ break ;
4042+ default :
4043+ // For fixed-length types SQL_NO_TOTAL should not
4044+ // occur; treat as NULL to avoid undefined behaviour.
4045+ LOG (" SQL_NO_TOTAL for unexpected fixed-type column %d "
4046+ " (dataType=%d), returning NULL" ,
4047+ col, noTotalColInfo.dataType );
4048+ Py_INCREF (Py_None);
4049+ PyList_SET_ITEM (row, col - 1 , Py_None);
4050+ break ;
4051+ }
39834052 continue ;
39844053 }
39854054
@@ -4166,13 +4235,21 @@ size_t calculateRowSize(py::list& columnNames, SQLUSMALLINT numCols) {
41664235 case SQL_CHAR:
41674236 case SQL_VARCHAR:
41684237 case SQL_LONGVARCHAR:
4169- rowSize += columnSize;
4238+ #if defined(_WIN32)
4239+ // Windows binds VARCHAR as SQL_C_WCHAR: buffer = (columnSize + 1) *
4240+ // sizeof(SQLWCHAR)
4241+ rowSize += (columnSize + 1 ) * sizeof (SQLWCHAR);
4242+ #else
4243+ // Linux/macOS bind VARCHAR as SQL_C_CHAR with UTF-8 expansion: buffer = columnSize
4244+ // * 4 + 1
4245+ rowSize += columnSize * 4 + 1 ;
4246+ #endif
41704247 break ;
41714248 case SQL_SS_XML:
41724249 case SQL_WCHAR:
41734250 case SQL_WVARCHAR:
41744251 case SQL_WLONGVARCHAR:
4175- rowSize += columnSize * sizeof (SQLWCHAR);
4252+ rowSize += ( columnSize + 1 ) * sizeof (SQLWCHAR);
41764253 break ;
41774254 case SQL_INTEGER:
41784255 rowSize += sizeof (SQLINTEGER);
0 commit comments