Skip to content

Commit e21b47e

Browse files
committed
fix comments
1 parent ac231c1 commit e21b47e

File tree

3 files changed

+179
-34
lines changed

3 files changed

+179
-34
lines changed

mssql_python/pybind/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ target_compile_definitions(ddbc_bindings PRIVATE
272272

273273
# Add warning level flags for MSVC
274274
if(MSVC)
275-
target_compile_options(ddbc_bindings PRIVATE /W4 /WX)
275+
target_compile_options(ddbc_bindings PRIVATE /W4 )
276276
endif()
277277

278278
# Add macOS-specific string conversion fix

mssql_python/pybind/ddbc_bindings.cpp

Lines changed: 166 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,87 +1711,220 @@ SQLRETURN SQLFetch_wrap(SqlHandlePtr StatementHandle) {
17111711
return SQLFetch_ptr(StatementHandle->get());
17121712
}
17131713

1714+
// static py::object FetchLobColumnData(SQLHSTMT hStmt,
1715+
// SQLUSMALLINT colIndex,
1716+
// SQLSMALLINT cType,
1717+
// bool isWideChar,
1718+
// bool isBinary)
1719+
// {
1720+
// std::vector<char> buffer;
1721+
// SQLLEN indicator = 0;
1722+
// SQLRETURN ret;
1723+
// int loopCount = 0;
1724+
1725+
// while (true) {
1726+
// ++loopCount;
1727+
// std::vector<char> chunk(DAE_CHUNK_SIZE);
1728+
// ret = SQLGetData_ptr(
1729+
// hStmt,
1730+
// colIndex,
1731+
// cType,
1732+
// chunk.data(),
1733+
// DAE_CHUNK_SIZE,
1734+
// &indicator
1735+
// );
1736+
// if (indicator == SQL_NULL_DATA) {
1737+
// LOG("Loop {}: Column {} is NULL", loopCount, colIndex);
1738+
// return py::none();
1739+
// }
1740+
// if (!SQL_SUCCEEDED(ret) && ret != SQL_SUCCESS_WITH_INFO) {
1741+
// LOG("Loop {}: Error fetching col={} with cType={} ret={}", loopCount, colIndex, cType, ret);
1742+
// ThrowStdException("Error fetching column data");
1743+
// }
1744+
// // SQLLEN copyCount = 0;
1745+
// SQLLEN copyCount = DAE_CHUNK_SIZE;
1746+
// if (indicator >= 0 && indicator != SQL_NO_TOTAL) {
1747+
// copyCount = std::min<SQLLEN>(indicator - buffer.size(), DAE_CHUNK_SIZE);
1748+
// }
1749+
// // else {
1750+
// // copyCount = DAE_CHUNK_SIZE;
1751+
// // }
1752+
// if (isWideChar && (copyCount % sizeof(SQLWCHAR) != 0)) {
1753+
// LOG("Loop {}: Warning – copyCount {} not multiple of {}", loopCount, copyCount, sizeof(SQLWCHAR));
1754+
// copyCount -= copyCount % sizeof(SQLWCHAR);
1755+
// }
1756+
1757+
1758+
// // Check if last byte(s) is a null terminator
1759+
// if (copyCount > 0) {
1760+
// if (!isWideChar && chunk[copyCount - 1] == '\0') {
1761+
// --copyCount;
1762+
// LOG("Loop {}: Trimmed null terminator (narrow)", loopCount);
1763+
// } else if (copyCount >= sizeof(SQLWCHAR)) {
1764+
// auto wcharBuf = reinterpret_cast<const SQLWCHAR*>(chunk.data());
1765+
// if (wcharBuf[(copyCount / sizeof(SQLWCHAR)) - 1] == L'\0') {
1766+
// copyCount -= sizeof(SQLWCHAR);
1767+
// LOG("Loop {}: Trimmed null terminator (wide)", loopCount);
1768+
// }
1769+
// }
1770+
// }
1771+
// if (copyCount > 0) {
1772+
// buffer.insert(buffer.end(), chunk.begin(), chunk.begin() + copyCount);
1773+
// LOG("Loop {}: Appended {} bytes", loopCount, copyCount);
1774+
// }
1775+
// if (ret == SQL_SUCCESS) {
1776+
// LOG("Loop {}: SQL_SUCCESS → no more data", loopCount);
1777+
// break;
1778+
// }
1779+
// }
1780+
// LOG("FetchLobColumnData: Total bytes collected = {}", buffer.size());
1781+
1782+
// if (indicator == 0 || buffer.empty()) {
1783+
// LOG("FetchLobColumnData: Returning empty string for col {}", colIndex);
1784+
// return py::str("");
1785+
// }
1786+
1787+
// if (isWideChar) {
1788+
// // std::wstring wstr(reinterpret_cast<const wchar_t*>(buffer.data()),
1789+
// // buffer.size() / sizeof(wchar_t));
1790+
// // LOG("FetchLobColumnData: Returning wide string of length {}", wstr.length());
1791+
// // return py::cast(wstr);
1792+
// std::wstring wstr = SQLWCHARToWString(reinterpret_cast<const SQLWCHAR*>(buffer.data()), buffer.size() / sizeof(SQLWCHAR));
1793+
// LOG("FetchLobColumnData: Returning wide string of length {}", wstr.length());
1794+
// return py::cast(wstr);
1795+
// // }
1796+
// if (isWideChar) {
1797+
// if (buffer.size() % sizeof(SQLWCHAR) != 0) {
1798+
// LOG("FetchLobColumnData: Buffer size {} not aligned with {}", buffer.size(), sizeof(SQLWCHAR));
1799+
// throw std::runtime_error("Invalid wide char buffer size");
1800+
// }
1801+
// #ifdef _WIN32
1802+
// std::wstring wstr(reinterpret_cast<const wchar_t*>(buffer.data()), buffer.size() / sizeof(wchar_t));
1803+
// #else
1804+
// size_t length = buffer.size() / sizeof(SQLWCHAR);
1805+
// std::wstring wstr = SQLWCHARToWString(reinterpret_cast<const SQLWCHAR*>(buffer.data()), length);
1806+
// #endif
1807+
// LOG("FetchLobColumnData: Returning wide string of length {}", wstr.length());
1808+
// return py::cast(wstr);
1809+
// }
1810+
// if (isBinary) {
1811+
1812+
// LOG("FetchLobColumnData: Returning binary of {} bytes", buffer.size());
1813+
// return py::bytes(buffer.data(), buffer.size());
1814+
// }
1815+
// std::string str(buffer.data(), buffer.size());
1816+
// LOG("FetchLobColumnData: Returning narrow string of length {}", str.length());
1817+
// return py::str(str);
1818+
// }
1819+
17141820
static py::object FetchLobColumnData(SQLHSTMT hStmt,
17151821
SQLUSMALLINT colIndex,
17161822
SQLSMALLINT cType,
17171823
bool isWideChar,
17181824
bool isBinary)
17191825
{
17201826
std::vector<char> buffer;
1721-
SQLLEN indicator = 0;
1722-
SQLRETURN ret;
1827+
SQLRETURN ret = SQL_SUCCESS_WITH_INFO;
17231828
int loopCount = 0;
17241829

17251830
while (true) {
17261831
++loopCount;
1727-
std::vector<char> chunk(DAE_CHUNK_SIZE);
1728-
ret = SQLGetData_ptr(
1729-
hStmt,
1730-
colIndex,
1731-
cType,
1732-
chunk.data(),
1733-
DAE_CHUNK_SIZE,
1734-
&indicator
1735-
);
1832+
1833+
std::vector<char> chunk(DAE_CHUNK_SIZE, 0); // Fill with zeros to handle padding safely
1834+
SQLLEN indicator = 0;
1835+
1836+
ret = SQLGetData_ptr(hStmt,
1837+
colIndex,
1838+
cType,
1839+
chunk.data(),
1840+
DAE_CHUNK_SIZE,
1841+
&indicator);
1842+
17361843
if (indicator == SQL_NULL_DATA) {
17371844
LOG("Loop {}: Column {} is NULL", loopCount, colIndex);
17381845
return py::none();
17391846
}
17401847
if (!SQL_SUCCEEDED(ret) && ret != SQL_SUCCESS_WITH_INFO) {
1741-
LOG("Loop {}: Error fetching col={} with cType={} ret={}", loopCount, colIndex, cType, ret);
1742-
return py::none();
1848+
LOG("Loop {}: Error fetching column {} with cType={} ret={}", loopCount, colIndex, cType, ret);
1849+
ThrowStdException("Error fetching column data");
17431850
}
1744-
SQLLEN copyCount = 0;
1851+
1852+
size_t bytesRead = 0;
1853+
1854+
// Determine how many bytes to process
17451855
if (indicator > 0 && indicator != SQL_NO_TOTAL) {
1746-
copyCount = std::min<SQLLEN>(indicator, DAE_CHUNK_SIZE);
1856+
bytesRead = std::min<size_t>(static_cast<size_t>(indicator), DAE_CHUNK_SIZE);
17471857
} else {
1748-
copyCount = DAE_CHUNK_SIZE;
1858+
// If unknown, assume full buffer minus possible null terminator padding
1859+
bytesRead = DAE_CHUNK_SIZE;
17491860
}
17501861

1751-
// Check if last byte(s) is a null terminator
1752-
if (copyCount > 0) {
1753-
if (!isWideChar && chunk[copyCount - 1] == '\0') {
1754-
--copyCount;
1755-
LOG("Loop {}: Trimmed null terminator (narrow)", loopCount);
1756-
} else if (copyCount >= sizeof(wchar_t)) {
1757-
auto wcharBuf = reinterpret_cast<const wchar_t*>(chunk.data());
1758-
if (wcharBuf[(copyCount / sizeof(wchar_t)) - 1] == L'\0') {
1759-
copyCount -= sizeof(wchar_t);
1760-
LOG("Loop {}: Trimmed null terminator (wide)", loopCount);
1862+
// For character data, trim trailing null terminators
1863+
if (!isBinary && bytesRead > 0) {
1864+
if (!isWideChar) {
1865+
while (bytesRead > 0 && chunk[bytesRead - 1] == '\0') {
1866+
--bytesRead;
1867+
}
1868+
if (bytesRead < DAE_CHUNK_SIZE) {
1869+
LOG("Loop {}: Trimmed null terminator (narrow)", loopCount);
1870+
}
1871+
} else {
1872+
// Wide characters
1873+
size_t wcharSize = sizeof(SQLWCHAR);
1874+
if (bytesRead >= wcharSize) {
1875+
auto wcharBuf = reinterpret_cast<const SQLWCHAR*>(chunk.data());
1876+
size_t wcharCount = bytesRead / wcharSize;
1877+
while (wcharCount > 0 && wcharBuf[wcharCount - 1] == 0) {
1878+
--wcharCount;
1879+
bytesRead -= wcharSize;
1880+
}
1881+
if (bytesRead < DAE_CHUNK_SIZE) {
1882+
LOG("Loop {}: Trimmed null terminator (wide)", loopCount);
1883+
}
17611884
}
17621885
}
17631886
}
1764-
if (copyCount > 0) {
1765-
buffer.insert(buffer.end(), chunk.begin(), chunk.begin() + copyCount);
1766-
LOG("Loop {}: Appended {} bytes", loopCount, copyCount);
1887+
1888+
if (bytesRead > 0) {
1889+
buffer.insert(buffer.end(), chunk.begin(), chunk.begin() + bytesRead);
1890+
LOG("Loop {}: Appended {} bytes", loopCount, bytesRead);
17671891
}
1892+
17681893
if (ret == SQL_SUCCESS) {
17691894
LOG("Loop {}: SQL_SUCCESS → no more data", loopCount);
17701895
break;
17711896
}
17721897
}
1898+
17731899
LOG("FetchLobColumnData: Total bytes collected = {}", buffer.size());
17741900

1775-
if (indicator == 0 || buffer.empty()) {
1776-
LOG("FetchLobColumnData: Returning empty string for col {}", colIndex);
1901+
// If buffer is empty, return empty string or bytes
1902+
if (buffer.empty()) {
1903+
if (isBinary) {
1904+
return py::bytes("");
1905+
}
17771906
return py::str("");
17781907
}
17791908

1909+
// Convert the collected buffer to appropriate Python type
17801910
if (isWideChar) {
1781-
std::wstring wstr(reinterpret_cast<const wchar_t*>(buffer.data()),
1782-
buffer.size() / sizeof(wchar_t));
1911+
std::wstring wstr = SQLWCHARToWString(reinterpret_cast<const SQLWCHAR*>(buffer.data()), buffer.size() / sizeof(SQLWCHAR));
17831912
LOG("FetchLobColumnData: Returning wide string of length {}", wstr.length());
17841913
return py::cast(wstr);
17851914
}
1915+
17861916
if (isBinary) {
17871917
LOG("FetchLobColumnData: Returning binary of {} bytes", buffer.size());
17881918
return py::bytes(buffer.data(), buffer.size());
17891919
}
1920+
1921+
// Default: narrow string
17901922
std::string str(buffer.data(), buffer.size());
17911923
LOG("FetchLobColumnData: Returning narrow string of length {}", str.length());
17921924
return py::str(str);
17931925
}
17941926

1927+
17951928
// Helper function to retrieve column data
17961929
SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, py::list& row) {
17971930
LOG("Get data from columns");

mssql_python/pybind/ddbc_bindings.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ inline std::vector<SQLWCHAR> WStringToSQLWCHAR(const std::wstring& str) {
3838
result.push_back(0);
3939
return result;
4040
}
41+
42+
inline std::wstring SQLWCHARToWString(const SQLWCHAR* sqlwStr, size_t length = SQL_NTS) {
43+
if (!sqlwStr) return std::wstring();
44+
45+
if (length == SQL_NTS) {
46+
size_t i = 0;
47+
while (sqlwStr[i] != 0) ++i;
48+
length = i;
49+
}
50+
return std::wstring(reinterpret_cast<const wchar_t*>(sqlwStr), length);
51+
}
52+
4153
#endif
4254

4355
#if defined(__APPLE__) || defined(__linux__)

0 commit comments

Comments
 (0)