Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
does not change the default provider or ship any Rust driver binaries.

### Changed
- Pooled check-in skips transaction sanitation only after a prior successful
native rollback/autocommit restore and no subsequent statement allocation or
uncertain operation. Used connections still roll back explicit transactions
before parking; sanitation uses one native attribute probe and one GIL release.
Raw-handle exposure and arbitrary connection attributes disable the fast path;
successfully applied scalar login timeouts (including `connect(timeout=30)`)
do not permanently disable it.
- Fetches reuse owned native metadata for stable columns within a result set;
`fetchmany()` avoids the Python metadata-dictionary roundtrip. Re-execution,
result transitions and statement/connection cleanup invalidate this metadata.
Expand Down
35 changes: 35 additions & 0 deletions eng/pipelines/pr-validation-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,41 @@ variables:
arm64QemuImage: 'tonistiigi/binfmt@sha256:400a4873b838d1b89194d982c45e5fb3cda4593fbfd7e08a02e76b03b21166f0'

jobs:
- job: NativePoolSanitation
displayName: 'Native pool sanitation tests'
Comment thread
sumitmsft marked this conversation as resolved.
timeoutInMinutes: 20
pool:
vmImage: 'ubuntu-latest'

steps:
- checkout: self
persistCredentials: false

- task: UsePythonVersion@0
inputs:
versionSpec: '3.13'
addToPath: true
displayName: 'Use Python 3.13 for native tests'

- bash: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y build-essential cmake unixodbc-dev
python -m pip install --no-deps --require-hashes -r eng/requirements-build-linux.txt
displayName: 'Install native test build dependencies'

- bash: |
set -euo pipefail
cmake -S mssql_python/pybind -B mssql_python/pybind/build/native-tests \
-DCMAKE_BUILD_TYPE=Release -DENABLE_PROFILING=OFF \
-DBUILD_NATIVE_POOL_TESTS=ON -DPython3_EXECUTABLE="$(command -v python)"
cmake --build mssql_python/pybind/build/native-tests \
--config Release --target pool_sanitation_tests --parallel 2
ctest --test-dir mssql_python/pybind/build/native-tests \
-C Release --output-on-failure --no-tests=error \
--timeout 120 -R '^pool_sanitation$'
displayName: 'Build and run native pool sanitation tests'

- job: CodeQLAnalysis
displayName: 'CodeQL Security Analysis'
pool:
Expand Down
27 changes: 3 additions & 24 deletions mssql_python/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2223,35 +2223,14 @@ def close(self) -> None:
# Close the connection even if cursor cleanup had issues
try:
if self._conn:
autocommit_error = None
rollback_error = None
manual_commit = False
# Native close owns transaction cleanup and fail-closed discard.
# Avoid duplicate attribute probes and untrusted rollback hints.
try:
manual_commit = not self._conn.get_autocommit()
except RuntimeError as e:
autocommit_error = e
if manual_commit:
# End caller work before native close. Pooled connections are
# additionally restored to autocommit by native check-in,
# which atomically discards them if sanitation fails.
logger.debug("Rolling back uncommitted changes before closing connection.")
try:
self._conn.rollback()
except RuntimeError as e:
rollback_error = e
# TODO: Check potential race conditions in case of multithreaded scenarios
# Close the connection
try:
self._conn.close(manual_commit and rollback_error is None)
self._conn.close(rollback_before_disconnect=True)
except RuntimeError as e:
_raise_connection_error(e)
finally:
self._conn = None
if rollback_error is not None:
# Preserve prior DB-API error mapping after deterministic cleanup.
_raise_connection_error(rollback_error)
if autocommit_error is not None:
_raise_connection_error(autocommit_error)
except Exception as e:
logger.error(f"Error closing database connection: {e}")
# Re-raise the connection close error as it's more critical
Expand Down
18 changes: 18 additions & 0 deletions mssql_python/pybind/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -399,3 +399,21 @@ if(APPLE)
target_compile_definitions(ddbc_bindings PRIVATE MACOS_STRING_FIX)
target_compile_options(ddbc_bindings PRIVATE -DAPPLE_SILICON)
endif()

# Exercise the real connection/pool/handle code against deterministic ODBC
# function pointers, without shipping test hooks in the extension.
option(BUILD_NATIVE_POOL_TESTS "Build database-free native pool sanitation tests" OFF)
if(BUILD_NATIVE_POOL_TESTS)
Comment thread
sumitmsft marked this conversation as resolved.
find_package(Python3 COMPONENTS Development REQUIRED)
get_target_property(pool_test_sources ddbc_bindings SOURCES)
add_executable(pool_sanitation_tests ${pool_test_sources}
../../tests/native/pool_sanitation.cpp)
foreach(property INCLUDE_DIRECTORIES COMPILE_DEFINITIONS COMPILE_OPTIONS)
set_property(TARGET pool_sanitation_tests PROPERTY ${property}
$<TARGET_PROPERTY:ddbc_bindings,${property}>)
endforeach()
target_link_libraries(pool_sanitation_tests PRIVATE
Python3::Python simdutf::simdutf ${CMAKE_DL_LIBS})
enable_testing()
add_test(NAME pool_sanitation COMMAND pool_sanitation_tests)
endif()
Loading
Loading