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// -------------------------------------------------------------------------------------------------
1516Connection::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
105109bool 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
117121SqlHandlePtr 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- }
0 commit comments