Skip to content
Draft
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b3bf2a7
FIX: Resource cleanup post failed cursor operations
bewithgaurav Sep 24, 2025
877d4b4
unused variable fix
bewithgaurav Sep 24, 2025
ef2f9ee
fix
bewithgaurav Sep 25, 2025
0990476
added fix and tests
bewithgaurav Sep 25, 2025
0694bb2
added last fix for connection busy, and corrected test
bewithgaurav Sep 25, 2025
55f3349
more tests
bewithgaurav Sep 25, 2025
a1b8b05
is_python_finalizing() is a new function now
bewithgaurav Sep 25, 2025
1048227
Merge branch 'main' of https://github.com/microsoft/mssql-python into…
bewithgaurav Sep 25, 2025
4f48b68
Merge branch 'main' into bewithgaurav/fix_segfault_cleanup_sql_failures
bewithgaurav Sep 26, 2025
9623ed8
Merge branch 'main' into bewithgaurav/fix_segfault_cleanup_sql_failures
bewithgaurav Sep 26, 2025
8298689
Merge branch 'main' into bewithgaurav/fix_segfault_cleanup_sql_failures
bewithgaurav Sep 29, 2025
aaf3d85
fixes memory leak issue-AB#37606
gargsaumya Sep 29, 2025
aaa1da0
copilot comment
gargsaumya Sep 29, 2025
94fafc5
Merge branch 'main' into saumya/issue-37606
gargsaumya Sep 30, 2025
3d312ec
added cerr for error during python finalization state
bewithgaurav Sep 30, 2025
1cd6324
Merge branch 'bewithgaurav/fix_segfault_cleanup_sql_failures' of http…
bewithgaurav Sep 30, 2025
a2d1f0e
Merge branch 'main' of https://github.com/microsoft/mssql-python into…
bewithgaurav Sep 30, 2025
f57da1d
Merge branch 'bewithgaurav/fix_segfault_cleanup_sql_failures' into sa…
bewithgaurav Sep 30, 2025
4cb300d
FIX: Tests for Custom Connection Attributes
bewithgaurav Sep 30, 2025
c118217
progress saved
bewithgaurav Sep 30, 2025
e412a4a
Merge branch 'main' into bewithgaurav/fix_and_test_attr_before
bewithgaurav Oct 6, 2025
e624c13
Update mssql_python/pybind/connection/connection.cpp
bewithgaurav Oct 6, 2025
2ebc7c1
fix tests
bewithgaurav Oct 6, 2025
40bce3f
removed stress test
bewithgaurav Oct 6, 2025
e67a21d
fixed tests
bewithgaurav Oct 6, 2025
7f6379b
CPP Destructor shutdown case
bewithgaurav Oct 6, 2025
27e6043
Token length 32 for accept token
bewithgaurav Oct 6, 2025
11f0b5e
readd concurrent threads test
bewithgaurav Oct 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions mssql_python/pybind/connection/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,25 @@ SQLRETURN Connection::setAttribute(SQLINTEGER attribute, py::object value) {
LOG("Setting SQL attribute");
SQLPOINTER ptr = nullptr;
SQLINTEGER length = 0;
std::string buffer; // to hold sensitive data temporarily

if (py::isinstance<py::int_>(value)) {
int intValue = value.cast<int>();
ptr = reinterpret_cast<SQLPOINTER>(static_cast<uintptr_t>(intValue));
length = SQL_IS_INTEGER;
} else if (py::isinstance<py::bytes>(value) || py::isinstance<py::bytearray>(value)) {
static std::vector<std::string> buffers;
buffers.emplace_back(value.cast<std::string>());
ptr = const_cast<char*>(buffers.back().c_str());
length = static_cast<SQLINTEGER>(buffers.back().size());
buffer = value.cast<std::string>(); // stack buffer

// DEFENSIVE FIX: Protect against ODBC driver bug with short access tokens
// Microsoft ODBC Driver 18 crashes when given access tokens shorter than 32 bytes
// Real access tokens are typically 100+ bytes, so reject anything under 32 bytes
if (attribute == SQL_COPT_SS_ACCESS_TOKEN && buffer.size() < 32) {
LOG("Access token too short (< 32 bytes) - protecting against ODBC driver crash");
return SQL_ERROR; // Return error instead of letting ODBC crash
}
Comment on lines 188 to 191
Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function returns SQL_ERROR but doesn't set any specific error message that can be retrieved by the caller. This makes debugging difficult. Consider setting a more descriptive error state or returning a custom error code.

Copilot uses AI. Check for mistakes.

ptr = buffer.data();
length = static_cast<SQLINTEGER>(buffer.size());
} else {
LOG("Unsupported attribute value type");
return SQL_ERROR;
Expand All @@ -195,6 +204,11 @@ SQLRETURN Connection::setAttribute(SQLINTEGER attribute, py::object value) {
else {
LOG("Set attribute successfully");
}

// Zero out sensitive data if used
if (!buffer.empty()) {
std::fill(buffer.begin(), buffer.end(), static_cast<char>(0));
}
return ret;
}

Expand Down
90 changes: 71 additions & 19 deletions mssql_python/pybind/ddbc_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,24 +665,67 @@ void HandleZeroColumnSizeAtFetch(SQLULEN& columnSize) {

} // namespace

// Helper function to check if Python is shutting down or finalizing
// This centralizes the shutdown detection logic to avoid code duplication
static bool is_python_finalizing() {
try {
if (Py_IsInitialized() == 0) {
return true; // Python is already shut down
}

py::gil_scoped_acquire gil;
py::object sys_module = py::module_::import("sys");
if (!sys_module.is_none()) {
// Check if the attribute exists before accessing it (for Python version compatibility)
if (py::hasattr(sys_module, "_is_finalizing")) {
py::object finalizing_func = sys_module.attr("_is_finalizing");
if (!finalizing_func.is_none() && finalizing_func().cast<bool>()) {
return true; // Python is finalizing
}
}
}
return false;
} catch (...) {
std::cerr << "Error occurred while checking Python finalization state." << std::endl;
Comment on lines 688 to 689
Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message is too generic and doesn't provide enough context about what specific error occurred. Consider logging the actual exception details or being more specific about the type of error.

Suggested change
} catch (...) {
std::cerr << "Error occurred while checking Python finalization state." << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error occurred while checking Python finalization state: " << e.what() << std::endl;
// Be conservative - don't assume shutdown on any exception
// Only return true if we're absolutely certain Python is shutting down
return false;
} catch (...) {
std::cerr << "Unknown error occurred while checking Python finalization state." << std::endl;

Copilot uses AI. Check for mistakes.
// Be conservative - don't assume shutdown on any exception
// Only return true if we're absolutely certain Python is shutting down
return false;
}
}

// TODO: Revisit GIL considerations if we're using python's logger
template <typename... Args>
void LOG(const std::string& formatString, Args&&... args) {
py::gil_scoped_acquire gil; // <---- this ensures safe Python API usage
// Check if Python is shutting down to avoid crash during cleanup
if (is_python_finalizing()) {
return; // Python is shutting down or finalizing, don't log
}

try {
py::gil_scoped_acquire gil; // <---- this ensures safe Python API usage

py::object logger = py::module_::import("mssql_python.logging_config").attr("get_logger")();
if (py::isinstance<py::none>(logger)) return;
py::object logger = py::module_::import("mssql_python.logging_config").attr("get_logger")();
if (py::isinstance<py::none>(logger)) return;

try {
std::string ddbcFormatString = "[DDBC Bindings log] " + formatString;
if constexpr (sizeof...(args) == 0) {
logger.attr("debug")(py::str(ddbcFormatString));
} else {
py::str message = py::str(ddbcFormatString).format(std::forward<Args>(args)...);
logger.attr("debug")(message);
try {
std::string ddbcFormatString = "[DDBC Bindings log] " + formatString;
if constexpr (sizeof...(args) == 0) {
logger.attr("debug")(py::str(ddbcFormatString));
} else {
py::str message = py::str(ddbcFormatString).format(std::forward<Args>(args)...);
logger.attr("debug")(message);
}
} catch (const std::exception& e) {
std::cerr << "Logging error: " << e.what() << std::endl;
}
} catch (const py::error_already_set& e) {
// Python is shutting down or in an inconsistent state, silently ignore
(void)e; // Suppress unused variable warning
return;
} catch (const std::exception& e) {
std::cerr << "Logging error: " << e.what() << std::endl;
// Any other error, ignore to prevent crash during cleanup
(void)e; // Suppress unused variable warning
return;
}
}

Expand Down Expand Up @@ -993,17 +1036,26 @@ SQLSMALLINT SqlHandle::type() const {
*/
void SqlHandle::free() {
if (_handle && SQLFreeHandle_ptr) {
const char* type_str = nullptr;
switch (_type) {
case SQL_HANDLE_ENV: type_str = "ENV"; break;
case SQL_HANDLE_DBC: type_str = "DBC"; break;
case SQL_HANDLE_STMT: type_str = "STMT"; break;
case SQL_HANDLE_DESC: type_str = "DESC"; break;
default: type_str = "UNKNOWN"; break;
// Check if Python is shutting down using centralized helper function
bool pythonShuttingDown = is_python_finalizing();

// CRITICAL FIX: During Python shutdown, don't free STMT handles as their parent DBC may already be freed
// This prevents segfault when handles are freed in wrong order during interpreter shutdown
// Type 3 = SQL_HANDLE_STMT, Type 2 = SQL_HANDLE_DBC, Type 1 = SQL_HANDLE_ENV
if (pythonShuttingDown && _type == 3) {
_handle = nullptr; // Mark as freed to prevent double-free attempts
return;
}

// Always clean up ODBC resources, regardless of Python state
SQLFreeHandle_ptr(_type, _handle);
_handle = nullptr;
// Don't log during destruction - it can cause segfaults during Python shutdown

// Only log if Python is not shutting down (to avoid segfault)
if (!pythonShuttingDown) {
// Don't log during destruction - even in normal cases it can be problematic
// If logging is needed, use explicit close() methods instead
}
}
}

Expand Down
Loading
Loading