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