1 /* $Id: public.cpp 620241 2020-11-18 15:39:15Z 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: Vladimir Soussov
27 *
28 * File Description: Data Server public interfaces
29 *
30 */
31
32 #include <ncbi_pch.hpp>
33 #include <dbapi/driver/public.hpp>
34 #include <dbapi/driver/impl/dbapi_impl_result.hpp>
35 #include <dbapi/driver/impl/dbapi_impl_cmd.hpp>
36 #include <dbapi/driver/impl/dbapi_impl_connection.hpp>
37 #include <dbapi/error_codes.hpp>
38
39 #ifdef NCBI_OS_MSWIN
40 # include <winsock2.h>
41 #elif !defined(NCBI_OS_SOLARIS)
42 # include <sys/fcntl.h>
43 # include <sys/types.h>
44 # include <sys/socket.h>
45 #endif
46
47 #define NCBI_USE_ERRCODE_X Dbapi_DataServer
48
49
50 BEGIN_NCBI_SCOPE
51
52
53
54 #ifdef _DEBUG
s_TraceParams(const CDBParams & params,const CDiagCompileInfo & info)55 static void s_TraceParams(const CDBParams& params,
56 const CDiagCompileInfo& info) {
57 if (dynamic_cast<const impl::CCachedRowInfo*>(¶ms)) {
58 return;
59 }
60 for (unsigned int i = 0, n = params.GetNum(); i < n; ++i) {
61 const CDB_Object* obj = params.GetValue(i);
62 const string& name = params.GetName(i);
63 string value = obj ? obj->GetLogString() : string("(null)");
64 if (name.empty()) { // insertion, typically bulk
65 CNcbiDiag(info, eDiag_Trace).GetRef()
66 << "Column #" << (i + 1) << " = " << value
67 << Endm;
68 } else {
69 CNcbiDiag(info, eDiag_Trace).GetRef()
70 << "Parameter #" << (i + 1) << " (" << name << ") = " << value
71 << Endm;
72 }
73 }
74 }
75 // Cite caller.
76 # define TRACE_PARAMS(params) s_TraceParams(params, DIAG_COMPILE_INFO)
77 #else
78 # define TRACE_PARAMS(params) ((void)0)
79 #endif
80
81
82
83 ////////////////////////////////////////////////////////////////////////////
84 // CDBParamVariant::
85 //
86 inline
ConvertI2UI(int value)87 unsigned int ConvertI2UI(int value)
88 {
89 CHECK_DRIVER_ERROR( (value < 0), "Negative parameter's position not allowed.", 200001 );
90
91 return static_cast<unsigned int>(value);
92 }
93
CDBParamVariant(int pos)94 CDBParamVariant::CDBParamVariant(int pos)
95 : m_IsPositional(true)
96 , m_Pos(ConvertI2UI(pos))
97 {
98 }
99
100
CDBParamVariant(unsigned int pos)101 CDBParamVariant::CDBParamVariant(unsigned int pos)
102 : m_IsPositional(true)
103 , m_Pos(pos)
104 {
105 }
106
107
CDBParamVariant(const char * name)108 CDBParamVariant::CDBParamVariant(const char* name)
109 : m_IsPositional(false)
110 , m_Pos(0)
111 , m_Name(MakeName(name, m_Format))
112 {
113 }
114
CDBParamVariant(const string & name)115 CDBParamVariant::CDBParamVariant(const string& name)
116 : m_IsPositional(false)
117 , m_Pos(0)
118 , m_Name(MakeName(name, m_Format))
119 {
120 }
121
122
~CDBParamVariant(void)123 CDBParamVariant::~CDBParamVariant(void)
124 {
125 }
126
127 // Not finished yet ...
128 string
GetName(CDBParamVariant::ENameFormat format) const129 CDBParamVariant::GetName(CDBParamVariant::ENameFormat format) const
130 {
131 if (format != GetFormat()) {
132 switch (format) {
133 case ePlainName:
134 return MakePlainName(m_Name);
135 case eQMarkName: // '...WHERE name=?'
136 return "?";
137 case eNumericName: // '...WHERE name=:1'
138 case eNamedName: // '...WHERE name=:name'
139 return ':' + MakePlainName(m_Name);
140 case eFormatName: // ANSI C printf format codes, e.g. '...WHERE name=%s'
141 return '%' + MakePlainName(m_Name);
142 case eSQLServerName: // '...WHERE name=@name'
143 return '@' + MakePlainName(m_Name);
144 }
145 }
146
147 return m_Name;
148 }
149
150
MakeName(const CTempString & name,CDBParamVariant::ENameFormat & format)151 CTempString CDBParamVariant::MakeName(const CTempString& name,
152 CDBParamVariant::ENameFormat& format)
153 {
154 // Do not make copy of name to process it ...
155
156 CTempString new_name;
157 CTempString::const_iterator begin_str = NULL, c = name.data();
158
159 format = ePlainName;
160
161 for (; c != NULL && c != name.end(); ++c) {
162 char ch = *c;
163 if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') {
164 if (begin_str == NULL) {
165 // Remove whitespace ...
166 continue;
167 } else {
168 // Look forward for non-space characters.
169 bool space_chars_only = true;
170 for (const char* tc = c; tc != NULL && *tc != '\0'; ++tc) {
171 char tch = *tc;
172 if (tch == ' ' || tch == '\t' || tch == '\n' || tch == '\r') {
173 continue;
174 } else {
175 space_chars_only = false;
176 break;
177 }
178 }
179
180 if (space_chars_only) {
181 // Remove trailing whitespace ...
182 break;
183 }
184 }
185 }
186 // Check for leading symbol ...
187 if (begin_str == NULL) {
188 begin_str = c;
189
190 switch (ch) {
191 case '?' :
192 format = eQMarkName;
193 break;
194 case ':' :
195 if (*(c + 1)) {
196 if (isdigit(*(c + 1))) {
197 format = eNumericName;
198 } else {
199 format = eNamedName;
200 }
201 } else {
202 DATABASE_DRIVER_ERROR("Invalid parameter format: "
203 + string(name), 1);
204 }
205 break;
206 case '@' :
207 format = eSQLServerName;
208 break;
209 case '%' :
210 format = eFormatName;
211 break;
212 case '$' :
213 // !!!!
214 format = eFormatName;
215 break;
216 }
217 }
218 }
219
220 if (begin_str != NULL) {
221 new_name.assign(begin_str, c - begin_str);
222 }
223
224 return new_name;
225 }
226
227
MakePlainName(const CTempString & name)228 string CDBParamVariant::MakePlainName(const CTempString& name)
229 {
230 // Do not make copy of name to process it ...
231
232 CTempString plain_name;
233 CTempString::const_iterator begin_str = NULL, c = name.data();
234
235 for (; c != NULL && c != name.end(); ++c) {
236 char ch = *c;
237 if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') {
238 if (begin_str == NULL) {
239 // Remove whitespaces ...
240 continue;
241 } else {
242 // Look forward for non-space characters.
243 bool space_chars_only = true;
244 for (const char* tc = c; tc != NULL && *tc != '\0'; ++tc) {
245 char tch = *tc;
246 if (tch == ' ' || tch == '\t' || tch == '\n' || tch == '\r') {
247 continue;
248 } else {
249 space_chars_only = false;
250 break;
251 }
252 }
253
254 if (space_chars_only) {
255 // Remove trailing whitespace ...
256 break;
257 }
258 }
259 }
260 // Check for leading symbol ...
261 if (begin_str == NULL) {
262 begin_str = c;
263 if (ch == ':' || ch == '@' || ch == '$' || ch == '%') {
264 // Skip leading symbol ...
265 ++begin_str;
266 }
267 }
268 }
269
270 if (begin_str != NULL) {
271 plain_name.assign(begin_str, c - begin_str);
272 }
273
274 return plain_name;
275 }
276
277
278 ////////////////////////////////////////////////////////////////////////////
279 // CCDB_Connection::
280 //
281
CDB_Connection(impl::CConnection * c)282 CDB_Connection::CDB_Connection(impl::CConnection* c)
283 : m_ConnImpl(c), m_HasTransaction(false)
284 {
285 CHECK_DRIVER_ERROR( !c, "No valid connection provided", 200001 );
286
287 m_ConnImpl->AttachTo(this);
288 m_ConnImpl->SetResultProcessor(0); // to clean up the result processor if any
289 }
290
291
IsAlive()292 bool CDB_Connection::IsAlive()
293 {
294 if (m_ConnImpl == NULL || !m_ConnImpl->IsAlive()) {
295 return false;
296 } else {
297 return x_IsAlive();
298 }
299 }
300
x_IsAlive()301 bool CDB_Connection::x_IsAlive()
302 {
303 // Try to confirm that the network connection hasn't closed.
304 // (Done only when no separate network library is necessary.)
305 // XXX - consider caching GetLowLevelHandle result, or at least
306 // availability.
307 #ifndef NCBI_OS_SOLARIS
308 try {
309 I_ConnectionExtra::TSockHandle s = m_ConnImpl->GetLowLevelHandle();
310 char c;
311 # ifdef NCBI_OS_UNIX
312 // On Windows, the non-blocking flag is write-only(!); leave it
313 // alone, since only the ftds driver implements
314 // GetLowLevelHandle, and FreeTDS normally enables non-blocking
315 // mode itself. (It does so on Unix too, but explicitly
316 // restoring settings is still best practice.)
317 int orig_flags = fcntl(s, F_GETFL, 0);
318 fcntl(s, F_SETFL, orig_flags | O_NONBLOCK);
319 # endif
320 int n = recv(s, &c, 1, MSG_PEEK);
321 # ifdef NCBI_OS_UNIX
322 if ((orig_flags & O_NONBLOCK) != O_NONBLOCK) {
323 fcntl(s, F_SETFL, orig_flags);
324 }
325 # endif
326 if (n > 0) {
327 return true; // open, with unread data available
328 } else if (n == 0) {
329 return false; // closed
330 } else {
331 # ifdef NCBI_OS_MSWIN
332 return WSAGetLastError() == WSAEWOULDBLOCK;
333 # else
334 switch (errno) {
335 case EAGAIN:
336 # if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
337 case EWOULDBLOCK:
338 # endif
339 return true; // open, but no data immediately available
340 default:
341 return false; // something else is wrong
342 }
343 # endif
344 }
345 } catch (CDB_Exception&) { // Presumably unimplemented
346 return true;
347 }
348 #endif
349 return true;
350 }
351
352 #define CHECK_CONNECTION( conn ) \
353 CHECK_DRIVER_WARNING( !conn, "Connection has been closed", 200002 )
354
LangCmd(const string & lang_query)355 CDB_LangCmd* CDB_Connection::LangCmd(const string& lang_query)
356 {
357 CHECK_CONNECTION(m_ConnImpl);
358 _TRACE("Sending SQL: " << lang_query);
359 return m_ConnImpl->LangCmd(lang_query);
360 }
361
RPC(const string & rpc_name)362 CDB_RPCCmd* CDB_Connection::RPC(const string& rpc_name)
363 {
364 CHECK_CONNECTION(m_ConnImpl);
365 // _TRACE-d in CDB_RPCCmd::Send, which performs the actual I/O.
366 return m_ConnImpl->RPC(rpc_name);
367 }
368
BCPIn(const string & table_name)369 CDB_BCPInCmd* CDB_Connection::BCPIn(const string& table_name)
370 {
371 CHECK_CONNECTION(m_ConnImpl);
372 _TRACE("Performing bulk insertion into " << table_name);
373 return m_ConnImpl->BCPIn(table_name);
374 }
375
Cursor(const string & cursor_name,const string & query,unsigned int batch_size)376 CDB_CursorCmd* CDB_Connection::Cursor(const string& cursor_name,
377 const string& query,
378 unsigned int batch_size)
379 {
380 CHECK_CONNECTION(m_ConnImpl);
381 _TRACE("Opening cursor for " << query);
382 return m_ConnImpl->Cursor(cursor_name, query, batch_size);
383 }
384
SendDataCmd(I_BlobDescriptor & desc,size_t data_size,bool log_it,bool dump_results)385 CDB_SendDataCmd* CDB_Connection::SendDataCmd(I_BlobDescriptor& desc,
386 size_t data_size,
387 bool log_it,
388 bool dump_results)
389 {
390 CHECK_CONNECTION(m_ConnImpl);
391 _TRACE("Sending " << data_size << " byte(s) of data");
392 return m_ConnImpl->SendDataCmd(desc, data_size, log_it, dump_results);
393 }
394
SendData(I_BlobDescriptor & desc,CDB_Stream & lob,bool log_it)395 bool CDB_Connection::SendData(I_BlobDescriptor& desc, CDB_Stream& lob,
396 bool log_it)
397 {
398 CHECK_CONNECTION(m_ConnImpl);
399 _TRACE("Sending " << lob.Size() << " byte(s) of data");
400 return m_ConnImpl->SendData(desc, lob, log_it);
401 }
402
SetDatabaseName(const string & name)403 void CDB_Connection::SetDatabaseName(const string& name)
404 {
405 if (name.empty()) {
406 return;
407 }
408
409 CHECK_CONNECTION(m_ConnImpl);
410 _TRACE("Now using database " << name);
411 m_ConnImpl->SetDatabaseName(name);
412 }
413
Refresh()414 bool CDB_Connection::Refresh()
415 {
416 CHECK_CONNECTION(m_ConnImpl);
417 return m_ConnImpl->Refresh() && x_IsAlive();
418 }
419
ServerName() const420 const string& CDB_Connection::ServerName() const
421 {
422 CHECK_CONNECTION(m_ConnImpl);
423 return m_ConnImpl->ServerName();
424 }
425
Host() const426 Uint4 CDB_Connection::Host() const
427 {
428 CHECK_CONNECTION(m_ConnImpl);
429 return m_ConnImpl->Host();
430 }
431
Port() const432 Uint2 CDB_Connection::Port() const
433 {
434 CHECK_CONNECTION(m_ConnImpl);
435 return m_ConnImpl->Port();
436 }
437
UserName() const438 const string& CDB_Connection::UserName() const
439 {
440 CHECK_CONNECTION(m_ConnImpl);
441 return m_ConnImpl->UserName();
442 }
443
Password() const444 const string& CDB_Connection::Password() const
445 {
446 CHECK_CONNECTION(m_ConnImpl);
447 return m_ConnImpl->Password();
448 }
449
DatabaseName() const450 const string& CDB_Connection::DatabaseName() const
451 {
452 CHECK_CONNECTION(m_ConnImpl);
453 return m_ConnImpl->GetDatabaseName();
454 }
455
ConnectMode() const456 I_DriverContext::TConnectionMode CDB_Connection::ConnectMode() const
457 {
458 CHECK_CONNECTION(m_ConnImpl);
459 return m_ConnImpl->ConnectMode();
460 }
461
IsReusable() const462 bool CDB_Connection::IsReusable() const
463 {
464 CHECK_CONNECTION(m_ConnImpl);
465 return m_ConnImpl->IsReusable();
466 }
467
GetReuseCount() const468 unsigned int CDB_Connection::GetReuseCount() const
469 {
470 CHECK_CONNECTION(m_ConnImpl);
471 return m_ConnImpl->GetReuseCount();
472 }
473
PoolName() const474 const string& CDB_Connection::PoolName() const
475 {
476 CHECK_CONNECTION(m_ConnImpl);
477 return m_ConnImpl->PoolName();
478 }
479
Context() const480 I_DriverContext* CDB_Connection::Context() const
481 {
482 CHECK_CONNECTION(m_ConnImpl);
483 return m_ConnImpl->Context();
484 }
485
PushMsgHandler(CDB_UserHandler * h,EOwnership ownership)486 void CDB_Connection::PushMsgHandler(CDB_UserHandler* h,
487 EOwnership ownership)
488 {
489 CHECK_CONNECTION(m_ConnImpl);
490 m_ConnImpl->PushMsgHandler(h, ownership);
491 }
492
PopMsgHandler(CDB_UserHandler * h)493 void CDB_Connection::PopMsgHandler(CDB_UserHandler* h)
494 {
495 CHECK_CONNECTION(m_ConnImpl);
496 m_ConnImpl->PopMsgHandler(h);
497 }
498
499 CDB_ResultProcessor*
SetResultProcessor(CDB_ResultProcessor * rp)500 CDB_Connection::SetResultProcessor(CDB_ResultProcessor* rp)
501 {
502 return m_ConnImpl? m_ConnImpl->SetResultProcessor(rp) : NULL;
503 }
504
~CDB_Connection()505 CDB_Connection::~CDB_Connection()
506 {
507 try {
508 if ( m_ConnImpl ) {
509 Close();
510 }
511 }
512 NCBI_CATCH_ALL_X( 2, NCBI_CURRENT_FUNCTION )
513 }
514
515
Abort()516 bool CDB_Connection::Abort()
517 {
518 CHECK_CONNECTION(m_ConnImpl);
519 if (m_ConnImpl->Abort()) {
520 Close();
521 return true;
522 }
523 return false;
524 }
525
526
Close(void)527 bool CDB_Connection::Close(void)
528 {
529 CHECK_CONNECTION(m_ConnImpl);
530 try {
531 if (m_ConnImpl->IsReusable() && m_ConnImpl->IsAlive() && x_IsAlive()
532 && m_ConnImpl->GetServerType() != CDBConnParams::eSybaseOpenServer) {
533 unique_ptr<CDB_LangCmd> lcmd(LangCmd("IF @@TRANCOUNT > 0 ROLLBACK"));
534 lcmd->Send();
535 lcmd->DumpResults();
536 }
537 } catch (CDB_Exception&) {
538 }
539 m_ConnImpl->Release();
540 m_ConnImpl = NULL;
541 return true;
542 }
543
SetTimeout(size_t nof_secs)544 void CDB_Connection::SetTimeout(size_t nof_secs)
545 {
546 CHECK_CONNECTION(m_ConnImpl);
547 m_ConnImpl->SetTimeout(nof_secs);
548 }
549
SetCancelTimeout(size_t nof_secs)550 void CDB_Connection::SetCancelTimeout(size_t nof_secs)
551 {
552 CHECK_CONNECTION(m_ConnImpl);
553 m_ConnImpl->SetCancelTimeout(nof_secs);
554 }
555
GetTimeout(void) const556 size_t CDB_Connection::GetTimeout(void) const
557 {
558 CHECK_CONNECTION(m_ConnImpl);
559 return m_ConnImpl->GetTimeout();
560 }
561
GetCancelTimeout(void) const562 size_t CDB_Connection::GetCancelTimeout(void) const
563 {
564 CHECK_CONNECTION(m_ConnImpl);
565 return m_ConnImpl->GetCancelTimeout();
566 }
567
GetExtraFeatures(void)568 I_ConnectionExtra& CDB_Connection::GetExtraFeatures(void)
569 {
570 CHECK_CONNECTION(m_ConnImpl);
571 return *m_ConnImpl;
572 }
573
GetDriverName(void) const574 string CDB_Connection::GetDriverName(void) const
575 {
576 CHECK_CONNECTION(m_ConnImpl);
577 return m_ConnImpl->GetDriverName();
578 }
579
FinishOpening(void)580 void CDB_Connection::FinishOpening(void)
581 {
582 CHECK_CONNECTION(m_ConnImpl);
583 m_ConnImpl->FinishOpening();
584 }
585
586 ////////////////////////////////////////////////////////////////////////////
587 // CDB_Result::
588 //
589
CDB_Result(impl::CResult * r)590 CDB_Result::CDB_Result(impl::CResult* r) :
591 m_ResImpl(r)
592 {
593 CHECK_DRIVER_ERROR( !m_ResImpl, "No valid result provided", 200004 );
594
595 m_ResImpl->AttachTo(this);
596 }
597
598
599 #define CHECK_RESULT( res ) \
600 CHECK_DRIVER_WARNING( !res, "This result is not available anymore", 200003 )
601
602
ResultType() const603 EDB_ResType CDB_Result::ResultType() const
604 {
605 CHECK_RESULT( GetIResultPtr() );
606 return GetIResult().ResultType();
607 }
608
609
GetDefineParams(void) const610 const CDBParams& CDB_Result::GetDefineParams(void) const
611 {
612 CHECK_RESULT( GetIResultPtr() );
613 return GetIResult().GetDefineParams();
614 }
615
616
NofItems() const617 unsigned int CDB_Result::NofItems() const
618 {
619 CHECK_RESULT( GetIResultPtr() );
620 return GetIResult().GetDefineParams().GetNum();
621 }
622
623 const char*
ItemName(unsigned int item_num) const624 CDB_Result::ItemName(unsigned int item_num) const
625 {
626 CHECK_RESULT( GetIResultPtr() );
627
628 const string& name = GetIResult().GetDefineParams().GetName(item_num);
629
630 if (!name.empty()) {
631 return name.c_str();
632 }
633
634 return NULL;
635 }
636
ItemMaxSize(unsigned int item_num) const637 size_t CDB_Result::ItemMaxSize(unsigned int item_num) const
638 {
639 CHECK_RESULT( GetIResultPtr() );
640 return GetIResult().GetDefineParams().GetMaxSize(item_num);
641 }
642
ItemDataType(unsigned int item_num) const643 EDB_Type CDB_Result::ItemDataType(unsigned int item_num) const
644 {
645 CHECK_RESULT( GetIResultPtr() );
646 return GetIResult().GetDefineParams().GetDataType(item_num);
647 }
648
Fetch()649 bool CDB_Result::Fetch()
650 {
651 // An exception should be thrown from this place. We cannot omit this exception
652 // because it is expected by ftds driver in CursorResult::Fetch.
653 CHECK_RESULT( GetIResultPtr() );
654 // if ( !GetIResultPtr() ) {
655 // return false;
656 // }
657 return GetIResult().Fetch();
658 }
659
CurrentItemNo() const660 int CDB_Result::CurrentItemNo() const
661 {
662 CHECK_RESULT( GetIResultPtr() );
663 return GetIResult().CurrentItemNo();
664 }
665
GetColumnNum(void) const666 int CDB_Result::GetColumnNum(void) const
667 {
668 CHECK_RESULT( GetIResultPtr() );
669 return GetIResult().GetColumnNum();
670 }
671
GetItem(CDB_Object * item_buf,EGetItem policy)672 CDB_Object* CDB_Result::GetItem(CDB_Object* item_buf, EGetItem policy)
673 {
674 CHECK_RESULT( GetIResultPtr() );
675 return GetIResult().GetItem(item_buf, policy);
676 }
677
ReadItem(void * buffer,size_t buffer_size,bool * is_null)678 size_t CDB_Result::ReadItem(void* buffer, size_t buffer_size, bool* is_null)
679 {
680 CHECK_RESULT( GetIResultPtr() );
681 return GetIResult().ReadItem(buffer, buffer_size, is_null);
682 }
683
GetBlobDescriptor()684 I_BlobDescriptor* CDB_Result::GetBlobDescriptor()
685 {
686 CHECK_RESULT( GetIResultPtr() );
687 return GetIResult().GetBlobDescriptor();
688 }
689
SkipItem()690 bool CDB_Result::SkipItem()
691 {
692 CHECK_RESULT( GetIResultPtr() );
693 return GetIResult().SkipItem();
694 }
695
696
~CDB_Result()697 CDB_Result::~CDB_Result()
698 {
699 try {
700 if ( GetIResultPtr() ) {
701 GetIResult().Release();
702 }
703 }
704 NCBI_CATCH_ALL_X( 3, NCBI_CURRENT_FUNCTION )
705 }
706
707
708
709 ////////////////////////////////////////////////////////////////////////////
710 // CDB_LangCmd::
711 //
712
CDB_LangCmd(impl::CBaseCmd * c)713 CDB_LangCmd::CDB_LangCmd(impl::CBaseCmd* c)
714 {
715 CHECK_DRIVER_ERROR( !c, "No valid command provided", 200004 );
716
717 m_CmdImpl = c;
718 m_CmdImpl->AttachTo(this);
719 }
720
721
722 #define CHECK_COMMAND( cmd ) \
723 CHECK_DRIVER_WARNING( !cmd, "This command cannot be used anymore", 200005 )
724
725
More(const string & query_text)726 bool CDB_LangCmd::More(const string& query_text)
727 {
728 CHECK_COMMAND( m_CmdImpl );
729 return m_CmdImpl->More(query_text);
730 }
731
GetBindParams(void)732 CDBParams& CDB_LangCmd::GetBindParams(void)
733 {
734 CHECK_COMMAND( m_CmdImpl );
735 return m_CmdImpl->GetBindParams();
736 }
737
738
GetDefineParams(void)739 CDBParams& CDB_LangCmd::GetDefineParams(void)
740 {
741 CHECK_COMMAND( m_CmdImpl );
742 return m_CmdImpl->GetDefineParams();
743 }
744
Send()745 bool CDB_LangCmd::Send()
746 {
747 CHECK_COMMAND( m_CmdImpl );
748 TRACE_PARAMS(m_CmdImpl->GetBindParams());
749 m_CmdImpl->SaveInParams();
750 return m_CmdImpl->Send();
751 }
752
WasSent() const753 bool CDB_LangCmd::WasSent() const
754 {
755 CHECK_COMMAND( m_CmdImpl );
756 return m_CmdImpl->WasSent();
757 }
758
Cancel()759 bool CDB_LangCmd::Cancel()
760 {
761 CHECK_COMMAND( m_CmdImpl );
762 return m_CmdImpl->Cancel();
763 }
764
WasCanceled() const765 bool CDB_LangCmd::WasCanceled() const
766 {
767 CHECK_COMMAND( m_CmdImpl );
768 return m_CmdImpl->WasCanceled();
769 }
770
Result()771 CDB_Result* CDB_LangCmd::Result()
772 {
773 CHECK_COMMAND( m_CmdImpl );
774 return m_CmdImpl->Result();
775 }
776
HasMoreResults() const777 bool CDB_LangCmd::HasMoreResults() const
778 {
779 CHECK_COMMAND( m_CmdImpl );
780 return m_CmdImpl->HasMoreResults();
781 }
782
HasFailed() const783 bool CDB_LangCmd::HasFailed() const
784 {
785 CHECK_COMMAND( m_CmdImpl );
786 return m_CmdImpl->HasFailed();
787 }
788
RowCount() const789 int CDB_LangCmd::RowCount() const
790 {
791 CHECK_COMMAND( m_CmdImpl );
792 return m_CmdImpl->RowCount();
793 }
794
DumpResults()795 void CDB_LangCmd::DumpResults()
796 {
797 CHECK_COMMAND( m_CmdImpl );
798 m_CmdImpl->DumpResults();
799 }
800
~CDB_LangCmd()801 CDB_LangCmd::~CDB_LangCmd()
802 {
803 try {
804 if ( m_CmdImpl ) {
805 m_CmdImpl->Release();
806 }
807 }
808 NCBI_CATCH_ALL_X( 4, NCBI_CURRENT_FUNCTION )
809 }
810
811
812
813 /////////////////////////////////////////////////////////////////////////////
814 // CDB_RPCCmd::
815 //
816
CDB_RPCCmd(impl::CBaseCmd * c)817 CDB_RPCCmd::CDB_RPCCmd(impl::CBaseCmd* c)
818 {
819 CHECK_DRIVER_ERROR( !c, "No valid command provided", 200006 );
820 m_CmdImpl = c;
821 m_CmdImpl->AttachTo(this);
822 }
823
824
GetBindParams(void)825 CDBParams& CDB_RPCCmd::GetBindParams(void)
826 {
827 CHECK_COMMAND( m_CmdImpl );
828 return m_CmdImpl->GetBindParams();
829 }
830
831
GetDefineParams(void)832 CDBParams& CDB_RPCCmd::GetDefineParams(void)
833 {
834 CHECK_COMMAND( m_CmdImpl );
835 return m_CmdImpl->GetDefineParams();
836 }
837
838
Send()839 bool CDB_RPCCmd::Send()
840 {
841 CHECK_COMMAND( m_CmdImpl );
842 _TRACE("Calling remote procedure " << GetProcName());
843 TRACE_PARAMS(m_CmdImpl->GetBindParams());
844 m_CmdImpl->SaveInParams();
845 return m_CmdImpl->Send();
846 }
847
WasSent() const848 bool CDB_RPCCmd::WasSent() const
849 {
850 CHECK_COMMAND( m_CmdImpl );
851 return m_CmdImpl->WasSent();
852 }
853
Cancel()854 bool CDB_RPCCmd::Cancel()
855 {
856 CHECK_COMMAND( m_CmdImpl );
857 return m_CmdImpl->Cancel();
858 }
859
WasCanceled() const860 bool CDB_RPCCmd::WasCanceled() const
861 {
862 CHECK_COMMAND( m_CmdImpl );
863 return m_CmdImpl->WasCanceled();
864 }
865
Result()866 CDB_Result* CDB_RPCCmd::Result()
867 {
868 CHECK_COMMAND( m_CmdImpl );
869 return m_CmdImpl->Result();
870 }
871
HasMoreResults() const872 bool CDB_RPCCmd::HasMoreResults() const
873 {
874 CHECK_COMMAND( m_CmdImpl );
875 return m_CmdImpl->HasMoreResults();
876 }
877
HasFailed() const878 bool CDB_RPCCmd::HasFailed() const
879 {
880 CHECK_COMMAND( m_CmdImpl );
881 return m_CmdImpl->HasFailed();
882 }
883
RowCount() const884 int CDB_RPCCmd::RowCount() const
885 {
886 CHECK_COMMAND( m_CmdImpl );
887 return m_CmdImpl->RowCount();
888 }
889
DumpResults()890 void CDB_RPCCmd::DumpResults()
891 {
892 CHECK_COMMAND( m_CmdImpl );
893 m_CmdImpl->DumpResults();
894 }
895
SetRecompile(bool recompile)896 void CDB_RPCCmd::SetRecompile(bool recompile)
897 {
898 CHECK_COMMAND( m_CmdImpl );
899 m_CmdImpl->SetRecompile(recompile);
900 }
901
GetProcName(void) const902 const string& CDB_RPCCmd::GetProcName(void) const
903 {
904 CHECK_COMMAND( m_CmdImpl );
905 return m_CmdImpl->GetQuery();
906 }
907
908
~CDB_RPCCmd()909 CDB_RPCCmd::~CDB_RPCCmd()
910 {
911 try {
912 if ( m_CmdImpl ) {
913 m_CmdImpl->Release();
914 }
915 }
916 NCBI_CATCH_ALL_X( 5, NCBI_CURRENT_FUNCTION )
917 }
918
919
920
921 ////////////////////////////////////////////////////////////////////////////
922 // CDB_BCPInCmd::
923 //
924
CDB_BCPInCmd(impl::CBaseCmd * c)925 CDB_BCPInCmd::CDB_BCPInCmd(impl::CBaseCmd* c)
926 {
927 CHECK_DRIVER_ERROR( !c, "No valid command provided", 200007 );
928
929 m_CmdImpl = c;
930 m_CmdImpl->AttachTo(this);
931 }
932
933
SetHints(CTempString hints)934 void CDB_BCPInCmd::SetHints(CTempString hints)
935 {
936 CHECK_COMMAND( m_CmdImpl );
937 m_CmdImpl->SetHints(hints);
938 }
939
940
AddHint(EBCP_Hints hint,unsigned int value)941 void CDB_BCPInCmd::AddHint(EBCP_Hints hint, unsigned int value /* = 0 */)
942 {
943 CHECK_COMMAND( m_CmdImpl );
944 m_CmdImpl->AddHint(hint, value);
945 }
946
947
AddOrderHint(CTempString columns)948 void CDB_BCPInCmd::AddOrderHint(CTempString columns)
949 {
950 CHECK_COMMAND( m_CmdImpl );
951 m_CmdImpl->AddOrderHint(columns);
952 }
953
954
GetBindParams(void)955 CDBParams& CDB_BCPInCmd::GetBindParams(void)
956 {
957 CHECK_COMMAND( m_CmdImpl );
958 return m_CmdImpl->GetBindParams();
959 }
960
961
Bind(unsigned int column_num,CDB_Object * value)962 bool CDB_BCPInCmd::Bind(unsigned int column_num, CDB_Object* value)
963 {
964 GetBindParams().Bind(column_num, value);
965 return true;
966 }
967
968
SendRow()969 bool CDB_BCPInCmd::SendRow()
970 {
971 CHECK_COMMAND( m_CmdImpl );
972 if (m_CmdImpl->m_RowsSent++ == 0) {
973 TRACE_PARAMS(m_CmdImpl->GetBindParams());
974 } else if (m_CmdImpl->m_AtStartOfBatch) {
975 m_CmdImpl->m_RowsSentAtBatchStart = m_CmdImpl->m_RowsSent - 1;
976 }
977 m_CmdImpl->m_AtStartOfBatch = false;
978 m_CmdImpl->SaveInParams();
979 return m_CmdImpl->Send();
980 }
981
Cancel()982 bool CDB_BCPInCmd::Cancel()
983 {
984 CHECK_COMMAND( m_CmdImpl );
985 return m_CmdImpl->Cancel();
986 }
987
CompleteBatch()988 bool CDB_BCPInCmd::CompleteBatch()
989 {
990 CHECK_COMMAND( m_CmdImpl );
991 if (m_CmdImpl->m_BatchesSent++ == 0 && m_CmdImpl->m_RowsSent > 1) {
992 _TRACE("Sent a batch of " << m_CmdImpl->GetRowsInCurrentBatch()
993 << " rows");
994 }
995 m_CmdImpl->m_AtStartOfBatch = true;
996 return m_CmdImpl->CommitBCPTrans();
997 }
998
CompleteBCP()999 bool CDB_BCPInCmd::CompleteBCP()
1000 {
1001 CHECK_COMMAND( m_CmdImpl );
1002 if (m_CmdImpl->m_BatchesSent > 1) {
1003 _TRACE("Sent " << m_CmdImpl->m_RowsSent << " rows, in "
1004 << m_CmdImpl->m_BatchesSent << " batches");
1005 }
1006 return m_CmdImpl->EndBCP();
1007 }
1008
1009
~CDB_BCPInCmd()1010 CDB_BCPInCmd::~CDB_BCPInCmd()
1011 {
1012 try {
1013 if ( m_CmdImpl ) {
1014 m_CmdImpl->Release();
1015 }
1016 }
1017 NCBI_CATCH_ALL_X( 6, NCBI_CURRENT_FUNCTION )
1018 }
1019
1020
1021
1022 /////////////////////////////////////////////////////////////////////////////
1023 // CDB_CursorCmd::
1024 //
1025
CDB_CursorCmd(impl::CBaseCmd * c)1026 CDB_CursorCmd::CDB_CursorCmd(impl::CBaseCmd* c)
1027 {
1028 CHECK_DRIVER_ERROR( !c, "No valid command provided", 200006 );
1029 m_CmdImpl = c;
1030 m_CmdImpl->AttachTo(this);
1031 }
1032
1033
GetBindParams(void)1034 CDBParams& CDB_CursorCmd::GetBindParams(void)
1035 {
1036 CHECK_COMMAND( m_CmdImpl );
1037 return m_CmdImpl->GetBindParams();
1038 }
1039
1040
GetDefineParams(void)1041 CDBParams& CDB_CursorCmd::GetDefineParams(void)
1042 {
1043 CHECK_COMMAND( m_CmdImpl );
1044 return m_CmdImpl->GetDefineParams();
1045 }
1046
1047
Open()1048 CDB_Result* CDB_CursorCmd::Open()
1049 {
1050 CHECK_COMMAND( m_CmdImpl );
1051 TRACE_PARAMS(m_CmdImpl->GetBindParams());
1052 return m_CmdImpl->OpenCursor();
1053 }
1054
Update(const string & table_name,const string & upd_query)1055 bool CDB_CursorCmd::Update(const string& table_name, const string& upd_query)
1056 {
1057 CHECK_COMMAND( m_CmdImpl );
1058 m_CmdImpl->SaveInParams();
1059 return m_CmdImpl->Update(table_name, upd_query);
1060 }
1061
UpdateBlob(unsigned int item_num,CDB_Stream & data,bool log_it)1062 bool CDB_CursorCmd::UpdateBlob(unsigned int item_num, CDB_Stream& data,
1063 bool log_it)
1064 {
1065 CHECK_COMMAND( m_CmdImpl );
1066 return m_CmdImpl->UpdateBlob(item_num, data, log_it);
1067 }
1068
SendDataCmd(unsigned int item_num,size_t size,bool log_it,bool dump_results)1069 CDB_SendDataCmd* CDB_CursorCmd::SendDataCmd(unsigned int item_num,
1070 size_t size,
1071 bool log_it,
1072 bool dump_results)
1073 {
1074 CHECK_COMMAND( m_CmdImpl );
1075 return m_CmdImpl->SendDataCmd(item_num, size, log_it, dump_results);
1076 }
1077
1078
Delete(const string & table_name)1079 bool CDB_CursorCmd::Delete(const string& table_name)
1080 {
1081 CHECK_COMMAND( m_CmdImpl );
1082 return m_CmdImpl->Delete(table_name);
1083 }
1084
RowCount() const1085 int CDB_CursorCmd::RowCount() const
1086 {
1087 CHECK_COMMAND( m_CmdImpl );
1088 return m_CmdImpl->RowCount();
1089 }
1090
Close()1091 bool CDB_CursorCmd::Close()
1092 {
1093 CHECK_COMMAND( m_CmdImpl );
1094 return m_CmdImpl->CloseCursor();
1095 }
1096
1097
~CDB_CursorCmd()1098 CDB_CursorCmd::~CDB_CursorCmd()
1099 {
1100 try {
1101 if ( m_CmdImpl ) {
1102 m_CmdImpl->Release();
1103 }
1104 }
1105 NCBI_CATCH_ALL_X( 7, NCBI_CURRENT_FUNCTION )
1106 }
1107
1108
1109
1110 /////////////////////////////////////////////////////////////////////////////
1111 // CDB_SendDataCmd::
1112 //
1113
CDB_SendDataCmd(impl::CSendDataCmd * c)1114 CDB_SendDataCmd::CDB_SendDataCmd(impl::CSendDataCmd* c)
1115 {
1116 CHECK_DRIVER_ERROR( !c, "No valid command provided", 200006 );
1117
1118 m_CmdImpl = c;
1119 m_CmdImpl->AttachTo(this);
1120 }
1121
1122
SendChunk(const void * pChunk,size_t nofBytes)1123 size_t CDB_SendDataCmd::SendChunk(const void* pChunk, size_t nofBytes)
1124 {
1125 CHECK_DRIVER_WARNING( !m_CmdImpl, "This command cannot be used anymore", 200005 );
1126
1127 return m_CmdImpl->SendChunk(pChunk, nofBytes);
1128 }
1129
1130
Cancel(void)1131 bool CDB_SendDataCmd::Cancel(void)
1132 {
1133 CHECK_DRIVER_WARNING( !m_CmdImpl, "This command cannot be used anymore", 200005 );
1134
1135 return m_CmdImpl->Cancel();
1136 }
1137
Result()1138 CDB_Result* CDB_SendDataCmd::Result()
1139 {
1140 CHECK_COMMAND( m_CmdImpl );
1141 return m_CmdImpl->Result();
1142 }
1143
HasMoreResults() const1144 bool CDB_SendDataCmd::HasMoreResults() const
1145 {
1146 CHECK_COMMAND( m_CmdImpl );
1147 return m_CmdImpl->HasMoreResults();
1148 }
1149
DumpResults()1150 void CDB_SendDataCmd::DumpResults()
1151 {
1152 CHECK_COMMAND( m_CmdImpl );
1153 m_CmdImpl->DumpResults();
1154 }
1155
~CDB_SendDataCmd()1156 CDB_SendDataCmd::~CDB_SendDataCmd()
1157 {
1158 try {
1159 if ( m_CmdImpl ) {
1160 m_CmdImpl->Release();
1161 }
1162 }
1163 NCBI_CATCH_ALL_X( 8, NCBI_CURRENT_FUNCTION )
1164 }
1165
1166
1167
1168 /////////////////////////////////////////////////////////////////////////////
1169 // CDB_BlobDescriptor::
1170 //
1171
CDB_BlobDescriptor(const string & table_name,const string & column_name,const string & search_conditions,ETDescriptorType column_type,ETriState has_legacy_type)1172 CDB_BlobDescriptor::CDB_BlobDescriptor(const string& table_name,
1173 const string& column_name,
1174 const string& search_conditions,
1175 ETDescriptorType column_type,
1176 ETriState has_legacy_type)
1177 : m_TableName(table_name)
1178 , m_ColumnName(column_name)
1179 , m_SearchConditions(search_conditions)
1180 , m_ColumnType(column_type)
1181 , m_HasLegacyType(has_legacy_type)
1182 {
1183 }
1184
~CDB_BlobDescriptor()1185 CDB_BlobDescriptor::~CDB_BlobDescriptor()
1186 {
1187 }
1188
DescriptorType() const1189 int CDB_BlobDescriptor::DescriptorType() const
1190 {
1191 return 0;
1192 }
1193
1194
1195
1196 /////////////////////////////////////////////////////////////////////////////
1197 // CDB_ResultProcessor::
1198 //
1199
CDB_ResultProcessor(CDB_Connection * c)1200 CDB_ResultProcessor::CDB_ResultProcessor(CDB_Connection* c) :
1201 m_Con(NULL),
1202 m_Prev(NULL),
1203 m_Next(NULL)
1204 {
1205 SetConn(c);
1206 }
1207
ProcessResult(CDB_Result & res)1208 void CDB_ResultProcessor::ProcessResult(CDB_Result& res)
1209 {
1210 while (res.Fetch()) // fetch and forget
1211 continue;
1212 }
1213
1214
~CDB_ResultProcessor()1215 CDB_ResultProcessor::~CDB_ResultProcessor()
1216 {
1217 try {
1218 if ( m_Con ) {
1219 m_Con->SetResultProcessor(m_Prev);
1220 }
1221
1222 if(m_Prev) {
1223 m_Prev->m_Next = m_Next;
1224 }
1225
1226 if(m_Next) {
1227 m_Next->m_Prev = m_Prev;
1228 }
1229 }
1230 NCBI_CATCH_ALL_X( 9, NCBI_CURRENT_FUNCTION )
1231 }
1232
SetConn(CDB_Connection * c)1233 void CDB_ResultProcessor::SetConn(CDB_Connection* c)
1234 {
1235 // Clear previously used connection ...
1236 if ( m_Con ) {
1237 m_Con->SetResultProcessor(NULL);
1238 }
1239
1240 m_Con = c;
1241
1242 if ( m_Con ) {
1243 _ASSERT(m_Prev == NULL);
1244 m_Prev = m_Con->SetResultProcessor(this);
1245
1246 if (m_Prev) {
1247 _ASSERT(m_Prev->m_Next == NULL);
1248 m_Prev->m_Next = this;
1249 }
1250 }
1251 }
1252
ReleaseConn(void)1253 void CDB_ResultProcessor::ReleaseConn(void)
1254 {
1255 m_Con = NULL;
1256 }
1257
1258
1259 ////////////////////////////////////////////////////////////////////////////////
CAutoTrans(const CSubject & subject)1260 CAutoTrans::CAutoTrans(const CSubject& subject)
1261 : m_Abort(true)
1262 , m_Conn(subject.m_Connection)
1263 , m_TranCount(0)
1264 {
1265 BeginTransaction();
1266 m_TranCount = GetTranCount();
1267 if (m_TranCount > 1) {
1268 m_SavepointName = "ncbi_dbapi_txn_"
1269 + NStr::NumericToString(reinterpret_cast<intptr_t>(this), 0, 16);
1270 unique_ptr<CDB_LangCmd> auto_stmt
1271 (m_Conn.LangCmd("SAVE TRANSACTION " + m_SavepointName));
1272 auto_stmt->Send();
1273 auto_stmt->DumpResults();
1274 }
1275 }
1276
1277
~CAutoTrans(void)1278 CAutoTrans::~CAutoTrans(void)
1279 {
1280 try
1281 {
1282 const int curr_TranCount = GetTranCount();
1283
1284 if (curr_TranCount >= m_TranCount) {
1285 if (curr_TranCount > m_TranCount) {
1286 // A nested transaction is started and not finished yet ...
1287 ERR_POST_X(1, Warning << "A nested transaction was started and "
1288 "it is not finished yet.");
1289 }
1290
1291 // Assume that we are on the same level of transaction nesting.
1292 if(m_Abort) {
1293 Rollback();
1294 } else {
1295 Commit();
1296 }
1297 }
1298 m_Conn.m_HasTransaction = (curr_TranCount <= 1);
1299
1300 // Skip commit/rollback if this transaction was previously
1301 // explicitly finished ...
1302 }
1303 NCBI_CATCH_ALL_X( 10, NCBI_CURRENT_FUNCTION )
1304 }
1305
1306 void
BeginTransaction(void)1307 CAutoTrans::BeginTransaction(void)
1308 {
1309 m_Conn.m_HasTransaction = true;
1310 unique_ptr<CDB_LangCmd> auto_stmt(m_Conn.LangCmd("BEGIN TRANSACTION"));
1311 auto_stmt->Send();
1312 auto_stmt->DumpResults();
1313 }
1314
1315
1316 void
Commit(void)1317 CAutoTrans::Commit(void)
1318 {
1319 unique_ptr<CDB_LangCmd> auto_stmt(m_Conn.LangCmd("COMMIT"));
1320 auto_stmt->Send();
1321 auto_stmt->DumpResults();
1322 }
1323
1324
1325 void
Rollback(void)1326 CAutoTrans::Rollback(void)
1327 {
1328 unique_ptr<CDB_LangCmd> auto_stmt
1329 (m_Conn.LangCmd("ROLLBACK TRANSACTION " + m_SavepointName));
1330 auto_stmt->Send();
1331 auto_stmt->DumpResults();
1332 if (m_SavepointName.empty()) {
1333 _ASSERT(m_TranCount == 1);
1334 } else {
1335 // Formally unwind via an empty commit, as a rollback would
1336 // also cancel outer transactions.
1337 Commit();
1338 }
1339 }
1340
1341
1342 int
GetTranCount(void)1343 CAutoTrans::GetTranCount(void)
1344 {
1345 int result = 0;
1346 unique_ptr<CDB_LangCmd> auto_stmt(m_Conn.LangCmd("SELECT @@trancount as tc"));
1347
1348 if (auto_stmt->Send()) {
1349 while(auto_stmt->HasMoreResults()) {
1350 unique_ptr<CDB_Result> rs(auto_stmt->Result());
1351
1352 if (rs.get() == NULL) {
1353 continue;
1354 }
1355
1356 if (rs->ResultType() != eDB_RowResult) {
1357 continue;
1358 }
1359
1360 if (rs->Fetch()) {
1361 CDB_Int tran_count;
1362 rs->GetItem(&tran_count);
1363 result = tran_count.Value();
1364 }
1365
1366 while(rs->Fetch()) {
1367 }
1368 }
1369 }
1370
1371 return result;
1372 }
1373
1374
1375 END_NCBI_SCOPE
1376
1377
1378