Skip to content

Commit cb5ae54

Browse files
committed
cleanup
1 parent cbbe9d6 commit cb5ae54

File tree

1 file changed

+34
-47
lines changed

1 file changed

+34
-47
lines changed

mssql_python/pybind/ddbc_bindings.cpp

Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@
3333
#define DAE_CHUNK_SIZE 8192
3434
#define SQL_MAX_LOB_SIZE 8000
3535

36-
//-------------------------------------------------------------------------------------------------
37-
// Performance optimization: Cached Python modules and objects
38-
//-------------------------------------------------------------------------------------------------
3936
namespace PythonObjectCache {
4037
static py::object datetime_class;
4138
static py::object date_class;
@@ -46,27 +43,21 @@ namespace PythonObjectCache {
4643

4744
void initialize() {
4845
if (!cache_initialized) {
49-
try {
50-
auto datetime_module = py::module_::import("datetime");
51-
datetime_class = datetime_module.attr("datetime");
52-
date_class = datetime_module.attr("date");
53-
time_class = datetime_module.attr("time");
54-
55-
auto decimal_module = py::module_::import("decimal");
56-
decimal_class = decimal_module.attr("Decimal");
57-
58-
auto uuid_module = py::module_::import("uuid");
59-
uuid_class = uuid_module.attr("UUID");
60-
61-
cache_initialized = true;
62-
} catch (...) {
63-
// If initialization fails, fall back to direct imports
64-
cache_initialized = false;
65-
}
46+
auto datetime_module = py::module_::import("datetime");
47+
datetime_class = datetime_module.attr("datetime");
48+
date_class = datetime_module.attr("date");
49+
time_class = datetime_module.attr("time");
50+
51+
auto decimal_module = py::module_::import("decimal");
52+
decimal_class = decimal_module.attr("Decimal");
53+
54+
auto uuid_module = py::module_::import("uuid");
55+
uuid_class = uuid_module.attr("UUID");
56+
57+
cache_initialized = true;
6658
}
6759
}
6860

69-
// Safe getter functions that fall back to direct import if cache fails
7061
py::object get_datetime_class() {
7162
if (cache_initialized && datetime_class) {
7263
return datetime_class;
@@ -2529,8 +2520,8 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
25292520

25302521
// Cache decimal separator to avoid repeated system calls
25312522
static const std::string defaultSeparator = ".";
2532-
static std::string decimalSeparator = GetDecimalSeparator();
2533-
static bool isDefaultDecimalSeparator = (decimalSeparator == defaultSeparator);
2523+
std::string decimalSeparator = GetDecimalSeparator();
2524+
bool isDefaultDecimalSeparator = (decimalSeparator == defaultSeparator);
25342525

25352526
for (SQLSMALLINT i = 1; i <= colCount; ++i) {
25362527
SQLWCHAR columnName[256];
@@ -2722,14 +2713,10 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
27222713
safeLen = bufSize;
27232714
}
27242715
}
2725-
2726-
// Use cached decimal separator for locale-specific formatting
27272716
if (isDefaultDecimalSeparator) {
2728-
// Fast path: Direct creation without string manipulation
27292717
py::object decimalObj = PythonObjectCache::get_decimal_class()(py::str(cnum, safeLen));
27302718
row.append(decimalObj);
27312719
} else {
2732-
// Slow path: Need separator replacement for locale
27332720
std::string numStr(cnum, safeLen);
27342721
size_t pos = numStr.find('.');
27352722
if (pos != std::string::npos) {
@@ -3208,9 +3195,9 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
32083195
struct ColumnInfo {
32093196
SQLSMALLINT dataType;
32103197
SQLULEN columnSize;
3211-
SQLULEN processedColumnSize; // Post-HandleZeroColumnSizeAtFetch processing
3212-
uint64_t fetchBufferSize; // Pre-computed buffer size for char/wchar types
3213-
bool isLob; // Pre-compute LOB status for O(1) lookup
3198+
SQLULEN processedColumnSize;
3199+
uint64_t fetchBufferSize;
3200+
bool isLob;
32143201
};
32153202
std::vector<ColumnInfo> columnInfos(numCols);
32163203
for (SQLUSMALLINT col = 0; col < numCols; col++) {
@@ -3230,15 +3217,7 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
32303217
std::string decimalSeparator = GetDecimalSeparator(); // Cache decimal separator
32313218
bool isDefaultDecimalSeparator = (decimalSeparator == defaultSeparator);
32323219

3233-
// numRowsFetched is the SQL_ATTR_ROWS_FETCHED_PTR attribute. It'll be populated by
3234-
// SQLFetchScroll
3235-
3236-
// Pre-allocate batch container to avoid 1000-5000 reallocations per batch
3237-
// We know the exact batch size (numRowsFetched), so reserve space upfront
32383220
size_t initialSize = rows.size();
3239-
3240-
// Extend the rows list with None placeholders for the entire batch
3241-
// This eliminates numRowsFetched reallocations during append operations
32423221
for (SQLULEN i = 0; i < numRowsFetched; i++) {
32433222
rows.append(py::none());
32443223
}
@@ -3250,21 +3229,15 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
32503229
const ColumnInfo& colInfo = columnInfos[col - 1];
32513230
SQLSMALLINT dataType = colInfo.dataType;
32523231
SQLLEN dataLen = buffers.indicators[col - 1][i];
3253-
if (dataLen == SQL_NULL_DATA) {
3232+
if (dataLen == SQL_NULL_DATA) {
32543233
row[col - 1] = py::none();
32553234
continue;
32563235
}
3257-
// TODO: variable length data needs special handling, this logic wont suffice
3258-
// This value indicates that the driver cannot determine the length of the data
32593236
if (dataLen == SQL_NO_TOTAL) {
32603237
LOG("Cannot determine the length of the data. Returning NULL value instead."
32613238
"Column ID - {}", col);
32623239
row[col - 1] = py::none();
32633240
continue;
3264-
} else if (dataLen == SQL_NULL_DATA) {
3265-
LOG("Column data is NULL. Setting None to the result row. Column ID - {}", col);
3266-
row[col - 1] = py::none();
3267-
continue;
32683241
} else if (dataLen == 0) {
32693242
// Handle zero-length (non-NULL) data
32703243
if (dataType == SQL_CHAR || dataType == SQL_VARCHAR || dataType == SQL_LONGVARCHAR) {
@@ -3359,10 +3332,9 @@ SQLRETURN FetchBatchData(SQLHSTMT hStmt, ColumnBuffers& buffers, py::list& colum
33593332

33603333
// Use pre-cached decimal separator
33613334
if (isDefaultDecimalSeparator) {
3362-
// Fast path: Direct py::str creation without intermediate string
3335+
// Direct py::str creation without intermediate string
33633336
row[col - 1] = PythonObjectCache::get_decimal_class()(py::str(rawData, decimalDataLen));
33643337
} else {
3365-
// Slow path: Need separator replacement
33663338
std::string numStr(rawData, decimalDataLen);
33673339
size_t pos = numStr.find('.');
33683340
if (pos != std::string::npos) {
@@ -3741,6 +3713,21 @@ SQLRETURN FetchAll_wrap(SqlHandlePtr StatementHandle, py::list& rows) {
37413713
}
37423714
}
37433715

3716+
// If we have LOBs → fall back to row-by-row fetch + SQLGetData_wrap
3717+
if (!lobColumns.empty()) {
3718+
LOG("LOB columns detected, using per-row SQLGetData path");
3719+
while (true) {
3720+
ret = SQLFetch_ptr(hStmt);
3721+
if (ret == SQL_NO_DATA) break;
3722+
if (!SQL_SUCCEEDED(ret)) return ret;
3723+
3724+
py::list row;
3725+
SQLGetData_wrap(StatementHandle, numCols, row); // <-- streams LOBs correctly
3726+
rows.append(row);
3727+
}
3728+
return SQL_SUCCESS;
3729+
}
3730+
37443731
ColumnBuffers buffers(numCols, fetchSize);
37453732

37463733
// Bind columns

0 commit comments

Comments
 (0)