//--------------------------------------------------------------------------------------------------------------------------------- // File: OdbcConnection.cpp // Contents: Async calls to ODBC done in background thread // // Copyright Microsoft Corporation and contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // You may obtain a copy of the License at: // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //--------------------------------------------------------------------------------------------------------------------------------- #include "stdafx.h" #include #include #include #include #include #include namespace mssql { OdbcEnvironmentHandle OdbcConnection::environment; #ifdef WINDOWS_BUILD bool OdbcConnection::InitializeEnvironment() { // fprintf(stderr, ">> InitializeEnvironment\n\n"); auto ret = SQLSetEnvAttr(nullptr, SQL_ATTR_CONNECTION_POOLING, reinterpret_cast(SQL_CP_ONE_PER_HENV), 0); if (!SQL_SUCCEEDED(ret)) { return false; } if (!environment.alloc()) { return false; } ret = SQLSetEnvAttr(environment, SQL_ATTR_ODBC_VERSION, reinterpret_cast(SQL_OV_ODBC3), 0); if (!SQL_SUCCEEDED(ret)) { return false; } ret = SQLSetEnvAttr(environment, SQL_ATTR_CP_MATCH, reinterpret_cast(SQL_CP_RELAXED_MATCH), 0); if (!SQL_SUCCEEDED(ret)) { return false; } // fprintf(stderr, "<< InitializeEnvironment\n\n"); return true; } #endif #ifdef LINUX_BUILD bool OdbcConnection::InitializeEnvironment() { if (!environment.alloc()) { return false; } auto ret = SQLSetEnvAttr(environment, SQL_ATTR_CONNECTION_POOLING, reinterpret_cast(SQL_CP_ONE_PER_HENV), 0); if (!SQL_SUCCEEDED(ret)) { return false; } ret = SQLSetEnvAttr(environment, SQL_ATTR_ODBC_VERSION, reinterpret_cast(SQL_OV_ODBC3), 0); if (!SQL_SUCCEEDED(ret)) { return false; } //ret = SQLSetEnvAttr(environment, SQL_ATTR_CP_MATCH, reinterpret_cast(SQL_CP_RELAXED_MATCH), 0); if (!SQL_SUCCEEDED(ret)) { fprintf(stderr, " InitializeEnvironment fail %d\n", ret); return false; } return true; } #endif OdbcConnection::OdbcConnection() : _statements(nullptr), connectionState(Closed) { _errors = make_shared>>(); // ops = make_shared(); } OdbcConnection::~OdbcConnection() { //fprintf(stderr, "destruct OdbcConnection\n"); } bool OdbcConnection::TryClose() { if (connectionState != Closed) // fast fail before critical section { ScopedCriticalSectionLock crit_sec_lock(closeCriticalSection); // std::cerr << " TryClose " << std::endl; //fprintf(stderr, "TryClose - %llu\n", statements->size()); _statements->clear(); _connectionHandles->clear(); if (connectionState != Closed) { const auto ch = _connectionHandles->connectionHandle(); const auto& connection = *ch; SQLDisconnect(connection); connectionState = Closed; } } return true; } bool OdbcConnection::ReturnOdbcError() { _errors->clear(); const auto ch = _connectionHandles->connectionHandle(); const auto& connection = *ch; connection.read_errors(_errors); // cerr << " RETURN_ODBC_ERROR " << endl; // fprintf(stderr, "RETURN_ODBC_ERROR - free connection handle\n\n"); TryClose(); return false; } bool OdbcConnection::CheckOdbcError(const SQLRETURN ret) { if (!SQL_SUCCEEDED(ret)) { return ReturnOdbcError(); } return true; } SQLRETURN OdbcConnection::open_timeout(const int timeout) { if (timeout > 0) { const auto ch = _connectionHandles->connectionHandle(); const auto& connection = *ch; auto* const to = reinterpret_cast(static_cast(timeout)); auto ret = SQLSetConnectAttr(connection, SQL_ATTR_CONNECTION_TIMEOUT, to, 0); if (!CheckOdbcError(ret)) return false; ret = SQLSetConnectAttr(connection, SQL_ATTR_LOGIN_TIMEOUT, to, 0); if (!CheckOdbcError(ret)) return false; } return true; } bool OdbcConnection::try_open(shared_ptr> connection_string, const int timeout) { assert(connectionState == Closed); _errors->clear(); this->_connectionHandles = make_shared(environment); const auto connection = _connectionHandles->connectionHandle(); if (connection == nullptr) { _errors->clear(); environment.read_errors(_errors); //fprintf(stderr, "RETURN_ODBC_ERROR - free environment handle\n\n"); environment.free(); return false; } const auto &handle = *connection; _statements = make_shared(_connectionHandles); auto ret = open_timeout(timeout); if (!CheckOdbcError(ret)) return false; ret = SQLSetConnectAttr(handle, SQL_COPT_SS_BCP, reinterpret_cast(SQL_BCP_ON), SQL_IS_INTEGER); if (!CheckOdbcError(ret)) return false; ret = SQLDriverConnect(handle, nullptr, reinterpret_cast(connection_string->data()), connection_string->size(), nullptr, 0, nullptr, SQL_DRIVER_NOPROMPT); if (!CheckOdbcError(ret)) return false; connectionState = Open; return true; } bool OdbcConnection::try_begin_tran() { // turn off autocommit const auto connection = _connectionHandles->connectionHandle(); if (!connection) return false; auto* const acoff = reinterpret_cast(SQL_AUTOCOMMIT_OFF); const auto ret = SQLSetConnectAttr(*connection, SQL_ATTR_AUTOCOMMIT, acoff, SQL_IS_UINTEGER); return CheckOdbcError(ret); } bool OdbcConnection::send(OdbcOperation* op) const { //fprintf(stderr, "OdbcConnection send\n"); const auto res = op->fetch_statement(); Nan::AsyncQueueWorker(op); return res; } bool OdbcConnection::try_end_tran(const SQLSMALLINT completion_type) { const auto connection = _connectionHandles->connectionHandle(); if (!connection) return false; auto ret = SQLEndTran(SQL_HANDLE_DBC, *connection, completion_type); // cout << " try_end_tran " << "ret = " << ret << endl; if (!CheckOdbcError(ret)) return false; auto* const acon = reinterpret_cast(SQL_AUTOCOMMIT_ON); // put the connection back into auto commit mode ret = SQLSetConnectAttr(*connection, SQL_ATTR_AUTOCOMMIT, acon, SQL_IS_UINTEGER); return CheckOdbcError(ret); } }