Skip to content

Commit 644c517

Browse files
committed
varcharmax streaming support in execute
1 parent d062f19 commit 644c517

File tree

2 files changed

+55
-24
lines changed

2 files changed

+55
-24
lines changed

mssql_python/cursor.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -428,13 +428,7 @@ def _map_sql_type(self, param, parameters_list, i):
428428
)
429429

430430
# For safety: unknown/unhandled Python types should not silently go to SQL
431-
# raise TypeError("Unsupported parameter type: The driver cannot safely convert it to a SQL type.")
432-
return (
433-
ddbc_sql_const.SQL_VARCHAR.value,
434-
ddbc_sql_const.SQL_C_CHAR.value,
435-
len(str(param)),
436-
0,
437-
)
431+
raise TypeError("Unsupported parameter type: The driver cannot safely convert it to a SQL type.")
438432

439433
def _initialize_cursor(self) -> None:
440434
"""

mssql_python/pybind/ddbc_bindings.cpp

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,27 @@ SQLRETURN BindParameters(SQLHANDLE hStmt, const py::list& params,
224224

225225
// TODO: Add more data types like money, guid, interval, TVPs etc.
226226
switch (paramInfo.paramCType) {
227-
case SQL_C_CHAR:
227+
case SQL_C_CHAR: {
228+
if (!py::isinstance<py::str>(param) && !py::isinstance<py::bytearray>(param) &&
229+
!py::isinstance<py::bytes>(param)) {
230+
ThrowStdException(MakeParamMismatchErrorStr(paramInfo.paramCType, paramIndex));
231+
}
232+
if (paramInfo.isDAE) {
233+
LOG("Parameter[{}] is marked for DAE streaming", paramIndex);
234+
dataPtr = const_cast<void*>(reinterpret_cast<const void*>(&paramInfos[paramIndex]));
235+
strLenOrIndPtr = AllocateParamBuffer<SQLLEN>(paramBuffers);
236+
*strLenOrIndPtr = SQL_LEN_DATA_AT_EXEC(0);
237+
bufferLength = 0;
238+
} else {
239+
std::string* strParam =
240+
AllocateParamBuffer<std::string>(paramBuffers, param.cast<std::string>());
241+
dataPtr = const_cast<void*>(static_cast<const void*>(strParam->c_str()));
242+
bufferLength = strParam->size() + 1;
243+
strLenOrIndPtr = AllocateParamBuffer<SQLLEN>(paramBuffers);
244+
*strLenOrIndPtr = SQL_NTS;
245+
}
246+
break;
247+
}
228248
case SQL_C_BINARY: {
229249
if (!py::isinstance<py::str>(param) && !py::isinstance<py::bytearray>(param) &&
230250
!py::isinstance<py::bytes>(param)) {
@@ -1004,23 +1024,40 @@ SQLRETURN SQLExecute_wrap(const SqlHandlePtr statementHandle,
10041024
continue;
10051025
}
10061026
if (py::isinstance<py::str>(pyObj)) {
1007-
std::wstring wstr = pyObj.cast<std::wstring>();
1008-
#if defined(__APPLE__) || defined(__linux__)
1009-
auto utf16Buf = WStringToSQLWCHAR(wstr);
1010-
const char* dataPtr = reinterpret_cast<const char*>(utf16Buf.data());
1011-
size_t totalBytes = (utf16Buf.size() - 1) * sizeof(SQLWCHAR);
1012-
#else
1013-
const char* dataPtr = reinterpret_cast<const char*>(wstr.data());
1014-
size_t totalBytes = wstr.size() * sizeof(wchar_t);
1015-
#endif
1016-
const size_t chunkSize = DAE_CHUNK_SIZE;
1017-
for (size_t offset = 0; offset < totalBytes; offset += chunkSize) {
1018-
size_t len = std::min(chunkSize, totalBytes - offset);
1019-
rc = SQLPutData_ptr(hStmt, (SQLPOINTER)(dataPtr + offset), static_cast<SQLLEN>(len));
1020-
if (!SQL_SUCCEEDED(rc)) {
1021-
LOG("SQLPutData failed at offset {} of {}", offset, totalBytes);
1022-
return rc;
1027+
if (matchedInfo->paramCType == SQL_C_WCHAR) {
1028+
std::wstring wstr = pyObj.cast<std::wstring>();
1029+
#if defined(__APPLE__) || defined(__linux__)
1030+
auto utf16Buf = WStringToSQLWCHAR(wstr);
1031+
const char* dataPtr = reinterpret_cast<const char*>(utf16Buf.data());
1032+
size_t totalBytes = (utf16Buf.size() - 1) * sizeof(SQLWCHAR);
1033+
#else
1034+
const char* dataPtr = reinterpret_cast<const char*>(wstr.data());
1035+
size_t totalBytes = wstr.size() * sizeof(wchar_t);
1036+
#endif
1037+
const size_t chunkSize = DAE_CHUNK_SIZE;
1038+
for (size_t offset = 0; offset < totalBytes; offset += chunkSize) {
1039+
size_t len = std::min(chunkSize, totalBytes - offset);
1040+
rc = SQLPutData_ptr(hStmt, (SQLPOINTER)(dataPtr + offset), static_cast<SQLLEN>(len));
1041+
if (!SQL_SUCCEEDED(rc)) {
1042+
LOG("SQLPutData failed at offset {} of {}", offset, totalBytes);
1043+
return rc;
1044+
}
10231045
}
1046+
} else if (matchedInfo->paramCType == SQL_C_CHAR) {
1047+
std::string s = pyObj.cast<std::string>();
1048+
const char* dataPtr = s.data();
1049+
size_t totalBytes = s.size();
1050+
const size_t chunkSize = DAE_CHUNK_SIZE;
1051+
for (size_t offset = 0; offset < totalBytes; offset += chunkSize) {
1052+
size_t len = std::min(chunkSize, totalBytes - offset);
1053+
rc = SQLPutData_ptr(hStmt, (SQLPOINTER)(dataPtr + offset), static_cast<SQLLEN>(len));
1054+
if (!SQL_SUCCEEDED(rc)) {
1055+
LOG("SQLPutData failed at offset {} of {}", offset, totalBytes);
1056+
return rc;
1057+
}
1058+
}
1059+
} else {
1060+
ThrowStdException("Unsupported C type for str in DAE");
10241061
}
10251062
} else {
10261063
ThrowStdException("DAE only supported for str or bytes");

0 commit comments

Comments
 (0)