@@ -26,54 +26,63 @@ Connection::Connection(const std::wstring& conn_str, bool autocommit)
2626 DriverLoader::getInstance ().loadDriver ();
2727 }
2828 SQLRETURN ret = SQLAllocHandle_ptr (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
29- checkError (ret, " Failed to allocate environment handle " );
29+ checkError (ret);
3030 _envHandle = std::make_shared<SqlHandle>(SQL_HANDLE_ENV, env);
3131
3232 LOG (" Setting environment attributes" );
3333 ret = SQLSetEnvAttr_ptr (_envHandle->get (), SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3_80, 0 );
34- checkError (ret, " Failed to set environment attribute " );
34+ checkError (ret);
3535 }
3636 allocateDbcHandle ();
3737}
3838
3939Connection::~Connection () {
40- disconnect (); // fallback if app forgets to disconnect
40+ disconnect (); // fallback if user forgets to disconnect
4141}
4242
4343// Allocates connection handle
4444void Connection::allocateDbcHandle () {
4545 SQLHANDLE dbc = nullptr ;
4646 LOG (" Allocate SQL Connection Handle" );
4747 SQLRETURN ret = SQLAllocHandle_ptr (SQL_HANDLE_DBC, _envHandle->get (), &dbc);
48- checkError (ret, " Failed to allocate connection handle " );
48+ checkError (ret);
4949 _dbcHandle = std::make_shared<SqlHandle>(SQL_HANDLE_DBC, dbc);
5050}
5151
52- void Connection::connect () {
52+ SQLRETURN Connection::connect (const py::dict& attrs_before ) {
5353 LOG (" Connecting to database" );
54+ // Apply access token before connect
55+ if (!attrs_before.is_none () && py::len (attrs_before) > 0 ) {
56+ LOG (" Apply attributes before connect" );
57+ applyAttrsBefore (attrs_before);
58+ if (_autocommit) {
59+ setAutocommit (_autocommit);
60+ }
61+ }
5462 SQLRETURN ret = SQLDriverConnect_ptr (
5563 _dbcHandle->get (), nullptr ,
5664 (SQLWCHAR*)_connStr.c_str (), SQL_NTS,
5765 nullptr , 0 , nullptr , SQL_DRIVER_NOPROMPT);
58- checkError (ret, " SQLDriverConnect failed" );
59- setAutocommit (_autocommit);
66+ checkError (ret);
6067}
6168
6269void Connection::disconnect () {
6370 if (_dbcHandle) {
6471 LOG (" Disconnecting from database" );
6572 SQLRETURN ret = SQLDisconnect_ptr (_dbcHandle->get ());
66- checkError (ret, " Failed to disconnect from database " );
73+ checkError (ret);
6774 _dbcHandle.reset (); // triggers SQLFreeHandle via destructor, if last owner
6875 }
6976 else {
7077 LOG (" No connection handle to disconnect" );
7178 }
7279}
7380
74- void Connection::checkError (SQLRETURN ret, const std::string& msg) const {
75- if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
76- throw std::runtime_error (" [ODBC Error] " + msg);
81+ void Connection::checkError (SQLRETURN ret) const {
82+ if (!SQL_SUCCEEDED (ret)) {
83+ ErrorInfo err = SQLCheckError_Wrap (SQL_HANDLE_DBC, _dbcHandle, ret);
84+ std::string errorMsg = std::string (err.ddbcErrorMsg .begin (), err.ddbcErrorMsg .end ());
85+ ThrowStdException (errorMsg);
7786 }
7887}
7988
@@ -83,7 +92,7 @@ void Connection::commit() {
8392 }
8493 LOG (" Committing transaction" );
8594 SQLRETURN ret = SQLEndTran_ptr (SQL_HANDLE_DBC, _dbcHandle->get (), SQL_COMMIT);
86- checkError (ret, " Failed to commit transaction " );
95+ checkError (ret);
8796}
8897
8998void Connection::rollback () {
@@ -92,7 +101,7 @@ void Connection::rollback() {
92101 }
93102 LOG (" Rolling back transaction" );
94103 SQLRETURN ret = SQLEndTran_ptr (SQL_HANDLE_DBC, _dbcHandle->get (), SQL_ROLLBACK);
95- checkError (ret, " Failed to rollback transaction " );
104+ checkError (ret);
96105}
97106
98107void Connection::setAutocommit (bool enable) {
@@ -102,7 +111,7 @@ void Connection::setAutocommit(bool enable) {
102111 SQLINTEGER value = enable ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
103112 LOG (" Set SQL Connection Attribute" );
104113 SQLRETURN ret = SQLSetConnectAttr_ptr (_dbcHandle->get (), SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)value, 0 );
105- checkError (ret, " Failed to set autocommit attribute " );
114+ checkError (ret);
106115 _autocommit = enable;
107116}
108117
@@ -114,7 +123,7 @@ bool Connection::getAutocommit() const {
114123 SQLINTEGER value;
115124 SQLINTEGER string_length;
116125 SQLRETURN ret = SQLGetConnectAttr_ptr (_dbcHandle->get (), SQL_ATTR_AUTOCOMMIT, &value, sizeof (value), &string_length);
117- checkError (ret, " Failed to get autocommit attribute " );
126+ checkError (ret);
118127 return value == SQL_AUTOCOMMIT_ON;
119128}
120129
@@ -125,32 +134,54 @@ SqlHandlePtr Connection::allocStatementHandle() {
125134 LOG (" Allocating statement handle" );
126135 SQLHANDLE stmt = nullptr ;
127136 SQLRETURN ret = SQLAllocHandle_ptr (SQL_HANDLE_STMT, _dbcHandle->get (), &stmt);
128- checkError (ret, " Failed to allocate statement handle " );
137+ checkError (ret);
129138 return std::make_shared<SqlHandle>(SQL_HANDLE_STMT, stmt);
130139}
131140
132- SqlHandlePtr Connection::getSharedEnvHandle () {
133- static std::once_flag flag;
134- static SqlHandlePtr env_handle;
135141
136- std::call_once (flag, []() {
137- LOG (" Allocating environment handle" );
138- SQLHANDLE env = nullptr ;
139- if (!SQLAllocHandle_ptr) {
140- LOG (" Function pointers not initialized, loading driver" );
141- DriverLoader::getInstance ().loadDriver ();
142- }
143- SQLRETURN ret = SQLAllocHandle_ptr (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
144- if (!SQL_SUCCEEDED (ret)) {
145- throw std::runtime_error (" Failed to allocate environment handle" );
142+ SQLRETURN Connection::setAttribute (SQLINTEGER attribute, py::object value) {
143+ LOG (" Setting SQL attribute" );
144+ SQLPOINTER ptr = nullptr ;
145+ SQLINTEGER length = 0 ;
146+
147+ if (py::isinstance<py::int_>(value)) {
148+ int intValue = value.cast <int >();
149+ ptr = reinterpret_cast <SQLPOINTER>(static_cast <uintptr_t >(intValue));
150+ length = SQL_IS_INTEGER;
151+ } else if (py::isinstance<py::bytes>(value) || py::isinstance<py::bytearray>(value)) {
152+ static std::vector<std::string> buffers;
153+ buffers.emplace_back (value.cast <std::string>());
154+ ptr = const_cast <char *>(buffers.back ().c_str ());
155+ length = static_cast <SQLINTEGER>(buffers.back ().size ());
156+ } else {
157+ LOG (" Unsupported attribute value type" );
158+ return SQL_ERROR;
159+ }
160+
161+ SQLRETURN ret = SQLSetConnectAttr_ptr (_dbcHandle->get (), attribute, ptr, length);
162+ if (!SQL_SUCCEEDED (ret)) {
163+ LOG (" Failed to set attribute" );
164+ }
165+ else {
166+ LOG (" Set attribute successfully" );
167+ }
168+ return ret;
169+ }
170+
171+ void Connection::applyAttrsBefore (const py::dict& attrs) {
172+ for (const auto & item : attrs) {
173+ int key;
174+ try {
175+ key = py::cast<int >(item.first );
176+ } catch (...) {
177+ continue ;
146178 }
147- env_handle = std::make_shared<SqlHandle>(SQL_HANDLE_ENV, env);
148179
149- LOG (" Setting environment attributes" );
150- ret = SQLSetEnvAttr_ptr (env_handle->get (), SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3_80, 0 );
151- if (!SQL_SUCCEEDED (ret)) {
152- throw std::runtime_error (" Failed to set environment attribute" );
180+ if (key == SQL_COPT_SS_ACCESS_TOKEN) {
181+ SQLRETURN ret = setAttribute (key, py::reinterpret_borrow<py::object>(item.second ));
182+ if (!SQL_SUCCEEDED (ret)) {
183+ throw std::runtime_error (" Failed to set access token before connect" );
184+ }
153185 }
154- });
155- return env_handle;
186+ }
156187}
0 commit comments