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