Skip to content

Commit c8dfd0e

Browse files
committed
minor updates
1 parent 7cd9679 commit c8dfd0e

File tree

4 files changed

+96
-119
lines changed

4 files changed

+96
-119
lines changed

mssql_python/pybind/connection/connection.cpp

Lines changed: 70 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -7,148 +7,124 @@
77
#include "connection.h"
88
#include <iostream>
99

10+
SqlHandlePtr Connection::_envHandle = nullptr;
1011
//-------------------------------------------------------------------------------------------------
1112
// Implements the Connection class declared in connection.h.
1213
// This class wraps low-level ODBC operations like connect/disconnect,
1314
// transaction control, and autocommit configuration.
1415
//-------------------------------------------------------------------------------------------------
1516
Connection::Connection(const std::wstring& conn_str, bool autocommit)
16-
: _conn_str(conn_str) , _autocommit(autocommit) {}
17+
: _connStr(conn_str) , _autocommit(autocommit) {
18+
if (!_envHandle) {
19+
LOG("Allocating environment handle");
20+
SQLHANDLE env = nullptr;
21+
if (!SQLAllocHandle_ptr) {
22+
LOG("Function pointers not initialized, loading driver");
23+
DriverLoader::getInstance().loadDriver();
24+
}
25+
SQLRETURN ret = SQLAllocHandle_ptr(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
26+
checkError(ret);
27+
_envHandle = std::make_shared<SqlHandle>(SQL_HANDLE_ENV, env);
1728

18-
Connection::~Connection() {
19-
close(); // Ensure the connection is closed when the object is destroyed.
29+
LOG("Setting environment attributes");
30+
ret = SQLSetEnvAttr_ptr(_envHandle->get(), SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3_80, 0);
31+
checkError(ret);
32+
}
33+
allocateDbcHandle();
2034
}
2135

22-
SQLRETURN Connection::connect() {
23-
allocDbcHandle();
24-
return connectToDb();
36+
Connection::~Connection() {
37+
disconnect(); // fallback if user forgets to disconnect
2538
}
2639

27-
// Allocates DBC handle
28-
void Connection::allocDbcHandle() {
40+
// Allocates connection handle
41+
void Connection::allocateDbcHandle() {
2942
SQLHANDLE dbc = nullptr;
3043
LOG("Allocate SQL Connection Handle");
31-
SQLRETURN ret = SQLAllocHandle_ptr(SQL_HANDLE_DBC, getSharedEnvHandle()->get(), &dbc);
32-
if (!SQL_SUCCEEDED(ret)) {
33-
throw std::runtime_error("Failed to allocate connection handle");
34-
}
35-
_dbc_handle = std::make_shared<SqlHandle>(SQL_HANDLE_DBC, dbc);
44+
SQLRETURN ret = SQLAllocHandle_ptr(SQL_HANDLE_DBC, _envHandle->get(), &dbc);
45+
checkError(ret);
46+
_dbcHandle = std::make_shared<SqlHandle>(SQL_HANDLE_DBC, dbc);
3647
}
3748

38-
// Connects to the database
39-
SQLRETURN Connection::connectToDb() {
49+
void Connection::connect() {
4050
LOG("Connecting to database");
41-
SQLRETURN ret = SQLDriverConnect_ptr(_dbc_handle->get(), nullptr,
42-
(SQLWCHAR*)_conn_str.c_str(), SQL_NTS,
43-
nullptr, 0, nullptr, SQL_DRIVER_NOPROMPT);
44-
if (!SQL_SUCCEEDED(ret)) {
45-
throw std::runtime_error("Failed to connect to database");
46-
}
47-
LOG("Connected to database successfully");
48-
return ret;
51+
SQLRETURN ret = SQLDriverConnect_ptr(
52+
_dbcHandle->get(), nullptr,
53+
(SQLWCHAR*)_connStr.c_str(), SQL_NTS,
54+
nullptr, 0, nullptr, SQL_DRIVER_NOPROMPT);
55+
checkError(ret);
56+
setAutocommit(_autocommit);
4957
}
5058

51-
SQLRETURN Connection::close() {
52-
if (!_dbc_handle) {
53-
LOG("No connection handle to close");
54-
return SQL_SUCCESS;
59+
void Connection::disconnect() {
60+
if (_dbcHandle) {
61+
LOG("Disconnecting from database");
62+
SQLRETURN ret = SQLDisconnect_ptr(_dbcHandle->get());
63+
checkError(ret);
64+
_dbcHandle.reset(); // triggers SQLFreeHandle via destructor, if last owner
5565
}
56-
LOG("Disconnect from MSSQL");
57-
if (!SQLDisconnect_ptr) {
58-
LOG("Function pointer not initialized. Loading the driver.");
59-
DriverLoader::getInstance().loadDriver();
66+
else {
67+
LOG("No connection handle to disconnect");
6068
}
69+
}
6170

62-
SQLRETURN ret = SQLDisconnect_ptr(_dbc_handle->get());
63-
_dbc_handle.reset();
64-
return ret;
71+
// TODO: Add an exception class in C++ for error handling, DB spec compliant
72+
void Connection::checkError(SQLRETURN ret) const{
73+
if (!SQL_SUCCEEDED(ret)) {
74+
ErrorInfo err = SQLCheckError_Wrap(SQL_HANDLE_DBC, _dbcHandle, ret);
75+
std::string errorMsg = std::string(err.ddbcErrorMsg.begin(), err.ddbcErrorMsg.end());
76+
ThrowStdException(errorMsg);
77+
}
6578
}
6679

67-
SQLRETURN Connection::commit() {
68-
if (!_dbc_handle) {
69-
throw std::runtime_error("Connection handle not allocated");
80+
void Connection::commit() {
81+
if (!_dbcHandle) {
82+
ThrowStdException("Connection handle not allocated");
7083
}
7184
LOG("Committing transaction");
72-
SQLRETURN ret = SQLEndTran_ptr(SQL_HANDLE_DBC, _dbc_handle->get(), SQL_COMMIT);
73-
if (!SQL_SUCCEEDED(ret)) {
74-
throw std::runtime_error("Failed to commit transaction");
75-
}
76-
return ret;
85+
SQLRETURN ret = SQLEndTran_ptr(SQL_HANDLE_DBC, _dbcHandle->get(), SQL_COMMIT);
86+
checkError(ret);
7787
}
7888

79-
SQLRETURN Connection::rollback() {
80-
if (!_dbc_handle) {
81-
throw std::runtime_error("Connection handle not allocated");
89+
void Connection::rollback() {
90+
if (!_dbcHandle) {
91+
ThrowStdException("Connection handle not allocated");
8292
}
8393
LOG("Rolling back transaction");
84-
SQLRETURN ret = SQLEndTran_ptr(SQL_HANDLE_DBC, _dbc_handle->get(), SQL_ROLLBACK);
85-
if (!SQL_SUCCEEDED(ret)) {
86-
throw std::runtime_error("Failed to rollback transaction");
87-
}
88-
return ret;
94+
SQLRETURN ret = SQLEndTran_ptr(SQL_HANDLE_DBC, _dbcHandle->get(), SQL_ROLLBACK);
95+
checkError(ret);
8996
}
9097

91-
SQLRETURN Connection::setAutocommit(bool enable) {
92-
if (!_dbc_handle) {
93-
throw std::runtime_error("Connection handle not allocated");
98+
void Connection::setAutocommit(bool enable) {
99+
if (!_dbcHandle) {
100+
ThrowStdException("Connection handle not allocated");
94101
}
95102
SQLINTEGER value = enable ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
96103
LOG("Set SQL Connection Attribute");
97-
SQLRETURN ret = SQLSetConnectAttr_ptr(_dbc_handle->get(), SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)value, 0);
98-
if (!SQL_SUCCEEDED(ret)) {
99-
throw std::runtime_error("Failed to set autocommit mode.");
100-
}
104+
SQLRETURN ret = SQLSetConnectAttr_ptr(_dbcHandle->get(), SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)value, 0);
105+
checkError(ret);
101106
_autocommit = enable;
102-
return ret;
103107
}
104108

105109
bool Connection::getAutocommit() const {
106-
if (!_dbc_handle) {
107-
throw std::runtime_error("Connection handle not allocated");
110+
if (!_dbcHandle) {
111+
ThrowStdException("Connection handle not allocated");
108112
}
109113
LOG("Get SQL Connection Attribute");
110114
SQLINTEGER value;
111115
SQLINTEGER string_length;
112-
SQLGetConnectAttr_ptr(_dbc_handle->get(), SQL_ATTR_AUTOCOMMIT, &value, sizeof(value), &string_length);
113-
116+
SQLRETURN ret = SQLGetConnectAttr_ptr(_dbcHandle->get(), SQL_ATTR_AUTOCOMMIT, &value, sizeof(value), &string_length);
117+
checkError(ret);
114118
return value == SQL_AUTOCOMMIT_ON;
115119
}
116120

117121
SqlHandlePtr Connection::allocStatementHandle() {
118-
if (!_dbc_handle) {
119-
throw std::runtime_error("Connection handle not allocated");
122+
if (!_dbcHandle) {
123+
ThrowStdException("Connection handle not allocated");
120124
}
121125
LOG("Allocating statement handle");
122126
SQLHANDLE stmt = nullptr;
123-
SQLRETURN ret = SQLAllocHandle_ptr(SQL_HANDLE_STMT, _dbc_handle->get(), &stmt);
124-
if (!SQL_SUCCEEDED(ret)) {
125-
throw std::runtime_error("Failed to allocate statement handle");
126-
}
127+
SQLRETURN ret = SQLAllocHandle_ptr(SQL_HANDLE_STMT, _dbcHandle->get(), &stmt);
128+
checkError(ret);
127129
return std::make_shared<SqlHandle>(SQL_HANDLE_STMT, stmt);
128130
}
129-
130-
SqlHandlePtr Connection::getSharedEnvHandle() {
131-
static std::once_flag flag;
132-
static SqlHandlePtr env_handle;
133-
134-
std::call_once(flag, []() {
135-
LOG("Allocating environment handle");
136-
SQLHANDLE env = nullptr;
137-
if (!SQLAllocHandle_ptr) {
138-
LOG("Function pointers not initialized, loading driver");
139-
DriverLoader::getInstance().loadDriver();
140-
}
141-
SQLRETURN ret = SQLAllocHandle_ptr(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
142-
if (!SQL_SUCCEEDED(ret)) {
143-
throw std::runtime_error("Failed to allocate environment handle");
144-
}
145-
env_handle = std::make_shared<SqlHandle>(SQL_HANDLE_ENV, env);
146-
147-
LOG("Setting environment attributes");
148-
ret = SQLSetEnvAttr_ptr(env_handle->get(), SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3_80, 0);
149-
if (!SQL_SUCCEEDED(ret)) {
150-
throw std::runtime_error("Failed to set environment attribute");
151-
}
152-
});
153-
return env_handle;
154-
}

mssql_python/pybind/connection/connection.h

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
// INFO|TODO - Note that is file is Windows specific right now. Making it arch agnostic will be
55
// taken up in future.
66

7-
#ifndef CONNECTION_H
8-
#define CONNECTION_H
9-
7+
#pragma once
108
#include "ddbc_bindings.h"
119

1210
// Represents a single ODBC database connection.
@@ -19,19 +17,19 @@ class Connection {
1917
~Connection();
2018

2119
// Establish the connection using the stored connection string.
22-
SQLRETURN connect();
20+
void connect();
2321

24-
// Close the connection and free resources.
25-
SQLRETURN close();
22+
// Disconnect and free the connection handle.
23+
void disconnect();
2624

2725
// Commit the current transaction.
28-
SQLRETURN commit();
26+
void commit();
2927

3028
// Rollback the current transaction.
31-
SQLRETURN rollback();
29+
void rollback();
3230

3331
// Enable or disable autocommit mode.
34-
SQLRETURN setAutocommit(bool value);
32+
void setAutocommit(bool value);
3533

3634
// Check whether autocommit is enabled.
3735
bool getAutocommit() const;
@@ -40,14 +38,13 @@ class Connection {
4038
SqlHandlePtr allocStatementHandle();
4139

4240
private:
43-
void allocDbcHandle();
44-
SQLRETURN connectToDb();
41+
void allocateDbcHandle();
42+
void checkError(SQLRETURN ret) const;
4543

46-
std::wstring _conn_str;
47-
SqlHandlePtr _dbc_handle;
48-
bool _autocommit = false;
44+
std::wstring _connStr;
45+
bool _usePool = false;
46+
bool _autocommit = true;
47+
SqlHandlePtr _dbcHandle;
4948

50-
static SqlHandlePtr getSharedEnvHandle();
49+
static SqlHandlePtr _envHandle;
5150
};
52-
53-
#endif // CONNECTION_H

mssql_python/pybind/ddbc_bindings.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,6 @@ struct ColumnBuffers {
105105
indicators(numCols, std::vector<SQLLEN>(fetchSize)) {}
106106
};
107107

108-
// This struct is used to relay error info obtained from SQLDiagRec API to the Python module
109-
struct ErrorInfo {
110-
std::wstring sqlState;
111-
std::wstring ddbcErrorMsg;
112-
};
113-
114108
//-------------------------------------------------------------------------------------------------
115109
// Function pointer initialization
116110
//-------------------------------------------------------------------------------------------------
@@ -629,10 +623,10 @@ DriverLoader& DriverLoader::getInstance() {
629623
}
630624

631625
void DriverLoader::loadDriver() {
632-
if (!m_driverLoaded) {
626+
std::call_once(m_onceFlag, [this]() {
633627
LoadDriverOrThrowException();
634628
m_driverLoaded = true;
635-
}
629+
});
636630
}
637631

638632
// SqlHandle definition

mssql_python/pybind/ddbc_bindings.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <sql.h>
1212
#include <sqlext.h>
1313
#include <memory>
14+
#include <mutex>
1415

1516
//-------------------------------------------------------------------------------------------------
1617
// Function pointer typedefs
@@ -135,7 +136,9 @@ class DriverLoader {
135136
DriverLoader();
136137
DriverLoader(const DriverLoader&) = delete;
137138
DriverLoader& operator=(const DriverLoader&) = delete;
139+
138140
bool m_driverLoaded;
141+
std::once_flag m_onceFlag;
139142
};
140143

141144
//-------------------------------------------------------------------------------------------------
@@ -156,3 +159,10 @@ class SqlHandle {
156159
SQLHANDLE _handle;
157160
};
158161
using SqlHandlePtr = std::shared_ptr<SqlHandle>;
162+
163+
// This struct is used to relay error info obtained from SQLDiagRec API to the Python module
164+
struct ErrorInfo {
165+
std::wstring sqlState;
166+
std::wstring ddbcErrorMsg;
167+
};
168+
ErrorInfo SQLCheckError_Wrap(SQLSMALLINT handleType, SqlHandlePtr handle, SQLRETURN retcode);

0 commit comments

Comments
 (0)