Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 3 additions & 2 deletions mssql_python/pybind/connection/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -803,9 +803,10 @@ void Connection::prepareForPool() {
SQLHANDLE statement = nullptr;
ret = SQLAllocHandle_ptr(SQL_HANDLE_STMT, _dbcHandle->get(), &statement);
if (SQL_SUCCEEDED(ret)) {
// Checkout restores isolation. Doing so here is redundant
// and rejects dedicated SQL pools that do not support READ COMMITTED.
const std::u16string rollbackQuery =
u"IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; "
u"SET TRANSACTION ISOLATION LEVEL READ COMMITTED";
u"IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION";
ret = SQLExecDirect_ptr(statement,
reinterpretU16stringAsSqlWChar(rollbackQuery), SQL_NTS);
if (!SQL_SUCCEEDED(ret)) {
Expand Down
4 changes: 2 additions & 2 deletions mssql_python/pybind/connection/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ class Connection {
// Only successful rollback + AUTOCOMMIT_ON establishes this proof. A new
// login or deferred SQL_ATTR_RESET_CONNECTION alone does not establish it.
mutable bool _poolClean = false;
// True only after check-in has triggered the driver reset and restored the
// default isolation level. This permits checkout to skip a duplicate reset.
// True only after check-in has triggered the driver reset. Checkout still
// restores isolation, but can skip scheduling a duplicate session reset.
mutable bool _poolSessionReset = false;
// Escaped raw handles and arbitrary/deferred attributes cannot be tracked.
mutable bool _poolProofDisabled = false;
Expand Down
23 changes: 22 additions & 1 deletion tests/native/pool_sanitation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "connection/connection.h"
#include "connection/connection_pool.h"
#include "logger_bridge.hpp"
#include "utf_utils.h"
#include <pybind11/embed.h>
#include <algorithm>
#include <array>
Expand Down Expand Up @@ -37,6 +38,7 @@ std::vector<SQLULEN> loginTimeouts;
std::function<void()> duringFree;
std::function<void()> duringRollbackBatch;
std::function<void()> duringReset;
bool rejectReadCommitted = false;

struct CallbackScope {
std::function<void()>& hook;
Expand Down Expand Up @@ -156,14 +158,23 @@ SQLRETURN SQL_API endTran(SQLSMALLINT, SQLHANDLE dbc, SQLSMALLINT completion) {
return ret;
}

SQLRETURN SQL_API executeDirect(SQLHSTMT statement, SQLWCHAR*, SQLINTEGER) {
SQLRETURN SQL_API executeDirect(SQLHSTMT statement, SQLWCHAR* query, SQLINTEGER) {
size_t length = 0;
while (query[length] != 0) {
++length;
}
const auto sql = dupeSqlWCharAsUtf16Le(query, length);
if (duringRollbackBatch) duringRollbackBatch();
auto& handle = *handles.at(handles.at(statement)->parent);
if (handle.resetPending) {
handle.transaction = handle.pendingWork = false;
handle.resetPending = false;
}
auto ret = record("rollback_batch");
if (rejectReadCommitted &&
sql.find(u"SET TRANSACTION ISOLATION LEVEL READ COMMITTED") != std::u16string::npos) {
ret = SQL_ERROR;
}
if (SQL_SUCCEEDED(ret)) {
handle.transaction = handle.pendingWork = false;
}
Expand Down Expand Up @@ -385,6 +396,7 @@ void resetScenario() {
calls.clear();
commits = logins = 0;
loginTimeouts.clear();
rejectReadCommitted = false;
}
} // namespace

Expand All @@ -411,6 +423,15 @@ int main() {
std::cout << "PASS " << name << '\n';
};
try {
run("autocommit pool return does not require READ COMMITTED support", [] {
rejectReadCommitted = true;
auto connection = acquire();
auto statement = connection->allocStatementHandle();
startWork(statement);
statement.reset();
connection->close();
checkParked();
});
run("getInfo size queries allow null output and length pointers", [] {
Handle handle{SQL_HANDLE_DBC, nullptr};
SQLSMALLINT length = 0;
Expand Down
28 changes: 28 additions & 0 deletions tests/test_009_pooling.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,34 @@ def test_connection_pooling_isolation_level_reset(conn_str, use_sql):
conn2.close()


def test_pooled_autocommit_close_after_query(conn_str):
"""Closing a used pooled connection must also work on dedicated SQL pools."""
_run_in_subprocess(
"""
import os
import mssql_python

mssql_python.pooling(enabled=True, max_size=1, idle_timeout=30)
try:
for _ in range(2):
conn = mssql_python.connect(
os.environ["DB_CONNECTION_STRING"], autocommit=True
)
try:
cursor = conn.cursor()
cursor.execute("SELECT 1")
assert cursor.fetchone()[0] == 1
cursor.close()
finally:
conn.close()
assert conn.closed
finally:
mssql_python.pooling(enabled=False)
""",
conn_str,
)


def test_connection_pooling_speed(conn_str):
"""Test that connection pooling provides performance benefits over multiple iterations."""
# Warm up to eliminate cold start effects
Expand Down
Loading