/* $Id: dbapi_impl_context.cpp 630289 2021-04-29 18:22:17Z ivanov $ * =========================================================================== * * PUBLIC DOMAIN NOTICE * National Center for Biotechnology Information * * This software/database is a "United States Government Work" under the * terms of the United States Copyright Act. It was written as part of * the author's official duties as a United States Government employee and * thus cannot be copyrighted. This software/database is freely available * to the public for use. The National Library of Medicine and the U.S. * Government have not placed any restriction on its use or reproduction. * * Although all reasonable efforts have been taken to ensure the accuracy * and reliability of the software and data, the NLM and the U.S. * Government do not and cannot warrant the performance or results that * may be obtained by using this software or data. The NLM and the U.S. * Government disclaim all warranties, express or implied, including * warranties of performance, merchantability or fitness for any particular * purpose. * * Please cite the author in any work or product based on this material. * * =========================================================================== * * Author: Sergey Sikorskiy * */ #include #include #include #include #include #include #include #include #include #include #if defined(NCBI_OS_MSWIN) # include #endif BEGIN_NCBI_SCOPE #define NCBI_USE_ERRCODE_X Dbapi_DrvrContext NCBI_PARAM_DEF_EX(bool, dbapi, conn_use_encrypt_data, false, eParam_NoThread, NULL); namespace impl { inline static bool s_Matches(CConnection* conn, const string& pool_name, const string& server_name) { if (pool_name.empty()) { // There is no server name check here. We assume that a connection // pool contains connections with appropriate server names only. return conn->ServerName() == server_name || conn->GetRequestedServer() == server_name; } else { return conn->PoolName() == pool_name; } } /////////////////////////////////////////////////////////////////////////// // CDriverContext:: // CDriverContext::CDriverContext(void) : m_PoolSem(0, 1), m_LoginTimeout(0), m_Timeout(0), m_CancelTimeout(0), m_MaxBlobSize(0), m_ClientEncoding(eEncoding_ISO8859_1) { PushCntxMsgHandler ( &CDB_UserHandler::GetDefault(), eTakeOwnership ); PushDefConnMsgHandler ( &CDB_UserHandler::GetDefault(), eTakeOwnership ); } CDriverContext::~CDriverContext(void) { try { DeleteAllConn(); } STD_CATCH_ALL("CDriverContext::DeleteAllConn"); } void CDriverContext::SetApplicationName(const string& app_name) { CMutexGuard mg(x_GetCtxMtx()); m_AppName = app_name; } string CDriverContext::GetApplicationName(void) const { CMutexGuard mg(x_GetCtxMtx()); return m_AppName; } void CDriverContext::SetHostName(const string& host_name) { CMutexGuard mg(x_GetCtxMtx()); m_HostName = host_name; } string CDriverContext::GetHostName(void) const { CMutexGuard mg(x_GetCtxMtx()); return m_HostName; } unsigned int CDriverContext::GetLoginTimeout(void) const { CMutexGuard mg(x_GetCtxMtx()); return m_LoginTimeout; } bool CDriverContext::SetLoginTimeout (unsigned int nof_secs) { CMutexGuard mg(x_GetCtxMtx()); m_LoginTimeout = nof_secs; return true; } unsigned int CDriverContext::GetTimeout(void) const { CMutexGuard mg(x_GetCtxMtx()); return m_Timeout; } bool CDriverContext::SetTimeout(unsigned int nof_secs) { bool success = true; CMutexGuard mg(x_GetCtxMtx()); try { m_Timeout = nof_secs; // We do not have to update query/connection timeout in context // any more. Each connection can be updated explicitly now. // UpdateConnTimeout(); } catch (...) { success = false; } return success; } unsigned int CDriverContext::GetCancelTimeout(void) const { CMutexGuard mg(x_GetCtxMtx()); return m_CancelTimeout; } bool CDriverContext::SetCancelTimeout(unsigned int nof_secs) { bool success = true; CMutexGuard mg(x_GetCtxMtx()); try { m_CancelTimeout = nof_secs; } catch (...) { success = false; } return success; } bool CDriverContext::SetMaxBlobSize(size_t nof_bytes) { CMutexGuard mg(x_GetCtxMtx()); m_MaxBlobSize = nof_bytes; UpdateConnMaxBlobSize(); return true; } void CDriverContext::PushCntxMsgHandler(CDB_UserHandler* h, EOwnership ownership) { CMutexGuard mg(x_GetCtxMtx()); m_CntxHandlers.Push(h, ownership); } void CDriverContext::PopCntxMsgHandler(CDB_UserHandler* h) { CMutexGuard mg(x_GetCtxMtx()); m_CntxHandlers.Pop(h); } void CDriverContext::PushDefConnMsgHandler(CDB_UserHandler* h, EOwnership ownership) { CMutexGuard mg(x_GetCtxMtx()); m_ConnHandlers.Push(h, ownership); } void CDriverContext::PopDefConnMsgHandler(CDB_UserHandler* h) { CMutexGuard mg(x_GetCtxMtx()); m_ConnHandlers.Pop(h); // Remove this handler from all connections TConnPool::value_type con = NULL; ITERATE(TConnPool, it, m_NotInUse) { con = *it; con->PopMsgHandler(h); } ITERATE(TConnPool, it, m_InUse) { con = *it; con->PopMsgHandler(h); } } void CDriverContext::ResetEnvSybase(void) { DEFINE_STATIC_MUTEX(env_mtx); CMutexGuard mg(env_mtx); CNcbiEnvironment env; // If user forces his own Sybase client path using $RESET_SYBASE // and $SYBASE -- use that unconditionally. try { if (env.Get("LC_ALL") == "POSIX") { // Canonicalize, since locales.dat might list C but not POSIX. env.Set("LC_ALL", "C"); } if (!env.Get("SYBASE").empty()) { string reset = env.Get("RESET_SYBASE"); if ( !reset.empty() && NStr::StringToBool(reset)) { return; } } // ...else try hardcoded paths } catch (const CStringException&) { // Conversion error -- try hardcoded paths too } // User-set or default hardcoded path if ( CDir(NCBI_GetSybasePath()).CheckAccess(CDirEntry::fRead) ) { env.Set("SYBASE", NCBI_GetSybasePath()); return; } // If NCBI_SetSybasePath() was used to set up the Sybase path, and it is // not right, then use the very Sybase client against which the code was // compiled if ( !NStr::Equal(NCBI_GetSybasePath(), NCBI_GetDefaultSybasePath()) && CDir(NCBI_GetDefaultSybasePath()).CheckAccess(CDirEntry::fRead) ) { env.Set("SYBASE", NCBI_GetDefaultSybasePath()); } // Else, well, use whatever $SYBASE there is } void CDriverContext::x_Recycle(CConnection* conn, bool conn_reusable) { CMutexGuard mg(m_PoolMutex); TConnPool::iterator it = find(m_InUse.begin(), m_InUse.end(), conn); if (it != m_InUse.end()) { m_InUse.erase(it); } #ifdef NCBI_THREADS bool for_sem = false; if ( !m_PoolSemSubject.empty() && (m_PoolSemSubjectHasPoolName ? conn->PoolName() == m_PoolSemSubject : s_Matches(conn, kEmptyStr, m_PoolSemSubject))) { for_sem = true; m_PoolSemSubject.erase(); m_PoolSemConn = NULL; m_PoolSem.TryWait(); // probably redundant, but ensures Post will work m_PoolSem.Post(); } #endif bool keep = false; if (conn_reusable && conn->IsOpeningFinished() && conn->IsValid()) { keep = true; // Tentatively; account for temporary overflow below, bearing // in mind that that conn is already gone from m_InUse and not // yet in m_NotInUse, and as such uncounted. if (conn->m_PoolMaxSize <= m_InUse.size() + m_NotInUse.size()) { unsigned int n; if (conn->m_Pool.empty()) { n = NofConnections(conn->m_RequestedServer); } else { n = NofConnections(kEmptyStr, conn->m_Pool); } if (n >= conn->m_PoolMaxSize) { keep = false; } } if (keep && conn->m_ReuseCount + 1 > conn->m_PoolMaxConnUse) { // CXX-4420: limit the number of time the connection is reused keep = false; } } if (keep) { if (conn->m_PoolIdleTimeParam.GetSign() != eNegative) { CTime now(CTime::eCurrent); conn->m_CleanupTime = now + conn->m_PoolIdleTimeParam; } m_NotInUse.push_back(conn); #ifdef NCBI_THREADS if (for_sem) { m_PoolSemConn = conn; } #endif } else { x_AdjustCounts(conn, -1); delete conn; } CloseOldIdleConns(1); } void CDriverContext::x_AdjustCounts(const CConnection* conn, int delta) { if (delta != 0 && conn->IsReusable()) { CMutexGuard mg(m_PoolMutex); string server_name = conn->GetServerName(); if (conn->Host() != 0 && server_name.find('@') == NPOS) { server_name += '@' + ConvertN2A(conn->Host()); if (conn->Port() != 0) { server_name += ':' + NStr::NumericToString(conn->Port()); } } _DEBUG_ARG(unsigned int pool_count =) m_CountsByPool[conn->PoolName()][server_name] += delta; _DEBUG_ARG(unsigned int service_count =) m_CountsByService[conn->GetRequestedServer()][server_name] += delta; _TRACE(server_name << " count += " << delta << " for pool " << conn->PoolName() << " (" << pool_count << ") and service " << conn->GetRequestedServer() << " (" << service_count << ')'); } } void CDriverContext::x_GetCounts(const TCountsMap& main_map, const string& name, TCounts* counts) const { CMutexGuard mg(m_PoolMutex); auto it = main_map.find(name); if (it == main_map.end()) { counts->clear(); } else { *counts = it->second; } } void CDriverContext::CloseUnusedConnections(const string& srv_name, const string& pool_name, unsigned int max_closings) { CMutexGuard mg(m_PoolMutex); TConnPool::value_type con; // close all connections first ERASE_ITERATE(TConnPool, it, m_NotInUse) { con = *it; if ( !srv_name.empty() && srv_name != con->ServerName() && srv_name != con->GetRequestedServer() ) continue; if((!pool_name.empty()) && pool_name.compare(con->PoolName())) continue; it = m_NotInUse.erase(it); x_AdjustCounts(con, -1); delete con; if (--max_closings == 0) { break; } } } unsigned int CDriverContext::NofConnections(const TSvrRef& svr_ref, const string& pool_name) const { CMutexGuard mg(m_PoolMutex); if ((!svr_ref || !svr_ref->IsValid()) && pool_name.empty()) { return static_cast(m_InUse.size() + m_NotInUse.size()); } string server; Uint4 host = 0; Uint2 port = 0; if (svr_ref) { host = svr_ref->GetHost(); port = svr_ref->GetPort(); if (host == 0) server = svr_ref->GetName(); } const TConnPool* pools[] = {&m_NotInUse, &m_InUse}; int n = 0; for (size_t i = 0; i < ArraySize(pools); ++i) { ITERATE(TConnPool, it, (*pools[i])) { TConnPool::value_type con = *it; if(!server.empty()) { if (server != con->ServerName() && server != con->GetRequestedServer()) continue; } else if (host != 0) { if (host != con->Host() || port != con->Port()) continue; } if((!pool_name.empty()) && pool_name.compare(con->PoolName())) continue; ++n; } } return n; } unsigned int CDriverContext::NofConnections(const string& srv_name, const string& pool_name) const { TSvrRef svr_ref(new CDBServer(srv_name, 0, 0)); return NofConnections(svr_ref, pool_name); } CDB_Connection* CDriverContext::MakeCDBConnection(CConnection* connection, int delta) { connection->m_CleanupTime.Clear(); CMutexGuard mg(m_PoolMutex); m_InUse.push_back(connection); x_AdjustCounts(connection, delta); return new CDB_Connection(connection); } CDB_Connection* CDriverContext::MakePooledConnection(const CDBConnParams& params) { if (params.GetParam("is_pooled") == "true") { CMutexGuard mg(m_PoolMutex); string pool_name (params.GetParam("pool_name")), server_name(params.GetServerName()); ERASE_ITERATE(TConnPool, it, m_NotInUse) { CConnection* t_con(*it); if (s_Matches(t_con, pool_name, server_name)) { it = m_NotInUse.erase(it); CDB_Connection* dbcon = MakeCDBConnection(t_con, 0); if (dbcon->Refresh()) { /* Future development ... if (!params.GetDatabaseName().empty()) { return SetDatabase(dbcon, params); } else { return dbcon; } */ return dbcon; } else { x_AdjustCounts(t_con, -1); t_con->m_Reusable = false; delete dbcon; } } } // Connection should be created, but we can have limit on number of // connections in the pool. string pool_max_str(params.GetParam("pool_maxsize")); if (!pool_max_str.empty() && pool_max_str != "default") { int pool_max = NStr::StringToInt(pool_max_str); if (pool_max != 0) { int total_cnt = 0; ITERATE(TConnPool, it, m_InUse) { CConnection* t_con(*it); if (s_Matches(t_con, pool_name, server_name)) { ++total_cnt; } } if (total_cnt >= pool_max) { #ifdef NCBI_THREADS string timeout_str(params.GetParam("pool_wait_time")); double timeout_val = 0.0; if ( !timeout_str.empty() && timeout_str != "default") { timeout_val = NStr::StringToDouble(timeout_str); } CTimeout timeout(timeout_val); if (pool_name.empty()) { m_PoolSemSubject = server_name; m_PoolSemSubjectHasPoolName = false; } else { m_PoolSemSubject = pool_name; m_PoolSemSubjectHasPoolName = true; } mg.Release(); if (m_PoolSem.TryWait(timeout)) { mg.Guard(m_PoolMutex); CConnection* t_con = NULL; NON_CONST_REVERSE_ITERATE(TConnPool, it, m_NotInUse) { if (*it == m_PoolSemConn) { t_con = m_PoolSemConn; m_PoolSemConn = NULL; m_NotInUse.erase((++it).base()); break; } } if (t_con != NULL) { return MakeCDBConnection(t_con, 0); } } else #endif if (params.GetParam("pool_allow_temp_overflow") != "true") { string msg = FORMAT("Connection pool full (size " << pool_max << ") for " << (pool_name.empty() ? server_name : pool_name)); DATABASE_DRIVER_ERROR(msg, 200011); } } } } } if (params.GetParam("do_not_connect") == "true") { return NULL; } // Precondition check. if (!TDbapi_CanUseKerberos::GetDefault() && (params.GetUserName().empty() || params.GetPassword().empty())) { string err_msg("Insufficient credentials to connect."); if (params.GetUserName().empty()) { err_msg += " User name has not been set."; } if (params.GetPassword().empty()) { err_msg += " Password has not been set."; } DATABASE_DRIVER_ERROR( err_msg, 200010 ); } CConnection* t_con = MakeIConnection(params); return MakeCDBConnection(t_con, 1); } void CDriverContext::CloseAllConn(void) { CMutexGuard mg(m_PoolMutex); // close all connections first ITERATE(TConnPool, it, m_NotInUse) { x_AdjustCounts(*it, -1); delete *it; } m_NotInUse.clear(); ITERATE(TConnPool, it, m_InUse) { (*it)->Close(); } } void CDriverContext::DeleteAllConn(void) { CMutexGuard mg(m_PoolMutex); // close all connections first ITERATE(TConnPool, it, m_NotInUse) { x_AdjustCounts(*it, -1); delete *it; } m_NotInUse.clear(); ITERATE(TConnPool, it, m_InUse) { x_AdjustCounts(*it, -1); delete *it; } m_InUse.clear(); } class CMakeConnActualParams: public CDBConnParamsBase { public: CMakeConnActualParams(const CDBConnParams& other) : m_Other(other) { // Override what is set in CDBConnParamsBase constructor SetParam("secure_login", kEmptyStr); SetParam("is_pooled", kEmptyStr); SetParam("do_not_connect", kEmptyStr); } ~CMakeConnActualParams(void) {} virtual Uint4 GetProtocolVersion(void) const { return m_Other.GetProtocolVersion(); } virtual EEncoding GetEncoding(void) const { return m_Other.GetEncoding(); } virtual string GetServerName(void) const { return CDBConnParamsBase::GetServerName(); } virtual string GetDatabaseName(void) const { return CDBConnParamsBase::GetDatabaseName(); } virtual string GetUserName(void) const { return CDBConnParamsBase::GetUserName(); } virtual string GetPassword(void) const { return CDBConnParamsBase::GetPassword(); } virtual EServerType GetServerType(void) const { return m_Other.GetServerType(); } virtual Uint4 GetHost(void) const { return m_Other.GetHost(); } virtual Uint2 GetPort(void) const { return m_Other.GetPort(); } virtual CRef GetConnValidator(void) const { return m_Other.GetConnValidator(); } virtual string GetParam(const string& key) const { string result(CDBConnParamsBase::GetParam(key)); if (result.empty()) return m_Other.GetParam(key); else return result; } using CDBConnParamsBase::SetServerName; using CDBConnParamsBase::SetUserName; using CDBConnParamsBase::SetDatabaseName; using CDBConnParamsBase::SetPassword; using CDBConnParamsBase::SetParam; private: const CDBConnParams& m_Other; }; struct SLoginData { string server_name; string user_name; string db_name; string password; SLoginData(const string& sn, const string& un, const string& dn, const string& pass) : server_name(sn), user_name(un), db_name(dn), password(pass) {} bool operator< (const SLoginData& right) const { if (server_name != right.server_name) return server_name < right.server_name; else if (user_name != right.user_name) return user_name < right.user_name; else if (db_name != right.db_name) return db_name < right.db_name; else return password < right.password; } }; static void s_TransformLoginData(string& server_name, string& user_name, string& db_name, string& password) { if (!TDbapi_ConnUseEncryptData::GetDefault()) return; string app_name = CNcbiApplication::Instance()->GetProgramDisplayName(); set visited; CNcbiResourceInfoFile res_file(CNcbiResourceInfoFile::GetDefaultFileName()); visited.insert(SLoginData(server_name, user_name, db_name, password)); for (;;) { string res_name = app_name; if (!user_name.empty()) { res_name += "/"; res_name += user_name; } if (!server_name.empty()) { res_name += "@"; res_name += server_name; } if (!db_name.empty()) { res_name += ":"; res_name += db_name; } const CNcbiResourceInfo& info = res_file.GetResourceInfo(res_name, password); if (!info) break; password = info.GetValue(); typedef CNcbiResourceInfo::TExtraValuesMap TExtraMap; typedef TExtraMap::const_iterator TExtraMapIt; const TExtraMap& extra = info.GetExtraValues().GetPairs(); TExtraMapIt it = extra.find("server"); if (it != extra.end()) server_name = it->second; it = extra.find("username"); if (it != extra.end()) user_name = it->second; it = extra.find("database"); if (it != extra.end()) db_name = it->second; if (!visited.insert( SLoginData(server_name, user_name, db_name, password)).second) { DATABASE_DRIVER_ERROR( "Circular dependency inside resources info file.", 100012); } } } void SDBConfParams::Clear(void) { flags = 0; server.clear(); port.clear(); database.clear(); username.clear(); password.clear(); login_timeout.clear(); io_timeout.clear(); single_server.clear(); is_pooled.clear(); pool_name.clear(); pool_maxsize.clear(); pool_idle_time.clear(); pool_wait_time.clear(); pool_allow_temp_overflow.clear(); pool_max_conn_use.clear(); args.clear(); } void CDriverContext::ReadDBConfParams(const string& service_name, SDBConfParams* params) { params->Clear(); if (service_name.empty()) return; CNcbiApplication* app = CNcbiApplication::Instance(); if (!app) return; const IRegistry& reg = app->GetConfig(); string section_name(service_name); section_name.append(1, '.'); section_name.append("dbservice"); if (!reg.HasEntry(section_name, kEmptyStr)) return; if (reg.HasEntry(section_name, "service", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fServerSet; params->server = reg.Get(section_name, "service"); } if (reg.HasEntry(section_name, "port", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fPortSet; params->port = reg.Get(section_name, "port"); } if (reg.HasEntry(section_name, "database", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fDatabaseSet; params->database = reg.Get(section_name, "database"); } if (reg.HasEntry(section_name, "username", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fUsernameSet; params->username = reg.Get(section_name, "username"); } if (reg.HasEntry(section_name, "password_key", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fPasswordKeySet; params->password_key_id = reg.Get(section_name, "password_key"); } if (reg.HasEntry(section_name, "password", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fPasswordSet; params->password = reg.Get(section_name, "password"); if (CNcbiEncrypt::IsEncrypted(params->password)) { try { params->password = CNcbiEncrypt::Decrypt(params->password); } NCBI_CATCH("Password decryption for " + service_name); } else if (params->password_key_id.empty()) { ERR_POST(Warning << "Using unencrypted password for " + service_name); } } if (reg.HasEntry(section_name, "password_file", IRegistry::fCountCleared)) { // password and password_file are mutually exclusive, but only SDBAPI // actually honors the latter, so it will take care of throwing // exceptions as necessary. params->flags += SDBConfParams::fPasswordFileSet; params->password_file = reg.Get(section_name, "password_file"); } if (reg.HasEntry(section_name, "login_timeout", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fLoginTimeoutSet; params->login_timeout = reg.Get(section_name, "login_timeout"); } if (reg.HasEntry(section_name, "io_timeout", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fIOTimeoutSet; params->io_timeout = reg.Get(section_name, "io_timeout"); } if (reg.HasEntry(section_name, "cancel_timeout", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fCancelTimeoutSet; params->cancel_timeout = reg.Get(section_name, "cancel_timeout"); } if (reg.HasEntry(section_name, "exclusive_server", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fSingleServerSet; params->single_server = reg.Get(section_name, "exclusive_server"); } if (reg.HasEntry(section_name, "use_conn_pool", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fIsPooledSet; params->is_pooled = reg.Get(section_name, "use_conn_pool"); params->pool_name = section_name; params->pool_name.append(1, '.'); params->pool_name.append("pool"); } if (reg.HasEntry(section_name, "conn_pool_name", IRegistry::fCountCleared)) { // params->flags += SDBConfParams::fPoolNameSet; params->pool_name = reg.Get(section_name, "conn_pool_name"); } if (reg.HasEntry(section_name, "conn_pool_minsize", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fPoolMinSizeSet; params->pool_minsize = reg.Get(section_name, "conn_pool_minsize"); } if (reg.HasEntry(section_name, "conn_pool_maxsize", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fPoolMaxSizeSet; params->pool_maxsize = reg.Get(section_name, "conn_pool_maxsize"); } if (reg.HasEntry(section_name, "conn_pool_idle_time", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fPoolIdleTimeSet; params->pool_idle_time = reg.Get(section_name, "conn_pool_idle_time"); } if (reg.HasEntry(section_name, "conn_pool_wait_time", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fPoolWaitTimeSet; params->pool_wait_time = reg.Get(section_name, "conn_pool_wait_time"); } if (reg.HasEntry(section_name, "conn_pool_allow_temp_overflow", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fPoolAllowTempSet; params->pool_allow_temp_overflow = reg.Get(section_name, "conn_pool_allow_temp_overflow"); } if (reg.HasEntry(section_name, "continue_after_raiserror", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fContRaiserrorSet; params->continue_after_raiserror = reg.Get(section_name, "continue_after_raiserror"); } if (reg.HasEntry(section_name, "conn_pool_max_conn_use", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fPoolMaxConnUseSet; params->pool_max_conn_use = reg.Get(section_name, "conn_pool_max_conn_use"); } if (reg.HasEntry(section_name, "args", IRegistry::fCountCleared)) { params->flags += SDBConfParams::fArgsSet; params->args = reg.Get(section_name, "args"); } } bool CDriverContext::SatisfyPoolMinimum(const CDBConnParams& params) { CMutexGuard mg(m_PoolMutex); string pool_min_str = params.GetParam("pool_minsize"); if (pool_min_str.empty() || pool_min_str == "default") return true; int pool_min = NStr::StringToInt(pool_min_str); if (pool_min <= 0) return true; string pool_name = params.GetParam("pool_name"), server_name = params.GetServerName(); int total_cnt = 0; ITERATE(TConnPool, it, m_InUse) { CConnection* t_con(*it); if (t_con->IsReusable() && s_Matches(t_con, pool_name, server_name) && t_con->IsValid() && t_con->IsAlive()) { ++total_cnt; } } ITERATE(TConnPool, it, m_NotInUse) { CConnection* t_con(*it); if (t_con->IsReusable() && s_Matches(t_con, pool_name, server_name) && t_con->IsAlive()) { ++total_cnt; } } mg.Release(); vector< AutoPtr > conns(pool_min); for (int i = total_cnt; i < pool_min; ++i) { try { conns.push_back(MakeConnection(params)); } catch (CDB_Exception& ex) { ERR_POST_X(1, "Error filling connection pool: " << ex); return false; } } return true; } CDB_Connection* CDriverContext::MakeConnection(const CDBConnParams& params) { CMutexGuard mg(x_GetCtxMtx()); CMakeConnActualParams act_params(params); SDBConfParams conf_params; conf_params.Clear(); if (params.GetParam("do_not_read_conf") != "true") { ReadDBConfParams(params.GetServerName(), &conf_params); } int was_timeout = GetTimeout(); int was_login_timeout = GetLoginTimeout(); unique_ptr t_con; try { string server_name = (conf_params.IsServerSet()? conf_params.server: params.GetServerName()); string user_name = (conf_params.IsUsernameSet()? conf_params.username: params.GetUserName()); string db_name = (conf_params.IsDatabaseSet()? conf_params.database: params.GetDatabaseName()); string password = (conf_params.IsPasswordSet()? conf_params.password: params.GetPassword()); if (conf_params.IsLoginTimeoutSet()) { if (conf_params.login_timeout.empty()) { SetLoginTimeout(0); } else { SetLoginTimeout(NStr::StringToInt(conf_params.login_timeout)); } } else { string value(params.GetParam("login_timeout")); if (value == "default") { SetLoginTimeout(0); } else if (!value.empty()) { SetLoginTimeout(NStr::StringToInt(value)); } } if (conf_params.IsIOTimeoutSet()) { if (conf_params.io_timeout.empty()) { SetTimeout(0); } else { SetTimeout(NStr::StringToInt(conf_params.io_timeout)); } } else { string value(params.GetParam("io_timeout")); if (value == "default") { SetTimeout(0); } else if (!value.empty()) { SetTimeout(NStr::StringToInt(value)); } } if (conf_params.IsCancelTimeoutSet()) { if (conf_params.cancel_timeout.empty()) { SetCancelTimeout(0); } else { SetCancelTimeout(NStr::StringToInt(conf_params.cancel_timeout)); } } else { string value(params.GetParam("cancel_timeout")); if (value == "default") { SetCancelTimeout(10); } else if (!value.empty()) { SetCancelTimeout(NStr::StringToInt(value)); } } if (conf_params.IsSingleServerSet()) { if (conf_params.single_server.empty()) { act_params.SetParam("single_server", "true"); } else { act_params.SetParam("single_server", NStr::BoolToString(NStr::StringToBool( conf_params.single_server))); } } else if (params.GetParam("single_server") == "default") { act_params.SetParam("single_server", "true"); } if (conf_params.IsPooledSet()) { if (conf_params.is_pooled.empty()) { act_params.SetParam("is_pooled", "false"); } else { act_params.SetParam("is_pooled", NStr::BoolToString(NStr::StringToBool( conf_params.is_pooled))); act_params.SetParam("pool_name", conf_params.pool_name); } } else if (params.GetParam("is_pooled") == "default") { act_params.SetParam("is_pooled", "false"); } if (conf_params.IsPoolMinSizeSet()) act_params.SetParam("pool_minsize", conf_params.pool_minsize); else if (params.GetParam("pool_minsize") == "default") { act_params.SetParam("pool_minsize", "0"); } if (conf_params.IsPoolMaxSizeSet()) act_params.SetParam("pool_maxsize", conf_params.pool_maxsize); else if (params.GetParam("pool_maxsize") == "default") { act_params.SetParam("pool_maxsize", ""); } if (conf_params.IsPoolIdleTimeSet()) act_params.SetParam("pool_idle_time", conf_params.pool_idle_time); else if (params.GetParam("pool_idle_time") == "default") { act_params.SetParam("pool_idle_time", ""); } if (conf_params.IsPoolWaitTimeSet()) act_params.SetParam("pool_wait_time", conf_params.pool_wait_time); else if (params.GetParam("pool_wait_time") == "default") { act_params.SetParam("pool_wait_time", "0"); } if (conf_params.IsPoolAllowTempOverflowSet()) { if (conf_params.pool_allow_temp_overflow.empty()) { act_params.SetParam("pool_allow_temp_overflow", "false"); } else { act_params.SetParam ("pool_allow_temp_overflow", NStr::BoolToString( NStr::StringToBool( conf_params.pool_allow_temp_overflow))); } } else if (params.GetParam("pool_allow_temp_overflow") == "default") { act_params.SetParam("pool_allow_temp_overflow", "false"); } if (conf_params.IsPoolMaxConnUseSet()) act_params.SetParam("pool_max_conn_use", conf_params.pool_max_conn_use); else if (params.GetParam("pool_max_conn_use") == "default") { act_params.SetParam("pool_max_conn_use", "0"); } if (conf_params.IsContinueAfterRaiserrorSet()) { if (conf_params.continue_after_raiserror.empty()) { act_params.SetParam("continue_after_raiserror", "false"); } else { act_params.SetParam ("continue_after_raiserror", NStr::BoolToString( NStr::StringToBool( conf_params.continue_after_raiserror))); } } else if (params.GetParam("continue_after_raiserror") == "default") { act_params.SetParam("continue_after_raiserror", "false"); } s_TransformLoginData(server_name, user_name, db_name, password); act_params.SetServerName(server_name); act_params.SetUserName(user_name); act_params.SetDatabaseName(db_name); act_params.SetPassword(password); CRef factory = CDbapiConnMgr::Instance().GetConnectionFactory(); t_con.reset(factory->MakeDBConnection(*this, act_params)); if (t_con.get() == NULL) { if (act_params.GetParam("do_not_connect") == "true") { return NULL; } string err; err += "Cannot connect to the server '" + act_params.GetServerName(); err += "' as user '" + act_params.GetUserName() + "'"; CDB_ClientEx ex(DIAG_COMPILE_INFO, NULL, err, eDiag_Error, 100011); ex.SetRetriable(eRetriable_No); CDB_UserHandler::TExceptions* expts = factory->GetExceptions(); if (expts) { NON_CONST_REVERSE_ITERATE(CDB_UserHandler::TExceptions, it, *expts) { ex.AddPrevious(*it); } } throw ex; } // Set database ... t_con->SetDatabaseName(act_params.GetDatabaseName()); } catch (exception&) { SetTimeout(was_timeout); SetLoginTimeout(was_login_timeout); throw; } SetTimeout(was_timeout); SetLoginTimeout(was_login_timeout); return t_con.release(); } size_t CDriverContext::CloseConnsForPool(const string& pool_name, Uint4 keep_host_ip, Uint2 keep_port) { size_t invalidated_count = 0; CMutexGuard mg(m_PoolMutex); ITERATE(TConnPool, it, m_InUse) { CConnection* t_con(*it); if (t_con->IsReusable() && pool_name == t_con->PoolName()) { if (t_con->Host() != keep_host_ip || t_con->Port() != keep_port) { t_con->Invalidate(); ++invalidated_count; } } } ERASE_ITERATE(TConnPool, it, m_NotInUse) { CConnection* t_con(*it); if (t_con->IsReusable() && pool_name == t_con->PoolName()) { if (t_con->Host() != keep_host_ip || t_con->Port() != keep_port) { m_NotInUse.erase(it); x_AdjustCounts(t_con, -1); delete t_con; } } } return invalidated_count; } void CDriverContext::CloseOldIdleConns(unsigned int max_closings, const string& pool_name) { CMutexGuard mg(m_PoolMutex); if (max_closings == 0) { return; } set at_min_by_pool, at_min_by_server; CTime now(CTime::eCurrent); ERASE_ITERATE (TConnPool, it, m_NotInUse) { const string& pool_name_2 = (*it)->PoolName(); set& at_min = pool_name_2.empty() ? at_min_by_server : at_min_by_pool; if (pool_name_2.empty()) { if ( !pool_name.empty() || at_min.find((*it)->GetRequestedServer()) != at_min.end()) { continue; } } else if (( !pool_name.empty() && pool_name != pool_name_2) || at_min.find(pool_name_2) != at_min.end()) { continue; } if ((*it)->m_CleanupTime.IsEmpty() || (*it)->m_CleanupTime > now) { continue; } unsigned int n; if (pool_name_2.empty()) { n = NofConnections((*it)->GetRequestedServer()); } else { n = NofConnections(TSvrRef(), pool_name_2); } if (n > (*it)->m_PoolMinSize) { x_AdjustCounts(*it, -1); delete *it; m_NotInUse.erase(it); if (--max_closings == 0) { break; } } else { at_min.insert(pool_name_2); } } } void CDriverContext::DestroyConnImpl(CConnection* impl) { if (impl) { impl->ReleaseInterface(); x_Recycle(impl, impl->IsReusable()); } } void CDriverContext::SetClientCharset(const string& charset) { CMutexGuard mg(x_GetCtxMtx()); m_ClientCharset = charset; m_ClientEncoding = eEncoding_Unknown; if (NStr::CompareNocase(charset, "UTF-8") == 0 || NStr::CompareNocase(charset, "UTF8") == 0) { m_ClientEncoding = eEncoding_UTF8; } else if (NStr::CompareNocase(charset, "Ascii") == 0) { m_ClientEncoding = eEncoding_Ascii; } else if (NStr::CompareNocase(charset, "ISO8859_1") == 0 || NStr::CompareNocase(charset, "ISO8859-1") == 0 ) { m_ClientEncoding = eEncoding_ISO8859_1; } else if (NStr::CompareNocase(charset, "Windows_1252") == 0 || NStr::CompareNocase(charset, "Windows-1252") == 0) { m_ClientEncoding = eEncoding_Windows_1252; } } void CDriverContext::UpdateConnTimeout(void) const { // Do not protect this method. It is already protected. ITERATE(TConnPool, it, m_NotInUse) { CConnection* t_con = *it; if (!t_con) continue; t_con->SetTimeout(GetTimeout()); } ITERATE(TConnPool, it, m_InUse) { CConnection* t_con = *it; if (!t_con) continue; t_con->SetTimeout(GetTimeout()); } } void CDriverContext::UpdateConnMaxBlobSize(void) const { // Do not protect this method. It is protected. ITERATE(TConnPool, it, m_NotInUse) { CConnection* t_con = *it; if (!t_con) continue; t_con->SetBlobSize(GetMaxBlobSize()); } ITERATE(TConnPool, it, m_InUse) { CConnection* t_con = *it; if (!t_con) continue; t_con->SetBlobSize(GetMaxBlobSize()); } } /////////////////////////////////////////////////////////////////////////// CWinSock::CWinSock(void) { #if defined(NCBI_OS_MSWIN) WSADATA wsaData; if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) { DATABASE_DRIVER_ERROR( "winsock initialization failed", 200001 ); } #endif } CWinSock::~CWinSock(void) { #if defined(NCBI_OS_MSWIN) WSACleanup(); #endif } } // namespace impl END_NCBI_SCOPE