1 /*  $Id: dbapi_impl_connection.cpp 563801 2018-05-15 15:55:00Z ucko $
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/impl/dbapi_impl_cmd.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_mgr.hpp>
36 
37 #include <dbapi/error_codes.hpp>
38 
39 #include <algorithm>
40 
41 
42 #define NCBI_USE_ERRCODE_X   Dbapi_ConnFactory
43 
44 
45 BEGIN_NCBI_SCOPE
46 
47 namespace impl
48 {
49 
50 ///////////////////////////////////////////////////////////////////////////
51 //  CConnection::
52 //
53 
Create_LangCmd(CBaseCmd & lang_cmd)54 CDB_LangCmd* CConnection::Create_LangCmd(CBaseCmd& lang_cmd)
55 {
56     m_CMDs.push_back(&lang_cmd);
57 
58     return new CDB_LangCmd(&lang_cmd);
59 }
60 
Create_RPCCmd(CBaseCmd & rpc_cmd)61 CDB_RPCCmd* CConnection::Create_RPCCmd(CBaseCmd& rpc_cmd)
62 {
63     m_CMDs.push_back(&rpc_cmd);
64 
65     return new CDB_RPCCmd(&rpc_cmd);
66 }
67 
Create_BCPInCmd(CBaseCmd & bcpin_cmd)68 CDB_BCPInCmd* CConnection::Create_BCPInCmd(CBaseCmd& bcpin_cmd)
69 {
70     m_CMDs.push_back(&bcpin_cmd);
71 
72     return new CDB_BCPInCmd(&bcpin_cmd);
73 }
74 
Create_CursorCmd(CBaseCmd & cursor_cmd)75 CDB_CursorCmd* CConnection::Create_CursorCmd(CBaseCmd& cursor_cmd)
76 {
77     m_CMDs.push_back(&cursor_cmd);
78 
79     return new CDB_CursorCmd(&cursor_cmd);
80 }
81 
Create_SendDataCmd(CSendDataCmd & senddata_cmd)82 CDB_SendDataCmd* CConnection::Create_SendDataCmd(CSendDataCmd& senddata_cmd)
83 {
84     m_CMDs.push_back(&senddata_cmd);
85 
86     return new CDB_SendDataCmd(&senddata_cmd);
87 }
88 
89 
CConnection(CDriverContext & dc,const CDBConnParams & params,bool isBCPable)90 CConnection::CConnection(CDriverContext& dc,
91                          const CDBConnParams& params,
92                          bool isBCPable
93                          )
94 : m_DriverContext(&dc)
95 , m_MsgHandlers(dc.GetConnHandlerStack())
96 , m_Interface(NULL)
97 , m_ResProc(NULL)
98 , m_ExceptionContext(new TDbgInfo(params))
99 , m_ServerType(params.GetServerType())
100 , m_ServerTypeIsKnown(false)
101 , m_RequestedServer(params.GetServerName())
102 , m_Host(params.GetHost())
103 , m_Port(params.GetPort())
104 , m_Passwd(params.GetPassword())
105 , m_Pool(params.GetParam("pool_name"))
106 , m_PoolMinSize(0)
107 , m_PoolMaxSize(kMax_UInt)
108 , m_PoolMaxConnUse(kMax_UInt)
109 , m_PoolIdleTimeParam(-1, 0)
110 , m_CleanupTime(CTime::eEmpty)
111 , m_ReuseCount(0)
112 , m_Reusable(params.GetParam("is_pooled") == "true")
113 , m_OpenFinished(false)
114 , m_Valid(true)
115 , m_BCPable(isBCPable)
116 , m_SecureLogin(params.GetParam("secure_login") == "true")
117 , m_Opened(false)
118 {
119     _ASSERT(m_MsgHandlers.GetSize() == dc.GetConnHandlerStack().GetSize());
120     _ASSERT(m_MsgHandlers.GetSize() > 0);
121     m_OpeningMsgHandlers = params.GetOpeningMsgHandlers();
122 
123     string pool_min_str      = params.GetParam("pool_minsize"),
124            pool_max_str      = params.GetParam("pool_maxsize"),
125            pool_idle_str     = params.GetParam("pool_idle_time"),
126            pool_max_conn_use = params.GetParam("pool_max_conn_use");
127 
128     if ( !pool_min_str.empty()  &&  pool_min_str != "default") {
129         m_PoolMinSize = NStr::StringToUInt(pool_min_str);
130     }
131     if ( !pool_max_str.empty()  &&  pool_max_str != "default") {
132         m_PoolMaxSize = NStr::StringToUInt(pool_max_str);
133     }
134     if ( !pool_idle_str.empty()  &&  pool_idle_str != "default") {
135         m_PoolIdleTimeParam = CTimeSpan(NStr::StringToDouble(pool_idle_str));
136     }
137     if ( !pool_max_conn_use.empty() && pool_max_conn_use != "default") {
138         m_PoolMaxConnUse = NStr::StringToUInt(pool_max_conn_use);
139         if (m_PoolMaxConnUse == 0)
140             m_PoolMaxConnUse = kMax_UInt;
141     }
142 
143     CheckCanOpen();
144 }
145 
~CConnection(void)146 CConnection::~CConnection(void)
147 {
148     DetachResultProcessor();
149 //         DetachInterface();
150     MarkClosed();
151 }
152 
CheckCanOpen(void)153 void CConnection::CheckCanOpen(void)
154 {
155     MarkClosed();
156 
157     // Check for maximum number of connections
158     if (!CDbapiConnMgr::Instance().AddConnect()) {
159         // May be ineffective, particularly given that there's no
160         // pool_idle_time by default.
161         m_DriverContext->CloseOldIdleConns(1);
162         if (CDbapiConnMgr::Instance().AddConnect()) {
163             m_Opened = true;
164             return;
165         } else {
166             // Could perhaps try more specific parameters first, but if a
167             // matching connection were already available in the pool, DBAPI
168             // would normally have used it instead of establishing a new one.
169             m_DriverContext->CloseUnusedConnections(kEmptyStr, kEmptyStr, 1);
170             if (CDbapiConnMgr::Instance().AddConnect()) {
171                 m_Opened = true;
172                 return;
173             }
174         }
175         const string conn_num = NStr::NumericToString(CDbapiConnMgr::Instance().GetMaxConnect());
176         const string msg =
177             "Cannot create new connection: hit limit of " + conn_num
178             + " simultaneously open connections.";
179         ERR_POST_X_ONCE(3, msg);
180         DATABASE_DRIVER_ERROR(msg, 500000);
181     }
182 
183     m_Opened = true;
184 }
185 
MarkClosed(void)186 void CConnection::MarkClosed(void)
187 {
188     if (m_Opened) {
189         CDbapiConnMgr::Instance().DelConnect();
190         m_Opened = false;
191     }
192 }
193 
194 
195 CDBConnParams::EServerType
CalculateServerType(CDBConnParams::EServerType server_type)196 CConnection::CalculateServerType(CDBConnParams::EServerType server_type)
197 {
198     if (server_type == CDBConnParams::eUnknown) {
199         CMsgHandlerGuard guard(*this);
200 
201         try {
202             unique_ptr<CDB_LangCmd> cmd(LangCmd("SELECT @@version"));
203             cmd->Send();
204 
205             while (cmd->HasMoreResults()) {
206                 unique_ptr<CDB_Result> res(cmd->Result());
207 
208                 if (res.get() != NULL && res->ResultType() == eDB_RowResult ) {
209                     CDB_VarChar version;
210 
211                     while (res->Fetch()) {
212                         res->GetItem(&version);
213 
214                         if (!version.IsNULL()) {
215                             const string& verstr = version.AsString();
216                             if (NStr::StartsWith(verstr, "Adaptive Server")
217                                 ||  NStr::StartsWith(verstr, "SAP IQ")) {
218                                 server_type = CDBConnParams::eSybaseSQLServer;
219                             } else if (NStr::StartsWith
220                                        (verstr, "Microsoft SQL Server")) {
221                                 server_type = CDBConnParams::eMSSqlServer;
222                             } else if (NStr::StartsWith(verstr, "Open Server")
223                                        ||  NStr::StartsWith
224                                            (verstr, "PubSeqOS")) {
225                                 server_type = CDBConnParams::eSybaseOpenServer;
226                             }
227                         }
228                     }
229                 }
230             }
231         }
232         catch(const CException&) {
233             server_type = CDBConnParams::eSybaseOpenServer;
234         }
235     }
236 
237     return server_type;
238 }
239 
240 CDBConnParams::EServerType
GetServerType(void)241 CConnection::GetServerType(void)
242 {
243     if (m_ServerType == CDBConnParams::eUnknown && !m_ServerTypeIsKnown) {
244         m_ServerType = CalculateServerType(CDBConnParams::eUnknown);
245         m_ServerTypeIsKnown = true;
246     }
247 
248     return m_ServerType;
249 }
250 
GetDateTimeSyntax(void)251 CDB_BigDateTime::ESyntax CConnection::GetDateTimeSyntax(void)
252 {
253     switch (GetServerType()) {
254     case CDBConnParams::eSybaseOpenServer:
255     case CDBConnParams::eSybaseSQLServer:
256         return CDB_BigDateTime::eSyntax_Sybase;
257     case CDBConnParams::eMSSqlServer:
258         return CDB_BigDateTime::eSyntax_Microsoft;
259     default:
260         return CDB_BigDateTime::eSyntax_Unknown;
261     }
262 }
263 
PushMsgHandler(CDB_UserHandler * h,EOwnership ownership)264 void CConnection::PushMsgHandler(CDB_UserHandler* h,
265                                     EOwnership ownership)
266 {
267     m_MsgHandlers.Push(h, ownership);
268     _ASSERT(m_MsgHandlers.GetSize() > 0);
269 }
270 
271 
PopMsgHandler(CDB_UserHandler * h)272 void CConnection::PopMsgHandler(CDB_UserHandler* h)
273 {
274     m_MsgHandlers.Pop(h, false);
275     _ASSERT(m_MsgHandlers.GetSize() > 0);
276 }
277 
DropCmd(impl::CCommand & cmd)278 void CConnection::DropCmd(impl::CCommand& cmd)
279 {
280     TCommandList::iterator it = find(m_CMDs.begin(), m_CMDs.end(), &cmd);
281 
282     if (it != m_CMDs.end()) {
283         m_CMDs.erase(it);
284     }
285 }
286 
DeleteAllCommands(void)287 void CConnection::DeleteAllCommands(void)
288 {
289     while (!m_CMDs.empty()) {
290         // Destructor will remove an entity from a container ...
291         delete m_CMDs.back();
292     }
293 }
294 
Release(void)295 void CConnection::Release(void)
296 {
297     // close all commands first
298     DeleteAllCommands();
299     GetCDriverContext().DestroyConnImpl(this);
300 }
301 
Context(void) const302 I_DriverContext* CConnection::Context(void) const
303 {
304     _ASSERT(m_DriverContext);
305     return m_DriverContext;
306 }
307 
DetachResultProcessor(void)308 void CConnection::DetachResultProcessor(void)
309 {
310     if (m_ResProc) {
311         m_ResProc->ReleaseConn();
312         m_ResProc = NULL;
313     }
314 }
315 
SetResultProcessor(CDB_ResultProcessor * rp)316 CDB_ResultProcessor* CConnection::SetResultProcessor(CDB_ResultProcessor* rp)
317 {
318     CDB_ResultProcessor* r = m_ResProc;
319     m_ResProc = rp;
320     return r;
321 }
322 
Create_Result(impl::CResult & result)323 CDB_Result* CConnection::Create_Result(impl::CResult& result)
324 {
325     return new CDB_Result(&result);
326 }
327 
ServerName(void) const328 const string& CConnection::ServerName(void) const
329 {
330     return m_ExceptionContext->server_name;
331 }
332 
Host(void) const333 Uint4 CConnection::Host(void) const
334 {
335     return m_Host;
336 }
337 
Port(void) const338 Uint2 CConnection::Port(void) const
339 {
340     return m_Port;
341 }
342 
343 
UserName(void) const344 const string& CConnection::UserName(void) const
345 {
346     return m_ExceptionContext->username;
347 }
348 
349 
Password(void) const350 const string& CConnection::Password(void) const
351 {
352     return m_Passwd;
353 }
354 
PoolName(void) const355 const string& CConnection::PoolName(void) const
356 {
357     return m_Pool;
358 }
359 
IsReusable(void) const360 bool CConnection::IsReusable(void) const
361 {
362     return m_Reusable;
363 }
364 
AttachTo(CDB_Connection * interface)365 void CConnection::AttachTo(CDB_Connection* interface)
366 {
367     m_Interface = interface;
368 }
369 
ReleaseInterface(void)370 void CConnection::ReleaseInterface(void)
371 {
372     m_Interface = NULL;
373     ++m_ReuseCount;
374 }
375 
376 
SetBlobSize(size_t)377 void CConnection::SetBlobSize(size_t /* nof_bytes */)
378 {
379 }
380 
381 
GetTimeout(void) const382 size_t CConnection::GetTimeout(void) const
383 {
384     return GetCDriverContext().GetTimeout();
385 }
386 
387 
GetCancelTimeout(void) const388 size_t CConnection::GetCancelTimeout(void) const
389 {
390     return GetCDriverContext().GetCancelTimeout();
391 }
392 
393 
394 bool
IsMultibyteClientEncoding(void) const395 CConnection::IsMultibyteClientEncoding(void) const
396 {
397     return GetCDriverContext().IsMultibyteClientEncoding();
398 }
399 
400 
401 EEncoding
GetClientEncoding(void) const402 CConnection::GetClientEncoding(void) const
403 {
404     return GetCDriverContext().GetClientEncoding();
405 }
406 
407 void
SetDatabaseName(const string & name)408 CConnection::SetDatabaseName(const string& name)
409 {
410     if (!name.empty()) {
411         const string sql = "use " + name;
412 
413         unique_ptr<CDB_LangCmd> auto_stmt(LangCmd(sql));
414         auto_stmt->Send();
415         auto_stmt->DumpResults();
416 
417         m_ExceptionContext->database_name = name;
418     }
419 }
420 
421 const string&
GetDatabaseName(void) const422 CConnection::GetDatabaseName(void) const
423 {
424     return m_ExceptionContext->database_name;
425 }
426 
427 I_ConnectionExtra::TSockHandle
GetLowLevelHandle(void) const428 CConnection::GetLowLevelHandle(void) const
429 {
430     DATABASE_DRIVER_ERROR("GetLowLevelHandle is not implemented", 500001);
431 }
432 
GetDriverName(void) const433 string CConnection::GetDriverName(void) const
434 {
435     return GetCDriverContext().GetDriverName();
436 }
437 
x_RecordServer(const CDBServer & server)438 void CConnection::x_RecordServer(const CDBServer& server)
439 {
440     CMutexGuard mg(eEmptyGuard);
441     string new_name = ServerName().substr(0, ServerName().find(':'))
442         + '@' + server.GetName();
443     _TRACE("Updating server metadata from " << ServerName() << '@'
444            << ConvertN2A(m_Host) << ':' << m_Port << " to " << new_name);
445 
446     if (m_Reusable) {
447         mg.Guard(m_DriverContext->m_PoolMutex);
448         m_DriverContext->x_AdjustCounts(this, -1);
449     }
450     m_ExceptionContext->server_name = new_name;
451     m_Host = server.GetHost();
452     m_Port = server.GetPort();
453     if (m_Reusable) {
454         m_DriverContext->x_AdjustCounts(this, 1);
455     }
456 }
457 
458 } // namespace impl
459 
460 END_NCBI_SCOPE
461 
462 
463