1 /* $Id: dbapi_impl_context.cpp 630289 2021-04-29 18:22:17Z ivanov $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Sergey Sikorskiy
27 *
28 */
29
30 #include <ncbi_pch.hpp>
31
32 #include <dbapi/driver/dbapi_driver_conn_mgr.hpp>
33 #include <dbapi/driver/impl/dbapi_impl_context.hpp>
34 #include <dbapi/driver/impl/dbapi_impl_connection.hpp>
35 #include <dbapi/driver/dbapi_driver_conn_params.hpp>
36 #include <dbapi/error_codes.hpp>
37
38 #include <corelib/resource_info.hpp>
39 #include <corelib/ncbifile.hpp>
40
41 #include <algorithm>
42 #include <set>
43
44
45 #if defined(NCBI_OS_MSWIN)
46 # include <winsock2.h>
47 #endif
48
49
50 BEGIN_NCBI_SCOPE
51
52 #define NCBI_USE_ERRCODE_X Dbapi_DrvrContext
53
54
55 NCBI_PARAM_DEF_EX(bool, dbapi, conn_use_encrypt_data, false, eParam_NoThread, NULL);
56
57
58 namespace impl
59 {
60
61 inline
s_Matches(CConnection * conn,const string & pool_name,const string & server_name)62 static bool s_Matches(CConnection* conn, const string& pool_name,
63 const string& server_name)
64 {
65 if (pool_name.empty()) {
66 // There is no server name check here. We assume that a connection
67 // pool contains connections with appropriate server names only.
68 return conn->ServerName() == server_name
69 || conn->GetRequestedServer() == server_name;
70 } else {
71 return conn->PoolName() == pool_name;
72 }
73 }
74
75 ///////////////////////////////////////////////////////////////////////////
76 // CDriverContext::
77 //
78
CDriverContext(void)79 CDriverContext::CDriverContext(void) :
80 m_PoolSem(0, 1),
81 m_LoginTimeout(0),
82 m_Timeout(0),
83 m_CancelTimeout(0),
84 m_MaxBlobSize(0),
85 m_ClientEncoding(eEncoding_ISO8859_1)
86 {
87 PushCntxMsgHandler ( &CDB_UserHandler::GetDefault(), eTakeOwnership );
88 PushDefConnMsgHandler ( &CDB_UserHandler::GetDefault(), eTakeOwnership );
89 }
90
~CDriverContext(void)91 CDriverContext::~CDriverContext(void)
92 {
93 try {
94 DeleteAllConn();
95 } STD_CATCH_ALL("CDriverContext::DeleteAllConn");
96 }
97
98 void
SetApplicationName(const string & app_name)99 CDriverContext::SetApplicationName(const string& app_name)
100 {
101 CMutexGuard mg(x_GetCtxMtx());
102
103 m_AppName = app_name;
104 }
105
106 string
GetApplicationName(void) const107 CDriverContext::GetApplicationName(void) const
108 {
109 CMutexGuard mg(x_GetCtxMtx());
110
111 return m_AppName;
112 }
113
114 void
SetHostName(const string & host_name)115 CDriverContext::SetHostName(const string& host_name)
116 {
117 CMutexGuard mg(x_GetCtxMtx());
118
119 m_HostName = host_name;
120 }
121
122 string
GetHostName(void) const123 CDriverContext::GetHostName(void) const
124 {
125 CMutexGuard mg(x_GetCtxMtx());
126
127 return m_HostName;
128 }
129
GetLoginTimeout(void) const130 unsigned int CDriverContext::GetLoginTimeout(void) const
131 {
132 CMutexGuard mg(x_GetCtxMtx());
133
134 return m_LoginTimeout;
135 }
136
SetLoginTimeout(unsigned int nof_secs)137 bool CDriverContext::SetLoginTimeout (unsigned int nof_secs)
138 {
139 CMutexGuard mg(x_GetCtxMtx());
140
141 m_LoginTimeout = nof_secs;
142
143 return true;
144 }
145
GetTimeout(void) const146 unsigned int CDriverContext::GetTimeout(void) const
147 {
148 CMutexGuard mg(x_GetCtxMtx());
149
150 return m_Timeout;
151 }
152
SetTimeout(unsigned int nof_secs)153 bool CDriverContext::SetTimeout(unsigned int nof_secs)
154 {
155 bool success = true;
156 CMutexGuard mg(x_GetCtxMtx());
157
158 try {
159 m_Timeout = nof_secs;
160
161 // We do not have to update query/connection timeout in context
162 // any more. Each connection can be updated explicitly now.
163 // UpdateConnTimeout();
164 } catch (...) {
165 success = false;
166 }
167
168 return success;
169 }
170
GetCancelTimeout(void) const171 unsigned int CDriverContext::GetCancelTimeout(void) const
172 {
173 CMutexGuard mg(x_GetCtxMtx());
174
175 return m_CancelTimeout;
176 }
177
SetCancelTimeout(unsigned int nof_secs)178 bool CDriverContext::SetCancelTimeout(unsigned int nof_secs)
179 {
180 bool success = true;
181 CMutexGuard mg(x_GetCtxMtx());
182
183 try {
184 m_CancelTimeout = nof_secs;
185 } catch (...) {
186 success = false;
187 }
188
189 return success;
190 }
191
SetMaxBlobSize(size_t nof_bytes)192 bool CDriverContext::SetMaxBlobSize(size_t nof_bytes)
193 {
194 CMutexGuard mg(x_GetCtxMtx());
195
196 m_MaxBlobSize = nof_bytes;
197
198 UpdateConnMaxBlobSize();
199
200 return true;
201 }
202
PushCntxMsgHandler(CDB_UserHandler * h,EOwnership ownership)203 void CDriverContext::PushCntxMsgHandler(CDB_UserHandler* h,
204 EOwnership ownership)
205 {
206 CMutexGuard mg(x_GetCtxMtx());
207 m_CntxHandlers.Push(h, ownership);
208 }
209
PopCntxMsgHandler(CDB_UserHandler * h)210 void CDriverContext::PopCntxMsgHandler(CDB_UserHandler* h)
211 {
212 CMutexGuard mg(x_GetCtxMtx());
213 m_CntxHandlers.Pop(h);
214 }
215
PushDefConnMsgHandler(CDB_UserHandler * h,EOwnership ownership)216 void CDriverContext::PushDefConnMsgHandler(CDB_UserHandler* h,
217 EOwnership ownership)
218 {
219 CMutexGuard mg(x_GetCtxMtx());
220 m_ConnHandlers.Push(h, ownership);
221 }
222
PopDefConnMsgHandler(CDB_UserHandler * h)223 void CDriverContext::PopDefConnMsgHandler(CDB_UserHandler* h)
224 {
225 CMutexGuard mg(x_GetCtxMtx());
226 m_ConnHandlers.Pop(h);
227
228 // Remove this handler from all connections
229 TConnPool::value_type con = NULL;
230 ITERATE(TConnPool, it, m_NotInUse) {
231 con = *it;
232 con->PopMsgHandler(h);
233 }
234
235 ITERATE(TConnPool, it, m_InUse) {
236 con = *it;
237 con->PopMsgHandler(h);
238 }
239 }
240
241
ResetEnvSybase(void)242 void CDriverContext::ResetEnvSybase(void)
243 {
244 DEFINE_STATIC_MUTEX(env_mtx);
245
246 CMutexGuard mg(env_mtx);
247 CNcbiEnvironment env;
248
249 // If user forces his own Sybase client path using $RESET_SYBASE
250 // and $SYBASE -- use that unconditionally.
251 try {
252 if (env.Get("LC_ALL") == "POSIX") {
253 // Canonicalize, since locales.dat might list C but not POSIX.
254 env.Set("LC_ALL", "C");
255 }
256 if (!env.Get("SYBASE").empty()) {
257 string reset = env.Get("RESET_SYBASE");
258 if ( !reset.empty() && NStr::StringToBool(reset)) {
259 return;
260 }
261 }
262 // ...else try hardcoded paths
263 } catch (const CStringException&) {
264 // Conversion error -- try hardcoded paths too
265 }
266
267 // User-set or default hardcoded path
268 if ( CDir(NCBI_GetSybasePath()).CheckAccess(CDirEntry::fRead) ) {
269 env.Set("SYBASE", NCBI_GetSybasePath());
270 return;
271 }
272
273 // If NCBI_SetSybasePath() was used to set up the Sybase path, and it is
274 // not right, then use the very Sybase client against which the code was
275 // compiled
276 if ( !NStr::Equal(NCBI_GetSybasePath(), NCBI_GetDefaultSybasePath()) &&
277 CDir(NCBI_GetDefaultSybasePath()).CheckAccess(CDirEntry::fRead) ) {
278 env.Set("SYBASE", NCBI_GetDefaultSybasePath());
279 }
280
281 // Else, well, use whatever $SYBASE there is
282 }
283
284
x_Recycle(CConnection * conn,bool conn_reusable)285 void CDriverContext::x_Recycle(CConnection* conn, bool conn_reusable)
286 {
287 CMutexGuard mg(m_PoolMutex);
288
289 TConnPool::iterator it = find(m_InUse.begin(), m_InUse.end(), conn);
290
291 if (it != m_InUse.end()) {
292 m_InUse.erase(it);
293 }
294
295 #ifdef NCBI_THREADS
296 bool for_sem = false;
297 if ( !m_PoolSemSubject.empty()
298 && (m_PoolSemSubjectHasPoolName
299 ? conn->PoolName() == m_PoolSemSubject
300 : s_Matches(conn, kEmptyStr, m_PoolSemSubject))) {
301 for_sem = true;
302 m_PoolSemSubject.erase();
303 m_PoolSemConn = NULL;
304 m_PoolSem.TryWait(); // probably redundant, but ensures Post will work
305 m_PoolSem.Post();
306 }
307 #endif
308
309 bool keep = false;
310 if (conn_reusable && conn->IsOpeningFinished() && conn->IsValid()) {
311 keep = true;
312 // Tentatively; account for temporary overflow below, bearing
313 // in mind that that conn is already gone from m_InUse and not
314 // yet in m_NotInUse, and as such uncounted.
315 if (conn->m_PoolMaxSize <= m_InUse.size() + m_NotInUse.size()) {
316 unsigned int n;
317 if (conn->m_Pool.empty()) {
318 n = NofConnections(conn->m_RequestedServer);
319 } else {
320 n = NofConnections(kEmptyStr, conn->m_Pool);
321 }
322 if (n >= conn->m_PoolMaxSize) {
323 keep = false;
324 }
325 }
326
327 if (keep && conn->m_ReuseCount + 1 > conn->m_PoolMaxConnUse) {
328 // CXX-4420: limit the number of time the connection is reused
329 keep = false;
330 }
331 }
332
333 if (keep) {
334 if (conn->m_PoolIdleTimeParam.GetSign() != eNegative) {
335 CTime now(CTime::eCurrent);
336 conn->m_CleanupTime = now + conn->m_PoolIdleTimeParam;
337 }
338 m_NotInUse.push_back(conn);
339 #ifdef NCBI_THREADS
340 if (for_sem) {
341 m_PoolSemConn = conn;
342 }
343 #endif
344 } else {
345 x_AdjustCounts(conn, -1);
346 delete conn;
347 }
348
349 CloseOldIdleConns(1);
350 }
351
352
x_AdjustCounts(const CConnection * conn,int delta)353 void CDriverContext::x_AdjustCounts(const CConnection* conn, int delta)
354 {
355 if (delta != 0 && conn->IsReusable()) {
356 CMutexGuard mg(m_PoolMutex);
357 string server_name = conn->GetServerName();
358 if (conn->Host() != 0 && server_name.find('@') == NPOS) {
359 server_name += '@' + ConvertN2A(conn->Host());
360 if (conn->Port() != 0) {
361 server_name += ':' + NStr::NumericToString(conn->Port());
362 }
363 }
364 _DEBUG_ARG(unsigned int pool_count =)
365 m_CountsByPool[conn->PoolName()][server_name] += delta;
366 _DEBUG_ARG(unsigned int service_count =)
367 m_CountsByService[conn->GetRequestedServer()][server_name] += delta;
368 _TRACE(server_name << " count += " << delta << " for pool "
369 << conn->PoolName() << " (" << pool_count << ") and service "
370 << conn->GetRequestedServer() << " (" << service_count << ')');
371 }
372 }
373
374
x_GetCounts(const TCountsMap & main_map,const string & name,TCounts * counts) const375 void CDriverContext::x_GetCounts(const TCountsMap& main_map,
376 const string& name, TCounts* counts) const
377 {
378 CMutexGuard mg(m_PoolMutex);
379 auto it = main_map.find(name);
380 if (it == main_map.end()) {
381 counts->clear();
382 } else {
383 *counts = it->second;
384 }
385 }
386
387
CloseUnusedConnections(const string & srv_name,const string & pool_name,unsigned int max_closings)388 void CDriverContext::CloseUnusedConnections(const string& srv_name,
389 const string& pool_name,
390 unsigned int max_closings)
391 {
392 CMutexGuard mg(m_PoolMutex);
393
394 TConnPool::value_type con;
395
396 // close all connections first
397 ERASE_ITERATE(TConnPool, it, m_NotInUse) {
398 con = *it;
399
400 if ( !srv_name.empty() && srv_name != con->ServerName()
401 && srv_name != con->GetRequestedServer() )
402 continue;
403 if((!pool_name.empty()) && pool_name.compare(con->PoolName())) continue;
404
405 it = m_NotInUse.erase(it);
406 x_AdjustCounts(con, -1);
407 delete con;
408 if (--max_closings == 0) {
409 break;
410 }
411 }
412 }
413
NofConnections(const TSvrRef & svr_ref,const string & pool_name) const414 unsigned int CDriverContext::NofConnections(const TSvrRef& svr_ref,
415 const string& pool_name) const
416 {
417 CMutexGuard mg(m_PoolMutex);
418
419 if ((!svr_ref || !svr_ref->IsValid()) && pool_name.empty()) {
420 return static_cast<unsigned int>(m_InUse.size() + m_NotInUse.size());
421 }
422
423 string server;
424 Uint4 host = 0;
425 Uint2 port = 0;
426 if (svr_ref) {
427 host = svr_ref->GetHost();
428 port = svr_ref->GetPort();
429 if (host == 0)
430 server = svr_ref->GetName();
431 }
432
433 const TConnPool* pools[] = {&m_NotInUse, &m_InUse};
434 int n = 0;
435 for (size_t i = 0; i < ArraySize(pools); ++i) {
436 ITERATE(TConnPool, it, (*pools[i])) {
437 TConnPool::value_type con = *it;
438 if(!server.empty()) {
439 if (server != con->ServerName()
440 && server != con->GetRequestedServer())
441 continue;
442 }
443 else if (host != 0) {
444 if (host != con->Host() || port != con->Port())
445 continue;
446 }
447 if((!pool_name.empty()) && pool_name.compare(con->PoolName())) continue;
448 ++n;
449 }
450 }
451
452 return n;
453 }
454
NofConnections(const string & srv_name,const string & pool_name) const455 unsigned int CDriverContext::NofConnections(const string& srv_name,
456 const string& pool_name) const
457 {
458 TSvrRef svr_ref(new CDBServer(srv_name, 0, 0));
459 return NofConnections(svr_ref, pool_name);
460 }
461
MakeCDBConnection(CConnection * connection,int delta)462 CDB_Connection* CDriverContext::MakeCDBConnection(CConnection* connection,
463 int delta)
464 {
465 connection->m_CleanupTime.Clear();
466 CMutexGuard mg(m_PoolMutex);
467 m_InUse.push_back(connection);
468 x_AdjustCounts(connection, delta);
469
470 return new CDB_Connection(connection);
471 }
472
473 CDB_Connection*
MakePooledConnection(const CDBConnParams & params)474 CDriverContext::MakePooledConnection(const CDBConnParams& params)
475 {
476 if (params.GetParam("is_pooled") == "true") {
477 CMutexGuard mg(m_PoolMutex);
478
479 string pool_name (params.GetParam("pool_name")),
480 server_name(params.GetServerName());
481
482 ERASE_ITERATE(TConnPool, it, m_NotInUse) {
483 CConnection* t_con(*it);
484 if (s_Matches(t_con, pool_name, server_name)) {
485 it = m_NotInUse.erase(it);
486 CDB_Connection* dbcon = MakeCDBConnection(t_con, 0);
487 if (dbcon->Refresh()) {
488 /* Future development ...
489 if (!params.GetDatabaseName().empty()) {
490 return SetDatabase(dbcon, params);
491 } else {
492 return dbcon;
493 }
494 */
495
496 return dbcon;
497 }
498 else {
499 x_AdjustCounts(t_con, -1);
500 t_con->m_Reusable = false;
501 delete dbcon;
502 }
503 }
504 }
505
506 // Connection should be created, but we can have limit on number of
507 // connections in the pool.
508 string pool_max_str(params.GetParam("pool_maxsize"));
509 if (!pool_max_str.empty() && pool_max_str != "default") {
510 int pool_max = NStr::StringToInt(pool_max_str);
511 if (pool_max != 0) {
512 int total_cnt = 0;
513 ITERATE(TConnPool, it, m_InUse) {
514 CConnection* t_con(*it);
515 if (s_Matches(t_con, pool_name, server_name)) {
516 ++total_cnt;
517 }
518 }
519 if (total_cnt >= pool_max) {
520 #ifdef NCBI_THREADS
521 string timeout_str(params.GetParam("pool_wait_time"));
522 double timeout_val = 0.0;
523 if ( !timeout_str.empty() && timeout_str != "default") {
524 timeout_val = NStr::StringToDouble(timeout_str);
525 }
526 CTimeout timeout(timeout_val);
527 if (pool_name.empty()) {
528 m_PoolSemSubject = server_name;
529 m_PoolSemSubjectHasPoolName = false;
530 } else {
531 m_PoolSemSubject = pool_name;
532 m_PoolSemSubjectHasPoolName = true;
533 }
534 mg.Release();
535 if (m_PoolSem.TryWait(timeout)) {
536 mg.Guard(m_PoolMutex);
537 CConnection* t_con = NULL;
538 NON_CONST_REVERSE_ITERATE(TConnPool, it, m_NotInUse) {
539 if (*it == m_PoolSemConn) {
540 t_con = m_PoolSemConn;
541 m_PoolSemConn = NULL;
542 m_NotInUse.erase((++it).base());
543 break;
544 }
545 }
546 if (t_con != NULL) {
547 return MakeCDBConnection(t_con, 0);
548 }
549 } else
550 #endif
551 if (params.GetParam("pool_allow_temp_overflow")
552 != "true") {
553 string msg = FORMAT("Connection pool full (size "
554 << pool_max << ") for "
555 << (pool_name.empty() ? server_name
556 : pool_name));
557 DATABASE_DRIVER_ERROR(msg, 200011);
558 }
559 }
560 }
561 }
562 }
563
564 if (params.GetParam("do_not_connect") == "true") {
565 return NULL;
566 }
567
568 // Precondition check.
569 if (!TDbapi_CanUseKerberos::GetDefault()
570 && (params.GetUserName().empty()
571 || params.GetPassword().empty()))
572 {
573 string err_msg("Insufficient credentials to connect.");
574
575 if (params.GetUserName().empty()) {
576 err_msg += " User name has not been set.";
577 }
578 if (params.GetPassword().empty()) {
579 err_msg += " Password has not been set.";
580 }
581
582 DATABASE_DRIVER_ERROR( err_msg, 200010 );
583 }
584
585 CConnection* t_con = MakeIConnection(params);
586
587 return MakeCDBConnection(t_con, 1);
588 }
589
590 void
CloseAllConn(void)591 CDriverContext::CloseAllConn(void)
592 {
593 CMutexGuard mg(m_PoolMutex);
594 // close all connections first
595 ITERATE(TConnPool, it, m_NotInUse) {
596 x_AdjustCounts(*it, -1);
597 delete *it;
598 }
599 m_NotInUse.clear();
600
601 ITERATE(TConnPool, it, m_InUse) {
602 (*it)->Close();
603 }
604 }
605
606 void
DeleteAllConn(void)607 CDriverContext::DeleteAllConn(void)
608 {
609 CMutexGuard mg(m_PoolMutex);
610 // close all connections first
611 ITERATE(TConnPool, it, m_NotInUse) {
612 x_AdjustCounts(*it, -1);
613 delete *it;
614 }
615 m_NotInUse.clear();
616
617 ITERATE(TConnPool, it, m_InUse) {
618 x_AdjustCounts(*it, -1);
619 delete *it;
620 }
621 m_InUse.clear();
622 }
623
624
625 class CMakeConnActualParams: public CDBConnParamsBase
626 {
627 public:
CMakeConnActualParams(const CDBConnParams & other)628 CMakeConnActualParams(const CDBConnParams& other)
629 : m_Other(other)
630 {
631 // Override what is set in CDBConnParamsBase constructor
632 SetParam("secure_login", kEmptyStr);
633 SetParam("is_pooled", kEmptyStr);
634 SetParam("do_not_connect", kEmptyStr);
635 }
636
~CMakeConnActualParams(void)637 ~CMakeConnActualParams(void)
638 {}
639
GetProtocolVersion(void) const640 virtual Uint4 GetProtocolVersion(void) const
641 {
642 return m_Other.GetProtocolVersion();
643 }
644
GetEncoding(void) const645 virtual EEncoding GetEncoding(void) const
646 {
647 return m_Other.GetEncoding();
648 }
649
GetServerName(void) const650 virtual string GetServerName(void) const
651 {
652 return CDBConnParamsBase::GetServerName();
653 }
654
GetDatabaseName(void) const655 virtual string GetDatabaseName(void) const
656 {
657 return CDBConnParamsBase::GetDatabaseName();
658 }
659
GetUserName(void) const660 virtual string GetUserName(void) const
661 {
662 return CDBConnParamsBase::GetUserName();
663 }
664
GetPassword(void) const665 virtual string GetPassword(void) const
666 {
667 return CDBConnParamsBase::GetPassword();
668 }
669
GetServerType(void) const670 virtual EServerType GetServerType(void) const
671 {
672 return m_Other.GetServerType();
673 }
674
GetHost(void) const675 virtual Uint4 GetHost(void) const
676 {
677 return m_Other.GetHost();
678 }
679
GetPort(void) const680 virtual Uint2 GetPort(void) const
681 {
682 return m_Other.GetPort();
683 }
684
GetConnValidator(void) const685 virtual CRef<IConnValidator> GetConnValidator(void) const
686 {
687 return m_Other.GetConnValidator();
688 }
689
GetParam(const string & key) const690 virtual string GetParam(const string& key) const
691 {
692 string result(CDBConnParamsBase::GetParam(key));
693 if (result.empty())
694 return m_Other.GetParam(key);
695 else
696 return result;
697 }
698
699 using CDBConnParamsBase::SetServerName;
700 using CDBConnParamsBase::SetUserName;
701 using CDBConnParamsBase::SetDatabaseName;
702 using CDBConnParamsBase::SetPassword;
703 using CDBConnParamsBase::SetParam;
704
705 private:
706 const CDBConnParams& m_Other;
707 };
708
709
710 struct SLoginData
711 {
712 string server_name;
713 string user_name;
714 string db_name;
715 string password;
716
SLoginDataimpl::SLoginData717 SLoginData(const string& sn, const string& un,
718 const string& dn, const string& pass)
719 : server_name(sn), user_name(un), db_name(dn), password(pass)
720 {}
721
operator <impl::SLoginData722 bool operator< (const SLoginData& right) const
723 {
724 if (server_name != right.server_name)
725 return server_name < right.server_name;
726 else if (user_name != right.user_name)
727 return user_name < right.user_name;
728 else if (db_name != right.db_name)
729 return db_name < right.db_name;
730 else
731 return password < right.password;
732 }
733 };
734
735
736 static void
s_TransformLoginData(string & server_name,string & user_name,string & db_name,string & password)737 s_TransformLoginData(string& server_name, string& user_name,
738 string& db_name, string& password)
739 {
740 if (!TDbapi_ConnUseEncryptData::GetDefault())
741 return;
742
743 string app_name = CNcbiApplication::Instance()->GetProgramDisplayName();
744 set<SLoginData> visited;
745 CNcbiResourceInfoFile res_file(CNcbiResourceInfoFile::GetDefaultFileName());
746
747 visited.insert(SLoginData(server_name, user_name, db_name, password));
748 for (;;) {
749 string res_name = app_name;
750 if (!user_name.empty()) {
751 res_name += "/";
752 res_name += user_name;
753 }
754 if (!server_name.empty()) {
755 res_name += "@";
756 res_name += server_name;
757 }
758 if (!db_name.empty()) {
759 res_name += ":";
760 res_name += db_name;
761 }
762 const CNcbiResourceInfo& info
763 = res_file.GetResourceInfo(res_name, password);
764 if (!info)
765 break;
766
767 password = info.GetValue();
768 typedef CNcbiResourceInfo::TExtraValuesMap TExtraMap;
769 typedef TExtraMap::const_iterator TExtraMapIt;
770 const TExtraMap& extra = info.GetExtraValues().GetPairs();
771
772 TExtraMapIt it = extra.find("server");
773 if (it != extra.end())
774 server_name = it->second;
775 it = extra.find("username");
776 if (it != extra.end())
777 user_name = it->second;
778 it = extra.find("database");
779 if (it != extra.end())
780 db_name = it->second;
781
782 if (!visited.insert(
783 SLoginData(server_name, user_name, db_name, password)).second)
784 {
785 DATABASE_DRIVER_ERROR(
786 "Circular dependency inside resources info file.", 100012);
787 }
788 }
789 }
790
791
792 void
Clear(void)793 SDBConfParams::Clear(void)
794 {
795 flags = 0;
796 server.clear();
797 port.clear();
798 database.clear();
799 username.clear();
800 password.clear();
801 login_timeout.clear();
802 io_timeout.clear();
803 single_server.clear();
804 is_pooled.clear();
805 pool_name.clear();
806 pool_maxsize.clear();
807 pool_idle_time.clear();
808 pool_wait_time.clear();
809 pool_allow_temp_overflow.clear();
810 pool_max_conn_use.clear();
811 args.clear();
812 }
813
814 void
ReadDBConfParams(const string & service_name,SDBConfParams * params)815 CDriverContext::ReadDBConfParams(const string& service_name,
816 SDBConfParams* params)
817 {
818 params->Clear();
819 if (service_name.empty())
820 return;
821
822 CNcbiApplication* app = CNcbiApplication::Instance();
823 if (!app)
824 return;
825
826 const IRegistry& reg = app->GetConfig();
827 string section_name(service_name);
828 section_name.append(1, '.');
829 section_name.append("dbservice");
830 if (!reg.HasEntry(section_name, kEmptyStr))
831 return;
832
833 if (reg.HasEntry(section_name, "service", IRegistry::fCountCleared)) {
834 params->flags += SDBConfParams::fServerSet;
835 params->server = reg.Get(section_name, "service");
836 }
837 if (reg.HasEntry(section_name, "port", IRegistry::fCountCleared)) {
838 params->flags += SDBConfParams::fPortSet;
839 params->port = reg.Get(section_name, "port");
840 }
841 if (reg.HasEntry(section_name, "database", IRegistry::fCountCleared)) {
842 params->flags += SDBConfParams::fDatabaseSet;
843 params->database = reg.Get(section_name, "database");
844 }
845 if (reg.HasEntry(section_name, "username", IRegistry::fCountCleared)) {
846 params->flags += SDBConfParams::fUsernameSet;
847 params->username = reg.Get(section_name, "username");
848 }
849 if (reg.HasEntry(section_name, "password_key", IRegistry::fCountCleared)) {
850 params->flags += SDBConfParams::fPasswordKeySet;
851 params->password_key_id = reg.Get(section_name, "password_key");
852 }
853 if (reg.HasEntry(section_name, "password", IRegistry::fCountCleared)) {
854 params->flags += SDBConfParams::fPasswordSet;
855 params->password = reg.Get(section_name, "password");
856 if (CNcbiEncrypt::IsEncrypted(params->password)) {
857 try {
858 params->password = CNcbiEncrypt::Decrypt(params->password);
859 } NCBI_CATCH("Password decryption for " + service_name);
860 } else if (params->password_key_id.empty()) {
861 ERR_POST(Warning
862 << "Using unencrypted password for " + service_name);
863 }
864 }
865 if (reg.HasEntry(section_name, "password_file",
866 IRegistry::fCountCleared)) {
867 // password and password_file are mutually exclusive, but only SDBAPI
868 // actually honors the latter, so it will take care of throwing
869 // exceptions as necessary.
870 params->flags += SDBConfParams::fPasswordFileSet;
871 params->password_file = reg.Get(section_name, "password_file");
872 }
873 if (reg.HasEntry(section_name, "login_timeout", IRegistry::fCountCleared)) {
874 params->flags += SDBConfParams::fLoginTimeoutSet;
875 params->login_timeout = reg.Get(section_name, "login_timeout");
876 }
877 if (reg.HasEntry(section_name, "io_timeout", IRegistry::fCountCleared)) {
878 params->flags += SDBConfParams::fIOTimeoutSet;
879 params->io_timeout = reg.Get(section_name, "io_timeout");
880 }
881 if (reg.HasEntry(section_name, "cancel_timeout", IRegistry::fCountCleared)) {
882 params->flags += SDBConfParams::fCancelTimeoutSet;
883 params->cancel_timeout = reg.Get(section_name, "cancel_timeout");
884 }
885 if (reg.HasEntry(section_name, "exclusive_server", IRegistry::fCountCleared)) {
886 params->flags += SDBConfParams::fSingleServerSet;
887 params->single_server = reg.Get(section_name, "exclusive_server");
888 }
889 if (reg.HasEntry(section_name, "use_conn_pool", IRegistry::fCountCleared)) {
890 params->flags += SDBConfParams::fIsPooledSet;
891 params->is_pooled = reg.Get(section_name, "use_conn_pool");
892 params->pool_name = section_name;
893 params->pool_name.append(1, '.');
894 params->pool_name.append("pool");
895 }
896 if (reg.HasEntry(section_name, "conn_pool_name", IRegistry::fCountCleared)) {
897 // params->flags += SDBConfParams::fPoolNameSet;
898 params->pool_name = reg.Get(section_name, "conn_pool_name");
899 }
900 if (reg.HasEntry(section_name, "conn_pool_minsize", IRegistry::fCountCleared)) {
901 params->flags += SDBConfParams::fPoolMinSizeSet;
902 params->pool_minsize = reg.Get(section_name, "conn_pool_minsize");
903 }
904 if (reg.HasEntry(section_name, "conn_pool_maxsize", IRegistry::fCountCleared)) {
905 params->flags += SDBConfParams::fPoolMaxSizeSet;
906 params->pool_maxsize = reg.Get(section_name, "conn_pool_maxsize");
907 }
908 if (reg.HasEntry(section_name, "conn_pool_idle_time",
909 IRegistry::fCountCleared)) {
910 params->flags += SDBConfParams::fPoolIdleTimeSet;
911 params->pool_idle_time = reg.Get(section_name, "conn_pool_idle_time");
912 }
913 if (reg.HasEntry(section_name, "conn_pool_wait_time",
914 IRegistry::fCountCleared)) {
915 params->flags += SDBConfParams::fPoolWaitTimeSet;
916 params->pool_wait_time = reg.Get(section_name, "conn_pool_wait_time");
917 }
918 if (reg.HasEntry(section_name, "conn_pool_allow_temp_overflow",
919 IRegistry::fCountCleared)) {
920 params->flags += SDBConfParams::fPoolAllowTempSet;
921 params->pool_allow_temp_overflow
922 = reg.Get(section_name, "conn_pool_allow_temp_overflow");
923 }
924 if (reg.HasEntry(section_name, "continue_after_raiserror",
925 IRegistry::fCountCleared)) {
926 params->flags += SDBConfParams::fContRaiserrorSet;
927 params->continue_after_raiserror
928 = reg.Get(section_name, "continue_after_raiserror");
929 }
930 if (reg.HasEntry(section_name, "conn_pool_max_conn_use",
931 IRegistry::fCountCleared)) {
932 params->flags += SDBConfParams::fPoolMaxConnUseSet;
933 params->pool_max_conn_use
934 = reg.Get(section_name, "conn_pool_max_conn_use");
935 }
936 if (reg.HasEntry(section_name, "args", IRegistry::fCountCleared)) {
937 params->flags += SDBConfParams::fArgsSet;
938 params->args = reg.Get(section_name, "args");
939 }
940 }
941
942 bool
SatisfyPoolMinimum(const CDBConnParams & params)943 CDriverContext::SatisfyPoolMinimum(const CDBConnParams& params)
944 {
945 CMutexGuard mg(m_PoolMutex);
946
947 string pool_min_str = params.GetParam("pool_minsize");
948 if (pool_min_str.empty() || pool_min_str == "default")
949 return true;
950 int pool_min = NStr::StringToInt(pool_min_str);
951 if (pool_min <= 0)
952 return true;
953
954 string pool_name = params.GetParam("pool_name"),
955 server_name = params.GetServerName();
956 int total_cnt = 0;
957 ITERATE(TConnPool, it, m_InUse) {
958 CConnection* t_con(*it);
959 if (t_con->IsReusable() && s_Matches(t_con, pool_name, server_name)
960 && t_con->IsValid() && t_con->IsAlive())
961 {
962 ++total_cnt;
963 }
964 }
965 ITERATE(TConnPool, it, m_NotInUse) {
966 CConnection* t_con(*it);
967 if (t_con->IsReusable() && s_Matches(t_con, pool_name, server_name)
968 && t_con->IsAlive())
969 {
970 ++total_cnt;
971 }
972 }
973 mg.Release();
974 vector< AutoPtr<CDB_Connection> > conns(pool_min);
975 for (int i = total_cnt; i < pool_min; ++i) {
976 try {
977 conns.push_back(MakeConnection(params));
978 }
979 catch (CDB_Exception& ex) {
980 ERR_POST_X(1, "Error filling connection pool: " << ex);
981 return false;
982 }
983 }
984 return true;
985 }
986
987 CDB_Connection*
MakeConnection(const CDBConnParams & params)988 CDriverContext::MakeConnection(const CDBConnParams& params)
989 {
990 CMutexGuard mg(x_GetCtxMtx());
991
992 CMakeConnActualParams act_params(params);
993
994 SDBConfParams conf_params;
995 conf_params.Clear();
996 if (params.GetParam("do_not_read_conf") != "true") {
997 ReadDBConfParams(params.GetServerName(), &conf_params);
998 }
999
1000 int was_timeout = GetTimeout();
1001 int was_login_timeout = GetLoginTimeout();
1002 unique_ptr<CDB_Connection> t_con;
1003 try {
1004 string server_name = (conf_params.IsServerSet()? conf_params.server:
1005 params.GetServerName());
1006 string user_name = (conf_params.IsUsernameSet()? conf_params.username:
1007 params.GetUserName());
1008 string db_name = (conf_params.IsDatabaseSet()? conf_params.database:
1009 params.GetDatabaseName());
1010 string password = (conf_params.IsPasswordSet()? conf_params.password:
1011 params.GetPassword());
1012 if (conf_params.IsLoginTimeoutSet()) {
1013 if (conf_params.login_timeout.empty()) {
1014 SetLoginTimeout(0);
1015 }
1016 else {
1017 SetLoginTimeout(NStr::StringToInt(conf_params.login_timeout));
1018 }
1019 }
1020 else {
1021 string value(params.GetParam("login_timeout"));
1022 if (value == "default") {
1023 SetLoginTimeout(0);
1024 }
1025 else if (!value.empty()) {
1026 SetLoginTimeout(NStr::StringToInt(value));
1027 }
1028 }
1029 if (conf_params.IsIOTimeoutSet()) {
1030 if (conf_params.io_timeout.empty()) {
1031 SetTimeout(0);
1032 }
1033 else {
1034 SetTimeout(NStr::StringToInt(conf_params.io_timeout));
1035 }
1036 }
1037 else {
1038 string value(params.GetParam("io_timeout"));
1039 if (value == "default") {
1040 SetTimeout(0);
1041 }
1042 else if (!value.empty()) {
1043 SetTimeout(NStr::StringToInt(value));
1044 }
1045 }
1046 if (conf_params.IsCancelTimeoutSet()) {
1047 if (conf_params.cancel_timeout.empty()) {
1048 SetCancelTimeout(0);
1049 }
1050 else {
1051 SetCancelTimeout(NStr::StringToInt(conf_params.cancel_timeout));
1052 }
1053 }
1054 else {
1055 string value(params.GetParam("cancel_timeout"));
1056 if (value == "default") {
1057 SetCancelTimeout(10);
1058 }
1059 else if (!value.empty()) {
1060 SetCancelTimeout(NStr::StringToInt(value));
1061 }
1062 }
1063 if (conf_params.IsSingleServerSet()) {
1064 if (conf_params.single_server.empty()) {
1065 act_params.SetParam("single_server", "true");
1066 }
1067 else {
1068 act_params.SetParam("single_server",
1069 NStr::BoolToString(NStr::StringToBool(
1070 conf_params.single_server)));
1071 }
1072 }
1073 else if (params.GetParam("single_server") == "default") {
1074 act_params.SetParam("single_server", "true");
1075 }
1076 if (conf_params.IsPooledSet()) {
1077 if (conf_params.is_pooled.empty()) {
1078 act_params.SetParam("is_pooled", "false");
1079 }
1080 else {
1081 act_params.SetParam("is_pooled",
1082 NStr::BoolToString(NStr::StringToBool(
1083 conf_params.is_pooled)));
1084 act_params.SetParam("pool_name", conf_params.pool_name);
1085 }
1086 }
1087 else if (params.GetParam("is_pooled") == "default") {
1088 act_params.SetParam("is_pooled", "false");
1089 }
1090 if (conf_params.IsPoolMinSizeSet())
1091 act_params.SetParam("pool_minsize", conf_params.pool_minsize);
1092 else if (params.GetParam("pool_minsize") == "default") {
1093 act_params.SetParam("pool_minsize", "0");
1094 }
1095 if (conf_params.IsPoolMaxSizeSet())
1096 act_params.SetParam("pool_maxsize", conf_params.pool_maxsize);
1097 else if (params.GetParam("pool_maxsize") == "default") {
1098 act_params.SetParam("pool_maxsize", "");
1099 }
1100 if (conf_params.IsPoolIdleTimeSet())
1101 act_params.SetParam("pool_idle_time", conf_params.pool_idle_time);
1102 else if (params.GetParam("pool_idle_time") == "default") {
1103 act_params.SetParam("pool_idle_time", "");
1104 }
1105 if (conf_params.IsPoolWaitTimeSet())
1106 act_params.SetParam("pool_wait_time", conf_params.pool_wait_time);
1107 else if (params.GetParam("pool_wait_time") == "default") {
1108 act_params.SetParam("pool_wait_time", "0");
1109 }
1110 if (conf_params.IsPoolAllowTempOverflowSet()) {
1111 if (conf_params.pool_allow_temp_overflow.empty()) {
1112 act_params.SetParam("pool_allow_temp_overflow", "false");
1113 }
1114 else {
1115 act_params.SetParam
1116 ("pool_allow_temp_overflow",
1117 NStr::BoolToString(
1118 NStr::StringToBool(
1119 conf_params.pool_allow_temp_overflow)));
1120 }
1121 }
1122 else if (params.GetParam("pool_allow_temp_overflow") == "default") {
1123 act_params.SetParam("pool_allow_temp_overflow", "false");
1124 }
1125 if (conf_params.IsPoolMaxConnUseSet())
1126 act_params.SetParam("pool_max_conn_use",
1127 conf_params.pool_max_conn_use);
1128 else if (params.GetParam("pool_max_conn_use") == "default") {
1129 act_params.SetParam("pool_max_conn_use", "0");
1130 }
1131 if (conf_params.IsContinueAfterRaiserrorSet()) {
1132 if (conf_params.continue_after_raiserror.empty()) {
1133 act_params.SetParam("continue_after_raiserror", "false");
1134 }
1135 else {
1136 act_params.SetParam
1137 ("continue_after_raiserror",
1138 NStr::BoolToString(
1139 NStr::StringToBool(
1140 conf_params.continue_after_raiserror)));
1141 }
1142 }
1143 else if (params.GetParam("continue_after_raiserror") == "default") {
1144 act_params.SetParam("continue_after_raiserror", "false");
1145 }
1146
1147 s_TransformLoginData(server_name, user_name, db_name, password);
1148 act_params.SetServerName(server_name);
1149 act_params.SetUserName(user_name);
1150 act_params.SetDatabaseName(db_name);
1151 act_params.SetPassword(password);
1152
1153 CRef<IDBConnectionFactory> factory = CDbapiConnMgr::Instance().GetConnectionFactory();
1154 t_con.reset(factory->MakeDBConnection(*this, act_params));
1155
1156 if (t_con.get() == NULL) {
1157 if (act_params.GetParam("do_not_connect") == "true") {
1158 return NULL;
1159 }
1160
1161 string err;
1162 err += "Cannot connect to the server '" + act_params.GetServerName();
1163 err += "' as user '" + act_params.GetUserName() + "'";
1164
1165 CDB_ClientEx ex(DIAG_COMPILE_INFO, NULL, err, eDiag_Error, 100011);
1166 ex.SetRetriable(eRetriable_No);
1167 CDB_UserHandler::TExceptions* expts = factory->GetExceptions();
1168 if (expts) {
1169 NON_CONST_REVERSE_ITERATE(CDB_UserHandler::TExceptions, it, *expts) {
1170 ex.AddPrevious(*it);
1171 }
1172 }
1173 throw ex;
1174 }
1175
1176 // Set database ...
1177 t_con->SetDatabaseName(act_params.GetDatabaseName());
1178
1179 }
1180 catch (exception&) {
1181 SetTimeout(was_timeout);
1182 SetLoginTimeout(was_login_timeout);
1183 throw;
1184 }
1185 SetTimeout(was_timeout);
1186 SetLoginTimeout(was_login_timeout);
1187
1188 return t_con.release();
1189 }
1190
CloseConnsForPool(const string & pool_name,Uint4 keep_host_ip,Uint2 keep_port)1191 size_t CDriverContext::CloseConnsForPool(const string& pool_name,
1192 Uint4 keep_host_ip, Uint2 keep_port)
1193 {
1194 size_t invalidated_count = 0;
1195 CMutexGuard mg(m_PoolMutex);
1196
1197 ITERATE(TConnPool, it, m_InUse) {
1198 CConnection* t_con(*it);
1199 if (t_con->IsReusable() && pool_name == t_con->PoolName()) {
1200 if (t_con->Host() != keep_host_ip || t_con->Port() != keep_port) {
1201 t_con->Invalidate();
1202 ++invalidated_count;
1203 }
1204 }
1205 }
1206 ERASE_ITERATE(TConnPool, it, m_NotInUse) {
1207 CConnection* t_con(*it);
1208 if (t_con->IsReusable() && pool_name == t_con->PoolName()) {
1209 if (t_con->Host() != keep_host_ip || t_con->Port() != keep_port) {
1210 m_NotInUse.erase(it);
1211 x_AdjustCounts(t_con, -1);
1212 delete t_con;
1213 }
1214 }
1215 }
1216 return invalidated_count;
1217 }
1218
1219
CloseOldIdleConns(unsigned int max_closings,const string & pool_name)1220 void CDriverContext::CloseOldIdleConns(unsigned int max_closings,
1221 const string& pool_name)
1222 {
1223 CMutexGuard mg(m_PoolMutex);
1224 if (max_closings == 0) {
1225 return;
1226 }
1227
1228 set<string> at_min_by_pool, at_min_by_server;
1229 CTime now(CTime::eCurrent);
1230 ERASE_ITERATE (TConnPool, it, m_NotInUse) {
1231 const string& pool_name_2 = (*it)->PoolName();
1232 set<string>& at_min
1233 = pool_name_2.empty() ? at_min_by_server : at_min_by_pool;
1234 if (pool_name_2.empty()) {
1235 if ( !pool_name.empty()
1236 || at_min.find((*it)->GetRequestedServer()) != at_min.end()) {
1237 continue;
1238 }
1239 } else if (( !pool_name.empty() && pool_name != pool_name_2)
1240 || at_min.find(pool_name_2) != at_min.end()) {
1241 continue;
1242 }
1243 if ((*it)->m_CleanupTime.IsEmpty() || (*it)->m_CleanupTime > now) {
1244 continue;
1245 }
1246 unsigned int n;
1247 if (pool_name_2.empty()) {
1248 n = NofConnections((*it)->GetRequestedServer());
1249 } else {
1250 n = NofConnections(TSvrRef(), pool_name_2);
1251 }
1252 if (n > (*it)->m_PoolMinSize) {
1253 x_AdjustCounts(*it, -1);
1254 delete *it;
1255 m_NotInUse.erase(it);
1256 if (--max_closings == 0) {
1257 break;
1258 }
1259 } else {
1260 at_min.insert(pool_name_2);
1261 }
1262 }
1263 }
1264
1265
DestroyConnImpl(CConnection * impl)1266 void CDriverContext::DestroyConnImpl(CConnection* impl)
1267 {
1268 if (impl) {
1269 impl->ReleaseInterface();
1270 x_Recycle(impl, impl->IsReusable());
1271 }
1272 }
1273
SetClientCharset(const string & charset)1274 void CDriverContext::SetClientCharset(const string& charset)
1275 {
1276 CMutexGuard mg(x_GetCtxMtx());
1277
1278 m_ClientCharset = charset;
1279 m_ClientEncoding = eEncoding_Unknown;
1280
1281 if (NStr::CompareNocase(charset, "UTF-8") == 0 ||
1282 NStr::CompareNocase(charset, "UTF8") == 0) {
1283 m_ClientEncoding = eEncoding_UTF8;
1284 } else if (NStr::CompareNocase(charset, "Ascii") == 0) {
1285 m_ClientEncoding = eEncoding_Ascii;
1286 } else if (NStr::CompareNocase(charset, "ISO8859_1") == 0 ||
1287 NStr::CompareNocase(charset, "ISO8859-1") == 0
1288 ) {
1289 m_ClientEncoding = eEncoding_ISO8859_1;
1290 } else if (NStr::CompareNocase(charset, "Windows_1252") == 0 ||
1291 NStr::CompareNocase(charset, "Windows-1252") == 0) {
1292 m_ClientEncoding = eEncoding_Windows_1252;
1293 }
1294 }
1295
UpdateConnTimeout(void) const1296 void CDriverContext::UpdateConnTimeout(void) const
1297 {
1298 // Do not protect this method. It is already protected.
1299
1300 ITERATE(TConnPool, it, m_NotInUse) {
1301 CConnection* t_con = *it;
1302 if (!t_con) continue;
1303
1304 t_con->SetTimeout(GetTimeout());
1305 }
1306
1307 ITERATE(TConnPool, it, m_InUse) {
1308 CConnection* t_con = *it;
1309 if (!t_con) continue;
1310
1311 t_con->SetTimeout(GetTimeout());
1312 }
1313 }
1314
1315
UpdateConnMaxBlobSize(void) const1316 void CDriverContext::UpdateConnMaxBlobSize(void) const
1317 {
1318 // Do not protect this method. It is protected.
1319
1320 ITERATE(TConnPool, it, m_NotInUse) {
1321 CConnection* t_con = *it;
1322 if (!t_con) continue;
1323
1324 t_con->SetBlobSize(GetMaxBlobSize());
1325 }
1326
1327 ITERATE(TConnPool, it, m_InUse) {
1328 CConnection* t_con = *it;
1329 if (!t_con) continue;
1330
1331 t_con->SetBlobSize(GetMaxBlobSize());
1332 }
1333 }
1334
1335
1336 ///////////////////////////////////////////////////////////////////////////
CWinSock(void)1337 CWinSock::CWinSock(void)
1338 {
1339 #if defined(NCBI_OS_MSWIN)
1340 WSADATA wsaData;
1341 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
1342 {
1343 DATABASE_DRIVER_ERROR( "winsock initialization failed", 200001 );
1344 }
1345 #endif
1346 }
1347
~CWinSock(void)1348 CWinSock::~CWinSock(void)
1349 {
1350 #if defined(NCBI_OS_MSWIN)
1351 WSACleanup();
1352 #endif
1353 }
1354
1355 } // namespace impl
1356
1357 END_NCBI_SCOPE
1358
1359
1360