Skip to content

Commit fd06b80

Browse files
bewithgauravCopilot
andcommitted
FIX: Revert RAII ParamResetGuard — it wiped ODBC diagnostics on error
ParamResetGuard called SQLFreeStmt(SQL_RESET_PARAMS) in its destructor before the caller could read SQLGetDiagRec, producing empty SQLSTATEs. Restore manual SQLFreeStmt on success-only paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 432b522 commit fd06b80

1 file changed

Lines changed: 9 additions & 22 deletions

File tree

mssql_python/pybind/ddbc_bindings.cpp

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -584,22 +584,6 @@ SQLDescribeParamFunc SQLDescribeParam_ptr = nullptr;
584584

585585
namespace {
586586

587-
// Ensures parameter bindings are always reset before local bound buffers go out
588-
// of scope, preventing dangling APD pointers on error/exception paths.
589-
struct ParamResetGuard {
590-
SQLHSTMT hStmt;
591-
bool active = false;
592-
593-
explicit ParamResetGuard(SQLHSTMT stmtHandle) : hStmt(stmtHandle) {}
594-
595-
~ParamResetGuard() {
596-
if (active && SQLFreeStmt_ptr) {
597-
SQLFreeStmt_ptr(hStmt, SQL_RESET_PARAMS);
598-
}
599-
}
600-
601-
void arm() { active = true; }
602-
};
603587

604588
const char* GetSqlCTypeAsString(const SQLSMALLINT cType) {
605589
switch (cType) {
@@ -2706,9 +2690,6 @@ SQLRETURN SQLExecuteLegacy_wrap(const SqlHandlePtr statementHandle, const std::u
27062690
if (!SQL_SUCCEEDED(rc)) {
27072691
return rc;
27082692
}
2709-
ParamResetGuard resetGuard(hStmt);
2710-
resetGuard.arm();
2711-
27122693
{
27132694
// Release the GIL during the blocking SQLExecute network call.
27142695
py::gil_scoped_release release;
@@ -2850,6 +2831,10 @@ SQLRETURN SQLExecuteLegacy_wrap(const SqlHandlePtr statementHandle, const std::u
28502831
rc, (void*)hStmt);
28512832
return rc;
28522833
}
2834+
2835+
// Unbind parameter buffers before they go out of scope.
2836+
// Not called on error paths — diagnostics must remain readable.
2837+
SQLFreeStmt_ptr(hStmt, SQL_RESET_PARAMS);
28532838
return rc;
28542839
}
28552840
}
@@ -2933,8 +2918,6 @@ SQLRETURN SQLExecute_wrap(const SqlHandlePtr statementHandle,
29332918
std::vector<std::shared_ptr<void>> paramBuffers;
29342919
rc = BindParameters(*statementHandle, hStmt, params, paramInfos, paramBuffers, charEncoding);
29352920
if (!SQL_SUCCEEDED(rc)) return rc;
2936-
ParamResetGuard resetGuard(hStmt);
2937-
resetGuard.arm();
29382921

29392922
{
29402923
py::gil_scoped_release release;
@@ -3045,7 +3028,11 @@ SQLRETURN SQLExecute_wrap(const SqlHandlePtr statementHandle,
30453028

30463029
if (!SQL_SUCCEEDED(rc) && rc != SQL_NO_DATA) return rc;
30473030

3048-
return rc;
3031+
// Unbind parameter buffers before they go out of scope.
3032+
// Not called on error paths — diagnostics must remain readable.
3033+
SQLRETURN exec_rc = rc;
3034+
SQLFreeStmt_ptr(hStmt, SQL_RESET_PARAMS);
3035+
return exec_rc;
30493036
}
30503037

30513038
SQLRETURN BindParameterArray(SqlHandle& handle, SQLHANDLE hStmt, const py::list& columnwise_params,

0 commit comments

Comments
 (0)