1 /*  $Id: dbapi_conn_factory.cpp 630683 2021-05-06 15:49:27Z 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 * Authors:  Sergey Sikorskiy, Aaron Ucko
27 *
28 */
29 
30 #include <ncbi_pch.hpp>
31 
32 #include <dbapi/driver/dbapi_conn_factory.hpp>
33 #include <dbapi/driver/dbapi_svc_mapper.hpp>
34 #include <dbapi/driver/dbapi_driver_conn_params.hpp>
35 #include <dbapi/driver/impl/dbapi_driver_utils.hpp>
36 #include <dbapi/driver/impl/dbapi_impl_connection.hpp>
37 #include <dbapi/driver/impl/dbapi_impl_context.hpp>
38 #include <dbapi/driver/impl/dbapi_pool_balancer.hpp>
39 #include <dbapi/driver/public.hpp>
40 #include <dbapi/error_codes.hpp>
41 #include <corelib/ncbiapp.hpp>
42 #include <corelib/request_ctx.hpp>
43 
44 #include <list>
45 
46 
47 #define NCBI_USE_ERRCODE_X   Dbapi_ConnFactory
48 
49 BEGIN_NCBI_SCOPE
50 
51 
52 ///////////////////////////////////////////////////////////////////////////////
CDBConnectionFactory(IDBServiceMapper::TFactory svc_mapper_factory,const IRegistry * registry,EDefaultMapping def_mapping)53 CDBConnectionFactory::CDBConnectionFactory(IDBServiceMapper::TFactory svc_mapper_factory,
54                                            const IRegistry* registry,
55                                            EDefaultMapping def_mapping) :
56 m_MapperFactory(svc_mapper_factory, registry, def_mapping),
57 m_MaxNumOfConnAttempts(1),
58 m_MaxNumOfValidationAttempts(1),
59 m_MaxNumOfServerAlternatives(32),
60 m_MaxNumOfDispatches(0),
61 m_ConnectionTimeout(0),
62 m_LoginTimeout(0),
63 m_TryServerToo(false)
64 {
65     ConfigureFromRegistry(registry);
66 }
67 
~CDBConnectionFactory(void)68 CDBConnectionFactory::~CDBConnectionFactory(void)
69 {
70     CDB_UserHandler::ClearExceptions(m_Errors);
71 }
72 
73 void
ConfigureFromRegistry(const IRegistry * registry)74 CDBConnectionFactory::ConfigureFromRegistry(const IRegistry* registry)
75 {
76     const string section_name("DB_CONNECTION_FACTORY");
77 
78     // Get current registry ...
79     if (!registry && CNcbiApplication::Instance()) {
80         registry = &CNcbiApplication::Instance()->GetConfig();
81     }
82 
83     if (registry) {
84         m_MaxNumOfConnAttempts =
85             registry->GetInt(section_name, "MAX_CONN_ATTEMPTS", 1);
86         m_MaxNumOfValidationAttempts =
87             registry->GetInt(section_name, "MAX_VALIDATION_ATTEMPTS", 1);
88         m_MaxNumOfServerAlternatives =
89             registry->GetInt(section_name, "MAX_SERVER_ALTERNATIVES", 32);
90         m_MaxNumOfDispatches =
91             registry->GetInt(section_name, "MAX_DISPATCHES", 0);
92         m_ConnectionTimeout =
93             registry->GetInt(section_name, "CONNECTION_TIMEOUT", 0);
94         m_LoginTimeout =
95             registry->GetInt(section_name, "LOGIN_TIMEOUT", 0);
96         m_TryServerToo =
97             registry->GetBool(section_name, "TRY_SERVER_AFTER_SERVICE", false);
98     } else {
99         m_MaxNumOfConnAttempts = 1;
100         m_MaxNumOfValidationAttempts = 1;
101         m_MaxNumOfServerAlternatives = 32;
102         m_MaxNumOfDispatches = 0;
103         m_ConnectionTimeout = 0;
104         m_LoginTimeout = 0;
105         m_TryServerToo = false;
106     }
107 }
108 
109 void
Configure(const IRegistry * registry)110 CDBConnectionFactory::Configure(const IRegistry* registry)
111 {
112     CFastMutexGuard mg(m_Mtx);
113 
114     ConfigureFromRegistry(registry);
115 }
116 
117 void
SetMaxNumOfConnAttempts(unsigned int max_num)118 CDBConnectionFactory::SetMaxNumOfConnAttempts(unsigned int max_num)
119 {
120     CFastMutexGuard mg(m_Mtx);
121 
122     m_MaxNumOfConnAttempts = max_num;
123 }
124 
125 void
SetMaxNumOfValidationAttempts(unsigned int max_num)126 CDBConnectionFactory::SetMaxNumOfValidationAttempts(unsigned int max_num)
127 {
128     CFastMutexGuard mg(m_Mtx);
129 
130     m_MaxNumOfValidationAttempts = max_num;
131 }
132 
133 void
SetMaxNumOfServerAlternatives(unsigned int max_num)134 CDBConnectionFactory::SetMaxNumOfServerAlternatives(unsigned int max_num)
135 {
136     CFastMutexGuard mg(m_Mtx);
137 
138     m_MaxNumOfServerAlternatives = max_num;
139 }
140 
141 void
SetMaxNumOfDispatches(unsigned int max_num)142 CDBConnectionFactory::SetMaxNumOfDispatches(unsigned int max_num)
143 {
144     CFastMutexGuard mg(m_Mtx);
145 
146     m_MaxNumOfDispatches = max_num;
147 }
148 
149 void
SetConnectionTimeout(unsigned int timeout)150 CDBConnectionFactory::SetConnectionTimeout(unsigned int timeout)
151 {
152     CFastMutexGuard mg(m_Mtx);
153 
154     m_ConnectionTimeout = timeout;
155 }
156 
157 void
SetLoginTimeout(unsigned int timeout)158 CDBConnectionFactory::SetLoginTimeout(unsigned int timeout)
159 {
160     CFastMutexGuard mg(m_Mtx);
161 
162     m_LoginTimeout = timeout;
163 }
164 
165 unsigned int
CalculateConnectionTimeout(const I_DriverContext & ctx) const166 CDBConnectionFactory::CalculateConnectionTimeout
167 (const I_DriverContext& ctx) const
168 {
169     unsigned int timeout = 30;
170 
171     unsigned int to = GetConnectionTimeout();
172     if (to != 0) {
173         timeout = to;
174     }
175     else {
176         to = ctx.GetTimeout();
177         if (to != 0) {
178             timeout = to;
179         }
180     }
181 
182     return timeout;
183 }
184 
185 unsigned int
CalculateLoginTimeout(const I_DriverContext & ctx) const186 CDBConnectionFactory::CalculateLoginTimeout(const I_DriverContext& ctx) const
187 {
188     unsigned int timeout = 30;
189 
190     unsigned int to = GetLoginTimeout();
191     if (to != 0) {
192         timeout = to;
193     }
194     else {
195         to = ctx.GetLoginTimeout();
196         if (to != 0) {
197             timeout = to;
198         }
199     }
200 
201     return timeout;
202 }
203 
204 CDBConnectionFactory::CRuntimeData&
GetRuntimeData(const CRef<IConnValidator> validator)205 CDBConnectionFactory::GetRuntimeData(const CRef<IConnValidator> validator)
206 {
207     string validator_name;
208     if (validator) {
209         validator_name = validator->GetName();
210     }
211     return GetRuntimeData(validator_name);
212 }
213 
214 CDBConnectionFactory::CRuntimeData&
GetRuntimeData(const string & validator_name)215 CDBConnectionFactory::GetRuntimeData(const string& validator_name)
216 {
217     TValidatorSet::iterator it = m_ValidatorSet.find(validator_name);
218     if (it != m_ValidatorSet.end()) {
219         return it->second;
220     }
221 
222     return m_ValidatorSet.insert(TValidatorSet::value_type(
223         validator_name,
224         CRuntimeData(*this, CRef<IDBServiceMapper>(m_MapperFactory.Make()))
225         )).first->second;
226 }
227 
228 
229 ///////////////////////////////////////////////////////////////////////////////
230 class CDB_DBLB_Delegate : public CDBConnParamsDelegate
231 {
232 public:
233     CDB_DBLB_Delegate(
234             const string& srv_name,
235             Uint4 host,
236             Uint2 port,
237             const CDBConnParams& other);
238     virtual ~CDB_DBLB_Delegate(void);
239 
240 public:
241     virtual string GetServerName(void) const;
242     virtual Uint4 GetHost(void) const;
243     virtual Uint2  GetPort(void) const;
244     virtual const impl::CDBHandlerStack& GetOpeningMsgHandlers(void) const;
245     impl::CDBHandlerStack& SetOpeningMsgHandlers(void);
246 
247 private:
248     // Non-copyable.
249     CDB_DBLB_Delegate(const CDB_DBLB_Delegate& other);
250     CDB_DBLB_Delegate& operator =(const CDB_DBLB_Delegate& other);
251 
252 private:
253     const string m_ServerName;
254     const Uint4  m_Host;
255     const Uint2  m_Port;
256     impl::CDBHandlerStack m_OpeningMsgHandlers;
257 };
258 
259 
CDB_DBLB_Delegate(const string & srv_name,Uint4 host,Uint2 port,const CDBConnParams & other)260 CDB_DBLB_Delegate::CDB_DBLB_Delegate(
261         const string& srv_name,
262         Uint4 host,
263         Uint2 port,
264         const CDBConnParams& other)
265 : CDBConnParamsDelegate(other)
266 , m_ServerName(srv_name)
267 , m_Host(host)
268 , m_Port(port)
269 {
270 }
271 
~CDB_DBLB_Delegate(void)272 CDB_DBLB_Delegate::~CDB_DBLB_Delegate(void)
273 {
274 }
275 
276 
GetServerName(void) const277 string CDB_DBLB_Delegate::GetServerName(void) const
278 {
279     return m_ServerName;
280 }
281 
282 
GetHost(void) const283 Uint4 CDB_DBLB_Delegate::GetHost(void) const
284 {
285     return m_Host;
286 }
287 
288 
GetPort(void) const289 Uint2 CDB_DBLB_Delegate::GetPort(void) const
290 {
291     return m_Port;
292 }
293 
GetOpeningMsgHandlers(void) const294 const impl::CDBHandlerStack& CDB_DBLB_Delegate::GetOpeningMsgHandlers(void)
295     const
296 {
297     return m_OpeningMsgHandlers;
298 }
299 
SetOpeningMsgHandlers(void)300 impl::CDBHandlerStack& CDB_DBLB_Delegate::SetOpeningMsgHandlers(void)
301 {
302     return m_OpeningMsgHandlers;
303 }
304 
305 ///////////////////////////////////////////////////////////////////////////////
SOpeningContext(I_DriverContext & driver_ctx_in)306 CDBConnectionFactory::SOpeningContext::SOpeningContext
307 (I_DriverContext& driver_ctx_in)
308     : driver_ctx(driver_ctx_in), conn_status(IConnValidator::eInvalidConn)
309 {
310 }
311 
312 CDB_Connection*
MakeDBConnection(I_DriverContext & ctx,const CDBConnParams & params)313 CDBConnectionFactory::MakeDBConnection(
314     I_DriverContext& ctx,
315     const CDBConnParams& params)
316 {
317     CFastMutexGuard mg(m_Mtx);
318 
319     CDB_UserHandler::ClearExceptions(m_Errors);
320 
321     unique_ptr<CDB_Connection> t_con;
322     CRuntimeData& rt_data = GetRuntimeData(params.GetConnValidator());
323     TSvrRef dsp_srv = rt_data.GetDispatchedServer(params.GetServerName());
324 
325     // Prepare to collect messages, whose proper severity depends on whether
326     // ANY attempt succeeds.
327     impl::CDBHandlerStack ultimate_handlers;
328     {{
329         const impl::CDriverContext* ctx_impl
330             = dynamic_cast<impl::CDriverContext*>(&ctx);
331         if (params.GetOpeningMsgHandlers().GetSize() > 0) {
332             ultimate_handlers = params.GetOpeningMsgHandlers();
333         } else if (ctx_impl != NULL) {
334             ultimate_handlers = ctx_impl->GetCtxHandlerStack();
335         } else {
336             ultimate_handlers.Push(&CDB_UserHandler::GetDefault());
337         }
338     }}
339     SOpeningContext opening_ctx(ctx);
340     CRef<CDB_UserHandler_Deferred> handler
341         (new CDB_UserHandler_Deferred(ultimate_handlers));
342     opening_ctx.handlers.Push(&*handler, eTakeOwnership);
343 
344     // Store original query timeout ...
345     unsigned int query_timeout = ctx.GetTimeout();
346 
347     // Set "validation timeouts" ...
348     ctx.SetTimeout(CalculateConnectionTimeout(ctx));
349     ctx.SetLoginTimeout(CalculateLoginTimeout(ctx));
350 
351     if ( dsp_srv.Empty() ) {
352         // We are here either because server name was never dispatched or
353         // because a named connection pool has been used before.
354         // Dispatch server name ...
355 
356         t_con.reset(DispatchServerName(opening_ctx, params));
357     } else {
358         // Server name is already dispatched ...
359         string single_server(params.GetParam("single_server"));
360         string is_pooled(params.GetParam("is_pooled"));
361 
362         // We probably need to re-dispatch it ...
363         if (single_server != "true"
364             &&  ((GetMaxNumOfDispatches()
365                   &&  (rt_data.GetNumOfDispatches(params.GetServerName())
366                        >= GetMaxNumOfDispatches()))
367                  || (dsp_srv->GetExpireTime() != 0
368                      &&  (CurrentTime(CTime::eUTC).GetTimeT()
369                           > dsp_srv->GetExpireTime())))) {
370             // We definitely need to re-dispatch it ...
371 
372             // Clean previous info ...
373             rt_data.CleanExcluded(params.GetServerName());
374             rt_data.SetDispatchedServer(params.GetServerName(), TSvrRef());
375             if (is_pooled == "true") {
376                 rt_data.GetServerOptions(params.GetServerName()).clear();
377             }
378             t_con.reset(DispatchServerName(opening_ctx, params));
379         } else if (single_server != "true"  &&  is_pooled == "true") {
380             // Don't fully redispatch, but keep the pool balanced
381             t_con.reset(DispatchServerName(opening_ctx, params));
382         } else {
383             // We do not need to re-dispatch it ...
384 
385             // Try to connect.
386             try {
387                 opening_ctx.last_tried.Reset(dsp_srv); // provisionally
388                 // I_DriverContext::SConnAttr cur_conn_attr(conn_attr);
389                 // cur_conn_attr.srv_name = dsp_srv->GetName();
390                 CDB_DBLB_Delegate cur_params(
391                         dsp_srv->GetName(),
392                         dsp_srv->GetHost(),
393                         dsp_srv->GetPort(),
394                         params);
395                 cur_params.SetOpeningMsgHandlers() = opening_ctx.handlers;
396 
397                 // MakeValidConnection may return NULL here because a newly
398                 // created connection may not pass validation.
399                 t_con.reset(MakeValidConnection(opening_ctx,
400                                                 // cur_conn_attr,
401                                                 cur_params));
402 
403             } catch (CDB_Exception& ex) {
404                 // m_Errors.push_back(ex.Clone());
405                 opening_ctx.handlers.PostMsg(&ex);
406                 if (params.GetConnValidator()) {
407                     opening_ctx.conn_status
408                         = params.GetConnValidator()->ValidateException(ex);
409                 }
410             }
411 
412             if (t_con.get() == NULL) {
413                 // We couldn't connect ...
414                 if (single_server != "true") {
415                     // Server might be temporarily unavailable ...
416                     // Check conn_status ...
417                     if (opening_ctx.conn_status
418                         == IConnValidator::eTempInvalidConn) {
419                         rt_data.IncNumOfValidationFailures
420                             (params.GetServerName(), opening_ctx.last_tried);
421                     }
422 
423                     // Re-dispatch ...
424                     t_con.reset(DispatchServerName(opening_ctx, params));
425                 }
426             } else {
427                 // Dispatched server is already set, but calling of this method
428                 // will increase number of successful dispatches.
429                 rt_data.SetDispatchedServer(params.GetServerName(),
430                                             opening_ctx.last_tried);
431             }
432         }
433     }
434 
435     // Restore original connection timeout ...
436     ctx.SetTimeout(query_timeout);
437 
438     // Restore original query timeout ...
439     if (t_con.get() != NULL) {
440         t_con->SetTimeout(query_timeout);
441     }
442 
443     x_LogConnection(opening_ctx, t_con.get(), params);
444     handler->Flush((t_con.get() == NULL) ? eDiagSevMax : eDiag_Warning);
445 
446     return t_con.release();
447 }
448 
449 CDB_Connection*
DispatchServerName(SOpeningContext & ctx,const CDBConnParams & params)450 CDBConnectionFactory::DispatchServerName(
451     SOpeningContext& ctx,
452     const CDBConnParams& params)
453 {
454     CStopWatchScope timing(ctx.dispatch_server_name_sw);
455     CDB_Connection* t_con = NULL;
456     // I_DriverContext::SConnAttr curr_conn_attr(conn_attr);
457     const string service_name(params.GetServerName());
458     bool do_not_dispatch = params.GetParam("do_not_dispatch") == "true";
459     string cur_srv_name;
460     Uint4 cur_host = 0;
461     Uint2  cur_port = 0;
462 
463     CRuntimeData& rt_data = GetRuntimeData(params.GetConnValidator());
464 
465     // Try to connect up to a given number of alternative servers ...
466     unsigned int alternatives = GetMaxNumOfServerAlternatives();
467     list<TSvrRef> tried_servers;
468     bool full_retry_made = false;
469     CRef<CDBPoolBalancer> balancer;
470 
471     if ( !do_not_dispatch  &&  params.GetParam("is_pooled") == "true"
472         &&  !service_name.empty()  ) {
473         balancer.Reset(new CDBPoolBalancer
474                        (service_name, params.GetParam("pool_name"),
475                         rt_data.GetServerOptions(service_name),
476                         &ctx.driver_ctx));
477     }
478     for ( ; !t_con && alternatives > 0; --alternatives ) {
479         TSvrRef dsp_srv;
480 
481         if (do_not_dispatch) {
482             cur_srv_name = params.GetServerName();
483             cur_host = params.GetHost();
484             cur_port = params.GetPort();
485         }
486         // It is possible that a server name won't be provided.
487         // This is possible when somebody uses a named connection pool.
488         // In this case we even won't try to map it.
489         else if (!service_name.empty()) {
490             if (balancer.NotEmpty()) {
491                 dsp_srv = balancer->GetServer(&t_con, &params);
492             }
493             if (dsp_srv.Empty()) {
494                 dsp_srv = rt_data.GetDispatchedServer(service_name);
495             }
496             if (dsp_srv.Empty()) {
497                 dsp_srv = rt_data.GetDBServiceMapper().GetServer(service_name);
498             }
499 
500             if (tried_servers.empty()
501                 &&  (dsp_srv.Empty()
502                      ||  (dsp_srv->GetName() == service_name
503                           &&  dsp_srv->GetHost() == 0
504                           &&  dsp_srv->GetPort() == 0
505                           &&  !rt_data.GetExcluded(service_name).empty() ))) {
506                 _TRACE("List of servers for service " << service_name
507                        << " is exhausted. Giving excluded a try.");
508                 rt_data.CleanExcluded(service_name);
509                 dsp_srv = rt_data.GetDBServiceMapper().GetServer(service_name);
510             }
511 
512             if (dsp_srv.Empty()) {
513                 m_Errors.push_back(new CDB_Exception(DIAG_COMPILE_INFO, NULL, CDB_Exception::EErrCode(0),
514                                    "Service mapper didn't return any server for " + service_name, eDiag_Error, 0));
515                 return NULL;
516             }
517             if (dsp_srv->GetName() == service_name
518                 &&  dsp_srv->GetHost() == 0  &&  dsp_srv->GetPort() == 0
519                 &&  !tried_servers.empty())
520             {
521                 if (full_retry_made) {
522                     if (!m_TryServerToo) {
523                         m_Errors.push_back(new CDB_Exception(DIAG_COMPILE_INFO, NULL, CDB_Exception::EErrCode(0),
524                                            "No more servers to try (didn't try " + service_name + " as server name)", eDiag_Error, 0));
525                         return NULL;
526                     }
527                 }
528                 else {
529                     _TRACE("List of servers for service " << service_name
530                            << " is exhausted. Giving excluded a try.");
531 
532                     rt_data.CleanExcluded(service_name);
533                     ITERATE(list<TSvrRef>, it, tried_servers) {
534                         rt_data.Exclude(service_name, *it);
535                     }
536                     if (balancer.NotEmpty()) {
537                         balancer.Reset
538                             (new CDBPoolBalancer
539                              (service_name, params.GetParam("pool_name"),
540                               rt_data.GetServerOptions(service_name, true),
541                               &ctx.driver_ctx));
542                     }
543                     full_retry_made = true;
544                     continue;
545                 }
546             }
547 
548             // If connection attempts will take too long mapper can return
549             // the same server once more. Let's not try to connect to it
550             // again.
551             bool found = false;
552             ITERATE(list<TSvrRef>, it, tried_servers) {
553                 if (**it == *dsp_srv) {
554                     rt_data.Exclude(service_name, dsp_srv);
555                     found = true;
556                     break;
557                 }
558             }
559             if (found)
560                 continue;
561 
562             // curr_conn_attr.srv_name = dsp_srv->GetName();
563             cur_srv_name = dsp_srv->GetName();
564             cur_host = dsp_srv->GetHost();
565             cur_port = dsp_srv->GetPort();
566         } else if (params.GetParam("pool_name").empty()) {
567             DATABASE_DRIVER_ERROR
568                 ("Neither server name nor pool name provided.", 111000);
569         } else {
570             // Old-fashioned connection pool.
571             // We do not change anything ...
572             cur_srv_name = params.GetServerName();
573             cur_host = params.GetHost();
574             cur_port = params.GetPort();
575         }
576 
577         // Try to connect up to a given number of attempts ...
578         unsigned int attempts = GetMaxNumOfConnAttempts();
579         bool need_exclude = true;
580         ctx.conn_status = IConnValidator::eInvalidConn;
581 
582         // We don't check value of conn_status inside of a loop below by design.
583         for (; attempts > 0; --attempts) {
584             try {
585                 CDB_DBLB_Delegate cur_params(
586                         cur_srv_name,
587                         cur_host,
588                         cur_port,
589                         params);
590                 cur_params.SetOpeningMsgHandlers() = ctx.handlers;
591                 t_con = MakeValidConnection(ctx,
592                                             // curr_conn_attr,
593                                             cur_params,
594                                             t_con);
595                 if (t_con != NULL) {
596                     need_exclude = false;
597                     break;
598                 }
599             } catch (CDB_Exception& ex) {
600                 // m_Errors.push_back(ex.Clone());
601                 ctx.handlers.PostMsg(&ex);
602                 if (params.GetConnValidator()) {
603                     ctx.conn_status
604                         = params.GetConnValidator()->ValidateException(ex);
605                 }
606                 if (ex.GetDBErrCode() == 200011) { // pool full
607                     need_exclude = false;
608                 }
609             }
610         }
611 
612         if (do_not_dispatch) {
613             return t_con;
614         }
615         else if (!t_con) {
616             if (cur_srv_name == service_name  &&  cur_host == 0  &&  cur_port == 0
617                 &&  (ctx.conn_status != IConnValidator::eTempInvalidConn
618                         ||  (GetMaxNumOfValidationAttempts()
619                              &&  rt_data.GetNumOfValidationFailures(service_name)
620                                                >= GetMaxNumOfValidationAttempts())))
621             {
622                 m_Errors.push_back(new CDB_Exception(DIAG_COMPILE_INFO, NULL, CDB_Exception::EErrCode(0),
623                                    "No more services/servers to try", eDiag_Error, 0));
624                 return NULL;
625             }
626 
627             if (need_exclude) {
628                 // Server might be temporarily unavailable ...
629                 // Check conn_status ...
630                 if (ctx.conn_status == IConnValidator::eTempInvalidConn) {
631                     rt_data.IncNumOfValidationFailures(service_name,
632                                                        ctx.last_tried);
633                 } else {
634                     // conn_status == IConnValidator::eInvalidConn
635                     rt_data.Exclude(service_name, ctx.last_tried);
636                     tried_servers.push_back(ctx.last_tried);
637                 }
638             }
639         } else {
640             rt_data.SetDispatchedServer(service_name, dsp_srv);
641         }
642     }
643 
644     if (!t_con) {
645         m_Errors.push_back(new CDB_Exception(DIAG_COMPILE_INFO, NULL, CDB_Exception::EErrCode(0),
646                            "Can't try any more alternatives (max number is "
647                            + NStr::UIntToString(GetMaxNumOfServerAlternatives()) + ")", eDiag_Error, 0));
648     }
649     return t_con;
650 }
651 
652 CDB_Connection*
MakeValidConnection(SOpeningContext & ctx,const CDBConnParams & params,CDB_Connection * candidate)653 CDBConnectionFactory::MakeValidConnection(
654     SOpeningContext& ctx,
655     const CDBConnParams& params,
656     CDB_Connection* candidate)
657 {
658     CStopWatchScope timing(ctx.make_valid_connection_sw);
659     _TRACE("Trying to connect to server '" << params.GetServerName()
660            << "', host " << impl::ConvertN2A(params.GetHost())
661            << ", port " << params.GetPort());
662     if (params.GetHost() != 0) {
663         ctx.tried.push_back(impl::ConvertN2A(params.GetHost()) + ':'
664                             + NStr::NumericToString(params.GetPort()));
665     } else {
666         ctx.tried.push_back(params.GetServerName());
667     }
668 
669     unique_ptr<CDB_Connection> conn;
670     try {
671         if (candidate == NULL) {
672             conn.reset(CtxMakeConnection(ctx.driver_ctx, params));
673         } else {
674             conn.reset(candidate);
675         }
676     } catch (...) {
677         CRef<IConnValidator> validator = params.GetConnValidator();
678         CRuntimeData&        rt_data   = GetRuntimeData(validator);
679         const string&        service   = params.GetServerName();
680         ctx.last_tried.Reset(rt_data.GetDispatchedServer(service));
681         if (ctx.last_tried.Empty()) {
682             ctx.last_tried.Reset
683                 (new CDBServer(service, params.GetHost(), params.GetPort()));
684         }
685         throw;
686     }
687 
688     if (conn.get())
689     {
690         ctx.last_tried.Reset(new CDBServer(conn->ServerName(),
691                                            conn->Host(), conn->Port()));
692         if (conn->IsReusable()  &&  !params.GetParam("pool_name").empty()) {
693             if (conn->Host() != 0) {
694                 ctx.tried.back() = (impl::ConvertN2A(conn->Host()) + ':'
695                                     + NStr::NumericToString(conn->Port()));
696             } else {
697                 ctx.tried.back() = conn->ServerName();
698             }
699         }
700 
701         if (conn->Host() == 0) {
702             GetRuntimeData(params.GetConnValidator()).GetDBServiceMapper()
703                 .RecordServer(conn->GetExtraFeatures());
704         }
705         CTrivialConnValidator use_db_validator(
706             params.GetDatabaseName(),
707             CTrivialConnValidator::eKeepModifiedConnection
708             );
709         CConnValidatorCoR validator;
710 
711         validator.Push(params.GetConnValidator());
712 
713         // Check "use <database>" command ...
714         if (!params.GetDatabaseName().empty()) {
715             validator.Push(CRef<IConnValidator>(&use_db_validator));
716         }
717 
718         ctx.conn_status = IConnValidator::eInvalidConn;
719         try {
720             ctx.conn_status = validator.Validate(*conn);
721             if (ctx.conn_status != IConnValidator::eValidConn) {
722                 if (conn->IsReusable()) {
723                     static_cast<impl::CConnection&>(conn->GetExtraFeatures())
724                         .m_Reusable = false;
725                 }
726                 CDB_Exception ex(DIAG_COMPILE_INFO, NULL,
727                                  CDB_Exception::EErrCode(0),
728                                  "Validation failed against "
729                                  + params.GetServerName(),
730                                  eDiag_Error, 0);
731                 ex.SetRetriable(eRetriable_No);
732                 // m_Errors.push_back(ex.Clone());
733                 ctx.handlers.PostMsg(&ex);
734                 return NULL;
735             }
736         } catch (CDB_Exception& ex) {
737             if (params.GetConnValidator().NotNull()) {
738                 ctx.conn_status
739                     = params.GetConnValidator()->ValidateException(ex);
740             }
741             if (ctx.conn_status != IConnValidator::eValidConn) {
742                 if (conn->IsReusable()) {
743                     static_cast<impl::CConnection&>(conn->GetExtraFeatures())
744                         .m_Reusable = false;
745                 }
746                 // m_Errors.push_back(ex.Clone());
747                 ctx.handlers.PostMsg(&ex);
748                 return NULL;
749             }
750         } catch (const CException& ex) {
751             ERR_POST_X(1, Warning << ex.ReportAll() << " when trying to connect to "
752                        << "server '" << params.GetServerName() << "' as user '"
753                        << params.GetUserName() << "'");
754             return NULL;
755         } catch (...) {
756             ERR_POST_X(2, Warning << "Unknown exception when trying to connect to "
757                        << "server '" << params.GetServerName() << "' as user '"
758                        << params.GetUserName() << "'");
759             throw;
760         }
761         ctx.tried.pop_back();
762         conn->FinishOpening();
763     }
764     else {
765         CRef<IConnValidator> validator = params.GetConnValidator();
766         CRuntimeData&        rt_data   = GetRuntimeData(validator);
767         const string&        service   = params.GetServerName();
768         ctx.last_tried.Reset(rt_data.GetDispatchedServer(service));
769         if (ctx.last_tried.Empty()) {
770             ctx.last_tried.Reset
771                 (new CDBServer(service, params.GetHost(), params.GetPort()));
772         }
773 
774         m_Errors.push_back(new CDB_Exception(DIAG_COMPILE_INFO, NULL, CDB_Exception::EErrCode(0),
775                            "Parameters prohibited creating connection", eDiag_Error, 0));
776     }
777     return conn.release();
778 }
779 
780 CDB_UserHandler::TExceptions*
GetExceptions(void)781 CDBConnectionFactory::GetExceptions(void)
782 {
783     return &m_Errors;
784 }
785 
786 void
GetServersList(const string & validator_name,const string & service_name,list<string> * serv_list)787 CDBConnectionFactory::GetServersList(const string& validator_name,
788                                      const string& service_name,
789                                      list<string>* serv_list)
790 {
791     CFastMutexGuard mg(m_Mtx);
792 
793     const IDBServiceMapper& mapper
794                         = GetRuntimeData(validator_name).GetDBServiceMapper();
795     mapper.GetServersList(service_name, serv_list);
796 }
797 
798 void
WorkWithSingleServer(const string & validator_name,const string & service_name,const string & server)799 CDBConnectionFactory::WorkWithSingleServer(const string& validator_name,
800                                            const string& service_name,
801                                            const string& server)
802 {
803     CFastMutexGuard mg(m_Mtx);
804 
805     CRuntimeData& rt_data = GetRuntimeData(validator_name);
806     TSvrRef svr(new CDBServer(server, 0, 0, numeric_limits<unsigned int>::max()));
807     rt_data.SetDispatchedServer(service_name, svr);
808 }
809 
810 // Make an effort to give each request its own connection numbering.
811 // The resulting field names can still technically repeat if an
812 // application reuses request IDs or tampers with named properties.
813 // However, that concern appears to be purely theoretical, and any
814 // duplication that does manage to occur will have fairly minor
815 // consequences.  In contrast, a global counter would cause trouble
816 // for log analyzers, and a map would either accumulate stale keys or
817 // risk duplication.
s_GetNextLogPrefix(void)818 static string s_GetNextLogPrefix(void)
819 {
820     CRequestContext& rctx = GetDiagContext().GetRequestContext();
821     string rid_str = NStr::NumericToString(rctx.GetRequestID());
822     if (rctx.GetProperty("_dbapi_last_rid") == rid_str) {
823         string connection_no
824             = NStr::UIntToString(
825                 NStr::StringToUInt(rctx.GetProperty("_dbapi_connection_no"))
826                 + 1U);
827         rctx.SetProperty("_dbapi_connection_no", connection_no);
828         return connection_no + ".dbapi_";
829     } else {
830         rctx.SetProperty("_dbapi_last_rid",      rid_str);
831         rctx.SetProperty("_dbapi_connection_no", "1");
832         return "1.dbapi_";
833     }
834 }
835 
x_LogConnection(const SOpeningContext & ctx,const CDB_Connection * connection,const CDBConnParams & params)836 void CDBConnectionFactory::x_LogConnection(const SOpeningContext& ctx,
837                                            const CDB_Connection* connection,
838                                            const CDBConnParams& params)
839 {
840     CDBServer stub_dsp_srv;
841 
842     CRef<IConnValidator> validator = params.GetConnValidator();
843     CRuntimeData&        rt_data   = GetRuntimeData(validator);
844     const string&        service   = params.GetServerName();
845     TSvrRef              dsp_srv   = ctx.last_tried;
846 
847     CDiagContext_Extra   extra     = GetDiagContext().Extra();
848     string               prefix    = s_GetNextLogPrefix();
849 
850     if (dsp_srv.Empty()) {
851         // Try rt_data.GetDispatchedServer(service) first?
852         dsp_srv.Reset(&stub_dsp_srv);
853     }
854 
855     {{
856         const char* status_str = "???";
857         switch (ctx.conn_status) {
858         case IConnValidator::eValidConn:
859             status_str = "valid";
860             break;
861         case IConnValidator::eInvalidConn:
862             status_str = "invalid";
863             break;
864         case IConnValidator::eTempInvalidConn:
865             status_str = "temporarily-invalid";
866             break;
867         }
868         extra.Print(prefix + "conn_status", status_str);
869     }}
870 
871     extra.Print(prefix + "resource", service);
872 
873     if ( !dsp_srv->GetName().empty() ) {
874         extra.Print(prefix + "server_name", dsp_srv->GetName());
875     } else if (connection != NULL  &&  !connection->ServerName().empty()) {
876         extra.Print(prefix + "server_name", connection->ServerName());
877     }
878 
879     if (dsp_srv->GetHost() != 0) {
880         extra.Print(prefix + "server_ip",
881                     impl::ConvertN2A(dsp_srv->GetHost()));
882     } else if (params.GetHost() != 0) {
883         extra.Print(prefix + "server_ip", impl::ConvertN2A(params.GetHost()));
884     } else if (connection != NULL  &&  connection->Host() != 0) {
885         extra.Print(prefix + "server_ip",
886                     impl::ConvertN2A(connection->Host()));
887     }
888 
889     if (dsp_srv->GetPort() != 0) {
890         extra.Print(prefix + "server_port", dsp_srv->GetPort());
891     } else if (params.GetPort() != 0) {
892         extra.Print(prefix + "server_port", params.GetPort());
893     } else if (connection != NULL  &&  connection->Port() != 0) {
894         extra.Print(prefix + "server_port", connection->Port());
895     }
896 
897     if ( !params.GetUserName().empty() ) {
898         extra.Print(prefix + "username", params.GetUserName());
899     }
900 
901     if ( !params.GetDatabaseName().empty() ) {
902         extra.Print(prefix + "db_name", params.GetDatabaseName());
903     } else {
904         CTrivialConnValidator* tcv
905             = dynamic_cast<CTrivialConnValidator*>(validator.GetPointer());
906         if (tcv != NULL  &&  !tcv->GetDBName().empty()) {
907             extra.Print(prefix + "db_name", tcv->GetDBName());
908         }
909     }
910 
911     if (connection != NULL  &&  !connection->PoolName().empty() ) {
912         extra.Print(prefix + "pool", connection->PoolName());
913     } else if ( !params.GetParam("pool_name").empty() ) {
914         extra.Print(prefix + "pool", params.GetParam("pool_name"));
915     }
916 
917     if (validator.NotEmpty()) {
918         extra.Print(prefix + "validator", validator->GetName());
919     }
920 
921     {{
922         string driver_name = ((connection == NULL)
923                               ? ctx.driver_ctx.GetDriverName()
924                               : connection->GetDriverName());
925         if ( !driver_name.empty() ) {
926             extra.Print(prefix + "driver", driver_name);
927         }
928     }}
929 
930     extra.Print(prefix + "name_mapper",
931                 rt_data.GetDBServiceMapper().GetName());
932 
933     size_t retries = ctx.tried.size();
934     if (ctx.conn_status != IConnValidator::eValidConn) {
935         --retries;
936     }
937     extra.Print(prefix + "retries", retries);
938     if ( !ctx.tried.empty() ) {
939         extra.Print(prefix + "tried", NStr::Join(ctx.tried, " "));
940     }
941     const string& excluded = rt_data.GetExcluded(service);
942     if ( !excluded.empty() ) {
943         extra.Print(prefix + "excluded", excluded);
944     }
945     if (connection != NULL) {
946         extra.Print(prefix + "conn_reuse_count", connection->GetReuseCount());
947     }
948 
949     double make_valid_connection_elapsed = ctx.make_valid_connection_sw.Elapsed();
950     if (make_valid_connection_elapsed != 0.0) {
951         extra.Print(prefix + "make_valid_connection_time",
952                     make_valid_connection_elapsed);
953     }
954 
955     double dispatch_server_name_elapsed = ctx.dispatch_server_name_sw.Elapsed();
956     if (dispatch_server_name_elapsed != 0.0) {
957         extra.Print(prefix + "dispatch_server_name_time",
958                     dispatch_server_name_elapsed);
959     }
960 }
961 
962 
963 ///////////////////////////////////////////////////////////////////////////////
CRuntimeData(const CDBConnectionFactory & parent,const CRef<IDBServiceMapper> & mapper)964 CDBConnectionFactory::CRuntimeData::CRuntimeData(
965     const CDBConnectionFactory& parent,
966     const CRef<IDBServiceMapper>& mapper
967     ) :
968 m_Parent(&parent),
969 m_DBServiceMapper(mapper)
970 {
971 }
972 
973 IDBServiceMapper::TOptions&
GetServerOptions(const string & svc_name,bool force_refresh)974 CDBConnectionFactory::CRuntimeData::GetServerOptions(const string& svc_name,
975                                                      bool force_refresh)
976 {
977     auto& options = m_ServerOptionsMap[svc_name];
978     if (force_refresh  ||  options.empty()) {
979         m_DBServiceMapper->GetServerOptions(svc_name, &options);
980         // OK to leave empty if nothing turns up; service mappers can and
981         // do take responsibility for temporarily remembering negative
982         // results if checking from scratch is slow, and higher-level logic
983         // on this end falls back on GetServer as needed.
984     }
985     return options;
986 }
987 
988 TSvrRef
GetDispatchedServer(const string & service_name)989 CDBConnectionFactory::CRuntimeData::GetDispatchedServer(
990     const string& service_name
991     )
992 {
993     return m_DispatchedSet[service_name];
994 }
995 
996 void
SetDispatchedServer(const string & service_name,const TSvrRef & server)997 CDBConnectionFactory::CRuntimeData::SetDispatchedServer(
998     const string& service_name,
999     const TSvrRef& server
1000     )
1001 {
1002     if (server.Empty()) {
1003         m_DispatchNumMap[service_name] = 0;
1004     } else {
1005         ++m_DispatchNumMap[service_name];
1006     }
1007 
1008     m_DispatchedSet[service_name] = server;
1009 }
1010 
1011 unsigned int
GetNumOfDispatches(const string & service_name)1012 CDBConnectionFactory::CRuntimeData::GetNumOfDispatches(
1013     const string& service_name
1014     )
1015 {
1016     return m_DispatchNumMap[service_name];
1017 }
1018 
1019 unsigned int
GetNumOfValidationFailures(const string & service_name)1020 CDBConnectionFactory::CRuntimeData::GetNumOfValidationFailures(
1021     const string& service_name
1022     )
1023 {
1024     return m_ValidationFailureMap[service_name];
1025 }
1026 
1027 void
IncNumOfValidationFailures(const string & server_name,const TSvrRef & dsp_srv)1028 CDBConnectionFactory::CRuntimeData::IncNumOfValidationFailures(
1029     const string& server_name,
1030     const TSvrRef& dsp_srv
1031     )
1032 {
1033     ++m_ValidationFailureMap[server_name];
1034 
1035     if (GetParent().GetMaxNumOfValidationAttempts() &&
1036         GetNumOfValidationFailures(server_name) >=
1037             GetParent().GetMaxNumOfValidationAttempts()) {
1038         // It is time to finish with this server ...
1039         Exclude(server_name, dsp_srv);
1040     }
1041 }
1042 
GetExcluded(const string & service_name)1043 string CDBConnectionFactory::CRuntimeData::GetExcluded
1044 (const string& service_name)
1045 {
1046     IDBServiceMapper& mapper = GetDBServiceMapper();
1047     if ( !mapper.HasExclusions(service_name) ) {
1048         return kEmptyStr;
1049     }
1050 
1051     IDBServiceMapper::TOptions options;
1052     mapper.GetServerOptions(service_name, &options);
1053     string result, delim;
1054     for (const auto& it : options) {
1055         if (it->IsExcluded()) {
1056             string exclusion;
1057             if (it->GetHost() != 0) {
1058                 exclusion = impl::ConvertN2A(it->GetHost());
1059                 if (it->GetPort() != 0) {
1060                     exclusion += ':' + NStr::NumericToString(it->GetPort());
1061                 }
1062             } else if ( !it->GetName().empty() ) {
1063                 exclusion = it->GetName();
1064             }
1065             if ( !exclusion.empty() ) {
1066                 result += delim + exclusion;
1067                 delim = " ";
1068             }
1069         }
1070     }
1071     return result;
1072 }
1073 
1074 ///////////////////////////////////////////////////////////////////////////////
CMapperFactory(IDBServiceMapper::TFactory svc_mapper_factory,const IRegistry * registry,EDefaultMapping def_mapping)1075 CDBConnectionFactory::CMapperFactory::CMapperFactory(
1076     IDBServiceMapper::TFactory svc_mapper_factory,
1077     const IRegistry* registry,
1078     EDefaultMapping def_mapping
1079     ) :
1080     m_SvcMapperFactory(svc_mapper_factory),
1081     m_Registry(registry),
1082     m_DefMapping(def_mapping)
1083 {
1084     CHECK_DRIVER_ERROR(!m_SvcMapperFactory && def_mapping != eUseDefaultMapper,
1085                        "Database service name to server name mapper was not "
1086                        "defined properly.",
1087                        0);
1088 }
1089 
1090 
1091 IDBServiceMapper*
Make(void) const1092 CDBConnectionFactory::CMapperFactory::Make(void) const
1093 {
1094     if (m_DefMapping == eUseDefaultMapper) {
1095         CRef<CDBServiceMapperCoR> mapper(new CDBServiceMapperCoR());
1096 
1097         mapper->Push(CRef<IDBServiceMapper>(new CDBDefaultServiceMapper()));
1098         if (m_SvcMapperFactory) {
1099             mapper->Push(CRef<IDBServiceMapper>(m_SvcMapperFactory(m_Registry)));
1100         }
1101 
1102         return mapper.Release();
1103     } else {
1104         if (m_SvcMapperFactory) {
1105             return m_SvcMapperFactory(m_Registry);
1106         }
1107     }
1108 
1109     return NULL;
1110 }
1111 
1112 
1113 ///////////////////////////////////////////////////////////////////////////////
CDBGiveUpFactory(IDBServiceMapper::TFactory svc_mapper_factory,const IRegistry * registry,EDefaultMapping def_mapping)1114 CDBGiveUpFactory::CDBGiveUpFactory(IDBServiceMapper::TFactory svc_mapper_factory,
1115                                    const IRegistry* registry,
1116                                    EDefaultMapping def_mapping)
1117 : CDBConnectionFactory(svc_mapper_factory, registry, def_mapping)
1118 {
1119     SetMaxNumOfConnAttempts(1); // This value is supposed to be default.
1120     SetMaxNumOfServerAlternatives(1); // Do not try other servers.
1121 }
1122 
~CDBGiveUpFactory(void)1123 CDBGiveUpFactory::~CDBGiveUpFactory(void)
1124 {
1125 }
1126 
1127 ///////////////////////////////////////////////////////////////////////////////
CDBRedispatchFactory(IDBServiceMapper::TFactory svc_mapper_factory,const IRegistry * registry,EDefaultMapping def_mapping)1128 CDBRedispatchFactory::CDBRedispatchFactory(IDBServiceMapper::TFactory svc_mapper_factory,
1129                                            const IRegistry* registry,
1130                                            EDefaultMapping def_mapping)
1131 : CDBConnectionFactory(svc_mapper_factory, registry, def_mapping)
1132 {
1133     SetMaxNumOfDispatches(1);
1134     SetMaxNumOfValidationAttempts(0); // Unlimited ...
1135 }
1136 
~CDBRedispatchFactory(void)1137 CDBRedispatchFactory::~CDBRedispatchFactory(void)
1138 {
1139 }
1140 
1141 
1142 ///////////////////////////////////////////////////////////////////////////////
CConnValidatorCoR(void)1143 CConnValidatorCoR::CConnValidatorCoR(void)
1144 {
1145 }
1146 
~CConnValidatorCoR(void)1147 CConnValidatorCoR::~CConnValidatorCoR(void)
1148 {
1149 }
1150 
1151 IConnValidator::EConnStatus
Validate(CDB_Connection & conn)1152 CConnValidatorCoR::Validate(CDB_Connection& conn)
1153 {
1154     CFastMutexGuard mg(m_Mtx);
1155 
1156     NON_CONST_ITERATE(TValidators, vr_it, m_Validators) {
1157         EConnStatus status = (*vr_it)->Validate(conn);
1158 
1159         // Exit if we met an invalid connection ...
1160         if (status != eValidConn) {
1161             return status;
1162         }
1163     }
1164     return eValidConn;
1165 }
1166 
1167 string
GetName(void) const1168 CConnValidatorCoR::GetName(void) const
1169 {
1170     string result("CConnValidatorCoR");
1171 
1172     CFastMutexGuard mg(m_Mtx);
1173 
1174     ITERATE(TValidators, vr_it, m_Validators) {
1175         result += (*vr_it)->GetName();
1176     }
1177 
1178     return result;
1179 }
1180 
1181 void
Push(const CRef<IConnValidator> & validator)1182 CConnValidatorCoR::Push(const CRef<IConnValidator>& validator)
1183 {
1184     if (validator.NotNull()) {
1185         CFastMutexGuard mg(m_Mtx);
1186 
1187         m_Validators.push_back(validator);
1188     }
1189 }
1190 
1191 void
Pop(void)1192 CConnValidatorCoR::Pop(void)
1193 {
1194     CFastMutexGuard mg(m_Mtx);
1195 
1196     m_Validators.pop_back();
1197 }
1198 
1199 CRef<IConnValidator>
Top(void) const1200 CConnValidatorCoR::Top(void) const
1201 {
1202     CFastMutexGuard mg(m_Mtx);
1203 
1204     return m_Validators.back();
1205 }
1206 
1207 bool
Empty(void) const1208 CConnValidatorCoR::Empty(void) const
1209 {
1210     CFastMutexGuard mg(m_Mtx);
1211 
1212     return m_Validators.empty();
1213 }
1214 
1215 ///////////////////////////////////////////////////////////////////////////////
CTrivialConnValidator(const string & db_name,int attr)1216 CTrivialConnValidator::CTrivialConnValidator(const string& db_name,
1217                                              int attr) :
1218 m_DBName(db_name),
1219 m_Attr(attr)
1220 {
1221 }
1222 
~CTrivialConnValidator(void)1223 CTrivialConnValidator::~CTrivialConnValidator(void)
1224 {
1225 }
1226 
1227 /* Future development ...
1228 IConnValidator::EConnStatus
1229 CTrivialConnValidator::Validate(CDB_Connection& conn)
1230 {
1231     string curr_dbname;
1232 
1233     // Get current database name ...
1234     {
1235         unique_ptr<CDB_LangCmd> auto_stmt(conn.LangCmd("select db_name()"));
1236         auto_stmt->Send();
1237         while (auto_stmt->HasMoreResults()) {
1238             unique_ptr<CDB_Result> rs(auto_stmt->Result());
1239 
1240             if (rs.get() == NULL) {
1241                 continue;
1242             }
1243 
1244             if (rs->ResultType() != eDB_RowResult) {
1245                 continue;
1246             }
1247 
1248             while (rs->Fetch()) {
1249                 CDB_VarChar db_name;
1250                 rs->GetItem(&db_name);
1251                 curr_dbname = static_cast<const string&>(db_name);
1252             }
1253         }
1254     }
1255     // Try to change a database ...
1256     if (curr_dbname != GetDBName()) {
1257         conn.SetDatabaseName(GetDBName());
1258     }
1259 
1260     if (GetAttr() & eCheckSysobjects) {
1261         unique_ptr<CDB_LangCmd> set_cmd(conn.LangCmd("SELECT id FROM sysobjects"));
1262         set_cmd->Send();
1263         set_cmd->DumpResults();
1264     }
1265 
1266     // Go back to the original (master) database ...
1267     if (GetAttr() & eRestoreDefaultDB && curr_dbname != GetDBName()) {
1268         conn.SetDatabaseName(curr_dbname);
1269     }
1270 
1271     // All exceptions are supposed to be caught and processed by
1272     // CDBConnectionFactory ...
1273     return eValidConn;
1274 }
1275 */
1276 
1277 IConnValidator::EConnStatus
Validate(CDB_Connection & conn)1278 CTrivialConnValidator::Validate(CDB_Connection& conn)
1279 {
1280     // Try to change a database ...
1281     conn.SetDatabaseName(GetDBName());
1282 
1283     if (GetAttr() & eCheckSysobjects) {
1284         unique_ptr<CDB_LangCmd> set_cmd(conn.LangCmd("SELECT id FROM sysobjects"));
1285         set_cmd->Send();
1286         set_cmd->DumpResults();
1287     }
1288 
1289     // Go back to the original (master) database ...
1290     if (GetAttr() & eRestoreDefaultDB) {
1291         conn.SetDatabaseName("master");
1292     }
1293 
1294     // All exceptions are supposed to be caught and processed by
1295     // CDBConnectionFactory ...
1296     return eValidConn;
1297 }
1298 
1299 string
GetName(void) const1300 CTrivialConnValidator::GetName(void) const
1301 {
1302     string result("CTrivialConnValidator");
1303 
1304     result += (GetAttr() == eCheckSysobjects ? "CSO" : "");
1305     result += GetDBName();
1306 
1307     return result;
1308 }
1309 
1310 END_NCBI_SCOPE
1311