Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
3 changes: 3 additions & 0 deletions mssql_python/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ class ConstantsDDBC(Enum):
# Reset Connection Constants
SQL_RESET_CONNECTION_YES = 1

# Query Timeout Constants
SQL_ATTR_QUERY_TIMEOUT = 0


class GetInfoConstants(Enum):
"""
Expand Down
35 changes: 21 additions & 14 deletions mssql_python/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -656,13 +656,34 @@ def _initialize_cursor(self) -> None:
Initialize the DDBC statement handle.
"""
self._allocate_statement_handle()
self._set_timeout()

def _allocate_statement_handle(self) -> None:
"""
Allocate the DDBC statement handle.
"""
self.hstmt = self._connection._conn.alloc_statement_handle()

def _set_timeout(self) -> None:
"""
Set the query timeout attribute on the statement handle.
This is called once when the cursor is created and after any handle reallocation.
Following pyodbc's approach for better performance.
"""
if self._timeout > 0:
logger.debug("_set_timeout: Setting query timeout=%d seconds", self._timeout)
try:
timeout_value = int(self._timeout)
ret = ddbc_bindings.DDBCSQLSetStmtAttr(
self.hstmt,
ddbc_sql_const.SQL_ATTR_QUERY_TIMEOUT.value,
timeout_value,
)
check_error(ddbc_sql_const.SQL_HANDLE_STMT.value, self.hstmt, ret)
logger.debug("Query timeout set to %d seconds", timeout_value)
except Exception as e: # pylint: disable=broad-exception-caught
logger.warning("Failed to set query timeout: %s", str(e))

def _reset_cursor(self) -> None:
"""
Reset the DDBC statement handle.
Expand Down Expand Up @@ -1191,20 +1212,6 @@ def execute( # pylint: disable=too-many-locals,too-many-branches,too-many-state
encoding_settings = self._get_encoding_settings()

# Apply timeout if set (non-zero)
if self._timeout > 0:
logger.debug("execute: Setting query timeout=%d seconds", self._timeout)
try:
timeout_value = int(self._timeout)
ret = ddbc_bindings.DDBCSQLSetStmtAttr(
self.hstmt,
ddbc_sql_const.SQL_ATTR_QUERY_TIMEOUT.value,
timeout_value,
)
check_error(ddbc_sql_const.SQL_HANDLE_STMT.value, self.hstmt, ret)
logger.debug("Set query timeout to %d seconds", timeout_value)
except Exception as e: # pylint: disable=broad-exception-caught
logger.warning("Failed to set query timeout: %s", str(e))

logger.debug("execute: Creating parameter type list")
param_info = ddbc_bindings.ParamInfo
parameters_type = []
Expand Down
13 changes: 11 additions & 2 deletions mssql_python/pybind/ddbc_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4353,8 +4353,17 @@ PYBIND11_MODULE(ddbc_bindings, m) {
"Set the decimal separator character");
m.def(
"DDBCSQLSetStmtAttr",
[](SqlHandlePtr stmt, SQLINTEGER attr, SQLPOINTER value) {
return SQLSetStmtAttr_ptr(stmt->get(), attr, value, 0);
[](SqlHandlePtr stmt, SQLINTEGER attr, py::object value) {
SQLPOINTER ptr_value;
if (py::isinstance<py::int_>(value)) {
// For integer attributes like SQL_ATTR_QUERY_TIMEOUT
ptr_value =
reinterpret_cast<SQLPOINTER>(static_cast<SQLULEN>(value.cast<int64_t>()));
} else {
// For pointer attributes
ptr_value = value.cast<SQLPOINTER>();
}
return SQLSetStmtAttr_ptr(stmt->get(), attr, ptr_value, 0);
},
"Set statement attributes");
m.def("DDBCSQLGetTypeInfo", &SQLGetTypeInfo_Wrapper,
Expand Down
Loading