@@ -3184,38 +3184,38 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
31843184 // numRowsFetched is the SQL_ATTR_ROWS_FETCHED_PTR attribute. It'll be populated by
31853185 // SQLFetchScroll
31863186 for (SQLULEN i = 0 ; i < numRowsFetched; i++) {
3187- py::list row;
3187+ py::list row (numCols); // Pre-allocate with known column count for better performance
31883188 for (SQLUSMALLINT col = 1 ; col <= numCols; col++) {
31893189 // Cache column metadata lookup for better performance
31903190 const auto & columnMeta = columnNames[col - 1 ].cast <py::dict>();
31913191 SQLSMALLINT dataType = columnMeta[" DataType" ].cast <SQLSMALLINT>();
31923192 SQLLEN dataLen = buffers.indicators [col - 1 ][i]; if (dataLen == SQL_NULL_DATA) {
3193- row. append ( py::none () );
3193+ row[col - 1 ] = py::none ();
31943194 continue ;
31953195 }
31963196 // TODO: variable length data needs special handling, this logic wont suffice
31973197 // This value indicates that the driver cannot determine the length of the data
31983198 if (dataLen == SQL_NO_TOTAL) {
31993199 LOG (" Cannot determine the length of the data. Returning NULL value instead."
32003200 " Column ID - {}" , col);
3201- row. append ( py::none () );
3201+ row[col - 1 ] = py::none ();
32023202 continue ;
32033203 } else if (dataLen == SQL_NULL_DATA) {
3204- LOG (" Column data is NULL. Appending None to the result row. Column ID - {}" , col);
3205- row. append ( py::none () );
3204+ LOG (" Column data is NULL. Setting None to the result row. Column ID - {}" , col);
3205+ row[col - 1 ] = py::none ();
32063206 continue ;
32073207 } else if (dataLen == 0 ) {
32083208 // Handle zero-length (non-NULL) data
32093209 if (dataType == SQL_CHAR || dataType == SQL_VARCHAR || dataType == SQL_LONGVARCHAR) {
3210- row. append ( std::string (" " ) );
3210+ row[col - 1 ] = std::string (" " );
32113211 } else if (dataType == SQL_WCHAR || dataType == SQL_WVARCHAR || dataType == SQL_WLONGVARCHAR) {
3212- row. append ( std::wstring (L" " ) );
3212+ row[col - 1 ] = std::wstring (L" " );
32133213 } else if (dataType == SQL_BINARY || dataType == SQL_VARBINARY || dataType == SQL_LONGVARBINARY) {
3214- row. append ( py::bytes (" " ) );
3214+ row[col - 1 ] = py::bytes (" " );
32153215 } else {
3216- // For other datatypes, 0 length is unexpected. Log & append None
3217- LOG (" Column data length is 0 for non-string/binary datatype. Appending None to the result row. Column ID - {}" , col);
3218- row. append ( py::none () );
3216+ // For other datatypes, 0 length is unexpected. Log & set None
3217+ LOG (" Column data length is 0 for non-string/binary datatype. Setting None to the result row. Column ID - {}" , col);
3218+ row[col - 1 ] = py::none ();
32193219 }
32203220 continue ;
32213221 } else if (dataLen < 0 ) {
@@ -3237,11 +3237,11 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
32373237 // fetchBufferSize includes null-terminator, numCharsInData doesn't. Hence '<'
32383238 if (!isLob && numCharsInData < fetchBufferSize) {
32393239 // SQLFetch will nullterminate the data
3240- row. append ( std::string (
3240+ row[col - 1 ] = std::string (
32413241 reinterpret_cast <char *>(&buffers.charBuffers [col - 1 ][i * fetchBufferSize]),
3242- numCharsInData)) ;
3242+ numCharsInData);
32433243 } else {
3244- row. append ( FetchLobColumnData (hStmt, col, SQL_C_CHAR, false , false ) );
3244+ row[col - 1 ] = FetchLobColumnData (hStmt, col, SQL_C_CHAR, false , false );
32453245 }
32463246 break ;
32473247 }
@@ -3261,36 +3261,36 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
32613261 // Use unix-specific conversion to handle the wchar_t/SQLWCHAR size difference
32623262 SQLWCHAR* wcharData = &buffers.wcharBuffers [col - 1 ][i * fetchBufferSize];
32633263 std::wstring wstr = SQLWCHARToWString (wcharData, numCharsInData);
3264- row. append ( wstr) ;
3264+ row[col - 1 ] = wstr;
32653265#else
32663266 // On Windows, wchar_t and SQLWCHAR are both 2 bytes, so direct cast works
3267- row. append ( std::wstring (
3267+ row[col - 1 ] = std::wstring (
32683268 reinterpret_cast <wchar_t *>(&buffers.wcharBuffers [col - 1 ][i * fetchBufferSize]),
3269- numCharsInData)) ;
3269+ numCharsInData);
32703270#endif
32713271 } else {
3272- row. append ( FetchLobColumnData (hStmt, col, SQL_C_WCHAR, true , false ) );
3272+ row[col - 1 ] = FetchLobColumnData (hStmt, col, SQL_C_WCHAR, true , false );
32733273 }
32743274 break ;
32753275 }
32763276 case SQL_INTEGER: {
3277- row. append ( buffers.intBuffers [col - 1 ][i]) ;
3277+ row[col - 1 ] = buffers.intBuffers [col - 1 ][i];
32783278 break ;
32793279 }
32803280 case SQL_SMALLINT: {
3281- row. append ( buffers.smallIntBuffers [col - 1 ][i]) ;
3281+ row[col - 1 ] = buffers.smallIntBuffers [col - 1 ][i];
32823282 break ;
32833283 }
32843284 case SQL_TINYINT: {
3285- row. append ( buffers.charBuffers [col - 1 ][i]) ;
3285+ row[col - 1 ] = buffers.charBuffers [col - 1 ][i];
32863286 break ;
32873287 }
32883288 case SQL_BIT: {
3289- row. append ( static_cast <bool >(buffers.charBuffers [col - 1 ][i]) );
3289+ row[col - 1 ] = static_cast <bool >(buffers.charBuffers [col - 1 ][i]);
32903290 break ;
32913291 }
32923292 case SQL_REAL: {
3293- row. append ( buffers.realBuffers [col - 1 ][i]) ;
3293+ row[col - 1 ] = buffers.realBuffers [col - 1 ][i];
32943294 break ;
32953295 }
32963296 case SQL_DECIMAL:
@@ -3313,47 +3313,47 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
33133313 }
33143314
33153315 // Convert to Python decimal using cached class (keep this optimization)
3316- row. append ( PythonObjectCache::get_decimal_class ()(numStr) );
3316+ row[col - 1 ] = PythonObjectCache::get_decimal_class ()(numStr);
33173317 } catch (const py::error_already_set& e) {
3318- // Handle the exception, e.g., log the error and append py::none()
3318+ // Handle the exception, e.g., log the error and set py::none()
33193319 LOG (" Error converting to decimal: {}" , e.what ());
3320- row. append ( py::none () );
3320+ row[col - 1 ] = py::none ();
33213321 }
33223322 break ;
33233323 }
33243324 case SQL_DOUBLE:
33253325 case SQL_FLOAT: {
3326- row. append ( buffers.doubleBuffers [col - 1 ][i]) ;
3326+ row[col - 1 ] = buffers.doubleBuffers [col - 1 ][i];
33273327 break ;
33283328 }
33293329 case SQL_TIMESTAMP:
33303330 case SQL_TYPE_TIMESTAMP:
33313331 case SQL_DATETIME: {
3332- row. append ( PythonObjectCache::get_datetime_class ()(buffers.timestampBuffers [col - 1 ][i].year ,
3333- buffers.timestampBuffers [col - 1 ][i].month ,
3334- buffers.timestampBuffers [col - 1 ][i].day ,
3335- buffers.timestampBuffers [col - 1 ][i].hour ,
3336- buffers.timestampBuffers [col - 1 ][i].minute ,
3337- buffers.timestampBuffers [col - 1 ][i].second ,
3338- buffers.timestampBuffers [col - 1 ][i].fraction / 1000 /* Convert back ns to µs */ ) );
3332+ row[col - 1 ] = PythonObjectCache::get_datetime_class ()(buffers.timestampBuffers [col - 1 ][i].year ,
3333+ buffers.timestampBuffers [col - 1 ][i].month ,
3334+ buffers.timestampBuffers [col - 1 ][i].day ,
3335+ buffers.timestampBuffers [col - 1 ][i].hour ,
3336+ buffers.timestampBuffers [col - 1 ][i].minute ,
3337+ buffers.timestampBuffers [col - 1 ][i].second ,
3338+ buffers.timestampBuffers [col - 1 ][i].fraction / 1000 /* Convert back ns to µs */ );
33393339 break ;
33403340 }
33413341 case SQL_BIGINT: {
3342- row. append ( buffers.bigIntBuffers [col - 1 ][i]) ;
3342+ row[col - 1 ] = buffers.bigIntBuffers [col - 1 ][i];
33433343 break ;
33443344 }
33453345 case SQL_TYPE_DATE: {
3346- row. append ( PythonObjectCache::get_date_class ()(buffers.dateBuffers [col - 1 ][i].year ,
3347- buffers.dateBuffers [col - 1 ][i].month ,
3348- buffers.dateBuffers [col - 1 ][i].day ) );
3346+ row[col - 1 ] = PythonObjectCache::get_date_class ()(buffers.dateBuffers [col - 1 ][i].year ,
3347+ buffers.dateBuffers [col - 1 ][i].month ,
3348+ buffers.dateBuffers [col - 1 ][i].day );
33493349 break ;
33503350 }
33513351 case SQL_TIME:
33523352 case SQL_TYPE_TIME:
33533353 case SQL_SS_TIME2: {
3354- row. append ( PythonObjectCache::get_time_class ()(buffers.timeBuffers [col - 1 ][i].hour ,
3355- buffers.timeBuffers [col - 1 ][i].minute ,
3356- buffers.timeBuffers [col - 1 ][i].second ) );
3354+ row[col - 1 ] = PythonObjectCache::get_time_class ()(buffers.timeBuffers [col - 1 ][i].hour ,
3355+ buffers.timeBuffers [col - 1 ][i].minute ,
3356+ buffers.timeBuffers [col - 1 ][i].second );
33573357 break ;
33583358 }
33593359 case SQL_SS_TIMESTAMPOFFSET: {
@@ -3377,16 +3377,16 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
33773377 tzinfo
33783378 );
33793379 py_dt = py_dt.attr (" astimezone" )(datetime.attr (" timezone" ).attr (" utc" ));
3380- row. append ( py_dt) ;
3380+ row[col - 1 ] = py_dt;
33813381 } else {
3382- row. append ( py::none () );
3382+ row[col - 1 ] = py::none ();
33833383 }
33843384 break ;
33853385 }
33863386 case SQL_GUID: {
33873387 SQLLEN indicator = buffers.indicators [col - 1 ][i];
33883388 if (indicator == SQL_NULL_DATA) {
3389- row. append ( py::none () );
3389+ row[col - 1 ] = py::none ();
33903390 break ;
33913391 }
33923392 SQLGUID* guidValue = &buffers.guidBuffers [col - 1 ][i];
@@ -3405,7 +3405,7 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
34053405 py::dict kwargs;
34063406 kwargs[" bytes" ] = py_guid_bytes;
34073407 py::object uuid_obj = py::module_::import (" uuid" ).attr (" UUID" )(**kwargs);
3408- row. append ( uuid_obj) ;
3408+ row[col - 1 ] = uuid_obj;
34093409 break ;
34103410 }
34113411 case SQL_BINARY:
@@ -3415,11 +3415,11 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
34153415 HandleZeroColumnSizeAtFetch (columnSize);
34163416 bool isLob = std::find (lobColumns.begin (), lobColumns.end (), col) != lobColumns.end ();
34173417 if (!isLob && static_cast <size_t >(dataLen) <= columnSize) {
3418- row. append ( py::bytes (reinterpret_cast <const char *>(
3419- &buffers.charBuffers [col - 1 ][i * columnSize]),
3420- dataLen) );
3418+ row[col - 1 ] = py::bytes (reinterpret_cast <const char *>(
3419+ &buffers.charBuffers [col - 1 ][i * columnSize]),
3420+ dataLen);
34213421 } else {
3422- row. append ( FetchLobColumnData (hStmt, col, SQL_C_BINARY, false , true ) );
3422+ row[col - 1 ] = FetchLobColumnData (hStmt, col, SQL_C_BINARY, false , true );
34233423 }
34243424 break ;
34253425 }
@@ -3434,7 +3434,7 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
34343434 }
34353435 }
34363436 }
3437- rows.append (row);
3437+ rows.append (row); // TODO: Could be optimized with pre-allocation if we knew total row count
34383438 }
34393439 return ret;
34403440}
0 commit comments