FEAT: Adding set_attr in connection class#177
Conversation
… jahnvi/conn_setencoding
…t/mssql-python into jahnvi/conn_setdecoding
…t/mssql-python into jahnvi/conn_set_attr
gargsaumya
left a comment
There was a problem hiding this comment.
Let's discuss the current implementation. Want to understand what we plan to do here.
Also, has this been thoroughly tested?
There was a problem hiding this comment.
Pull Request Overview
This pull request introduces comprehensive support for setting ODBC connection attributes through a new set_attr method, providing pyodbc-compatible functionality for configuring connection behavior. The implementation includes robust input validation, error handling, and proper timing constraints for different attribute types.
- Added
set_attrmethod to Connection class with input validation and error handling - Implemented comprehensive connection attribute constants and transaction isolation levels
- Enhanced C++ backend with improved attribute setting capabilities and error reporting
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_003_connection.py | Added extensive test cases for setencoding, setdecoding, and set_attr functionality |
| mssql_python/pybind/ddbc_bindings.cpp | Registered set_attr method with Python bindings |
| mssql_python/pybind/connection/connection.h | Added setAttribute as public method and setAttr to ConnectionHandle |
| mssql_python/pybind/connection/connection.cpp | Enhanced setAttribute implementation with better error handling and type support |
| mssql_python/helpers.py | Added validate_attribute_value function for input validation |
| mssql_python/constants.py | Added connection attribute constants and timing validation helpers |
| mssql_python/connection.py | Implemented set_attr method in Python Connection class |
| mssql_python/init.py | Exported connection attribute constants for public API |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
aff067b
📊 Code Coverage Report
Diff CoverageDiff: main...HEAD, staged and unstaged changes
Summary
mssql_python/connection.pyLines 620-628 620
621 # Determine appropriate exception type based on error content
622 error_str = str(e).lower()
623 if 'invalid' in error_str or 'unsupported' in error_str or 'cast' in error_str:
! 624 raise InterfaceError(error_msg, str(e)) from e
625 else:
626 raise ProgrammingError(error_msg, str(e)) from e
627
628 @propertymssql_python/helpers.pyLines 175-184 175 def _sanitize_for_logging(input_val, max_length=max_log_length):
176 if not isinstance(input_val, str):
177 try:
178 input_val = str(input_val)
! 179 except:
! 180 return "<non-string>"
181
182 # Allow alphanumeric, dash, underscore, and dot
183 sanitized = re.sub(r'[^\w\-\.]', '', input_val)
184 Lines 183-191 183 sanitized = re.sub(r'[^\w\-\.]', '', input_val)
184
185 # Limit length
186 if len(sanitized) > max_length:
! 187 sanitized = sanitized[:max_length] + "..."
188
189 return sanitized if sanitized else "<invalid>"
190
191 # Create sanitized versions for loggingLines 230-244 230 elif isinstance(value, str):
231 # Basic string length check
232 MAX_STRING_SIZE = 8192 # 8KB maximum
233 if len(value) > MAX_STRING_SIZE:
! 234 return False, f"String value too large: {len(value)} bytes (max {MAX_STRING_SIZE})", sanitized_attr, sanitized_val
235
236 elif isinstance(value, (bytes, bytearray)):
237 # Basic binary length check
! 238 MAX_BINARY_SIZE = 32768 # 32KB maximum
! 239 if len(value) > MAX_BINARY_SIZE:
! 240 return False, f"Binary value too large: {len(value)} bytes (max {MAX_BINARY_SIZE})", sanitized_attr, sanitized_val
241
242 else:
243 # Reject unsupported value types
244 return False, f"Unsupported attribute value type: {type(value).__name__}", sanitized_attr, sanitized_valmssql_python/pybind/connection/connection.cppLines 201-211 201
202 // Convert to wide string
203 std::wstring wstr = Utf8ToWString(utf8_str);
204 if (wstr.empty() && !utf8_str.empty()) {
! 205 LOG("Failed to convert string value to wide string");
! 206 return SQL_ERROR;
! 207 }
208
209 // Limit static buffer growth for memory safety
210 constexpr size_t MAX_BUFFER_COUNT = 100;
211 if (wstr_buffers.size() >= MAX_BUFFER_COUNT) {Lines 209-218 209 // Limit static buffer growth for memory safety
210 constexpr size_t MAX_BUFFER_COUNT = 100;
211 if (wstr_buffers.size() >= MAX_BUFFER_COUNT) {
212 // Remove oldest 50% of entries when limit reached
! 213 wstr_buffers.erase(wstr_buffers.begin(), wstr_buffers.begin() + (MAX_BUFFER_COUNT / 2));
! 214 }
215
216 wstr_buffers.push_back(wstr);
217
218 SQLPOINTER ptr;Lines 221-231 221 #if defined(__APPLE__) || defined(__linux__)
222 // For macOS/Linux, convert wstring to SQLWCHAR buffer
223 std::vector<SQLWCHAR> sqlwcharBuffer = WStringToSQLWCHAR(wstr);
224 if (sqlwcharBuffer.empty() && !wstr.empty()) {
! 225 LOG("Failed to convert wide string to SQLWCHAR buffer");
! 226 return SQL_ERROR;
! 227 }
228
229 ptr = sqlwcharBuffer.data();
230 length = static_cast<SQLINTEGER>(sqlwcharBuffer.size() * sizeof(SQLWCHAR));
231 #elseLines 243-253 243 }
244 return ret;
245 }
246 catch (const std::exception& e) {
! 247 LOG("Exception during string attribute setting: " + std::string(e.what()));
! 248 return SQL_ERROR;
! 249 }
250 }
251 else if (py::isinstance<py::bytes>(value) || py::isinstance<py::bytearray>(value)) {
252 try {
253 static std::vector<std::string> buffers;Lines 256-265 256 // Limit static buffer growth
257 constexpr size_t MAX_BUFFER_COUNT = 100;
258 if (buffers.size() >= MAX_BUFFER_COUNT) {
259 // Remove oldest 50% of entries when limit reached
! 260 buffers.erase(buffers.begin(), buffers.begin() + (MAX_BUFFER_COUNT / 2));
! 261 }
262
263 buffers.emplace_back(std::move(binary_data));
264 SQLPOINTER ptr = const_cast<char*>(buffers.back().c_str());
265 SQLINTEGER length = static_cast<SQLINTEGER>(buffers.back().size());Lines 267-287 267 SQLRETURN ret = SQLSetConnectAttr_ptr(_dbcHandle->get(), attribute, ptr, length);
268 if (!SQL_SUCCEEDED(ret)) {
269 LOG("Failed to set attribute with binary data");
270 }
! 271 else {
! 272 LOG("Set attribute successfully with binary data");
! 273 }
274 return ret;
275 }
276 catch (const std::exception& e) {
! 277 LOG("Exception during binary attribute setting: " + std::string(e.what()));
! 278 return SQL_ERROR;
! 279 }
280 }
281 else {
! 282 LOG("Unsupported attribute value type");
! 283 return SQL_ERROR;
284 }
285 }
286
287 void Connection::applyAttrsBefore(const py::dict& attrs) {Lines 468-477 468 }
469
470 void ConnectionHandle::setAttr(int attribute, py::object value) {
471 if (!_conn) {
! 472 ThrowStdException("Connection not established");
! 473 }
474
475 // Use existing setAttribute with better error handling
476 SQLRETURN ret = _conn->setAttribute(static_cast<SQLINTEGER>(attribute), value);
477 if (!SQL_SUCCEEDED(ret)) {📋 Files Needing Attention📉 Files with overall lowest coverage (click to expand)mssql_python.ddbc_bindings.py: 68.5%
mssql_python.pybind.ddbc_bindings.cpp: 70.5%
mssql_python.row.py: 76.3%
mssql_python.pybind.connection.connection_pool.cpp: 78.9%
mssql_python.pybind.connection.connection.cpp: 80.1%
mssql_python.cursor.py: 81.7%
mssql_python.connection.py: 82.3%
mssql_python.helpers.py: 83.8%
mssql_python.auth.py: 85.3%
mssql_python.type.py: 86.8%🔗 Quick Links
|
### Work Item / Issue Reference <!-- IMPORTANT: Please follow the PR template guidelines below. For mssql-python maintainers: Insert your ADO Work Item ID below (e.g. AB#37452) For external contributors: Insert Github Issue number below (e.g. #149) Only one reference is required - either GitHub issue OR ADO Work Item. --> <!-- mssql-python maintainers: ADO Work Item --> > [AB#39058](https://sqlclientdrivers.visualstudio.com/c6d89619-62de-46a0-8b46-70b92a84d85e/_workitems/edit/39058) ------------------------------------------------------------------- ### Summary This pull request introduces comprehensive support for setting ODBC connection attributes in the `mssql_python` library, aligning its functionality with pyodbc's `set_attr` API. The changes include new constants for connection attributes, transaction isolation levels, and related options, as well as robust error handling and input validation in both Python and C++ layers. This enables users to configure connection behavior (e.g., autocommit, isolation level, timeouts) in a standardized and secure manner. ### Connection Attribute Support * Added a wide set of ODBC connection attribute constants, transaction isolation level constants, access mode constants, and related enums to `mssql_python/__init__.py` and `mssql_python/constants.py`, making them available for use in Python code. * Implemented the `set_attr` method in the `Connection` Python class, providing pyodbc-compatible functionality for setting connection attributes with detailed input validation and error handling. ### C++ Backend Enhancements * Exposed `setAttribute` as a public method in the C++ `Connection` class, and added a new `setAttr` method in `ConnectionHandle`, with improved error reporting and range validation for SQLUINTEGER values. * Registered the new `set_attr` method with the Python bindings, making it accessible from Python code. ### Code Cleanup and Refactoring * Moved and consolidated connection attribute constants in `ConstantsDDBC` to improve maintainability, and removed legacy/unused constants. These changes provide a robust interface for configuring ODBC connection attributes, improve compatibility with pyodbc, and enhance error handling for attribute operations.
Work Item / Issue Reference
Summary
This pull request introduces comprehensive support for setting ODBC connection attributes in the
mssql_pythonlibrary, aligning its functionality with pyodbc'sset_attrAPI. The changes include new constants for connection attributes, transaction isolation levels, and related options, as well as robust error handling and input validation in both Python and C++ layers. This enables users to configure connection behavior (e.g., autocommit, isolation level, timeouts) in a standardized and secure manner.Connection Attribute Support
mssql_python/__init__.pyandmssql_python/constants.py, making them available for use in Python code.set_attrmethod in theConnectionPython class, providing pyodbc-compatible functionality for setting connection attributes with detailed input validation and error handling.C++ Backend Enhancements
setAttributeas a public method in the C++Connectionclass, and added a newsetAttrmethod inConnectionHandle, with improved error reporting and range validation for SQLUINTEGER values.set_attrmethod with the Python bindings, making it accessible from Python code.Code Cleanup and Refactoring
ConstantsDDBCto improve maintainability, and removed legacy/unused constants.These changes provide a robust interface for configuring ODBC connection attributes, improve compatibility with pyodbc, and enhance error handling for attribute operations.