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*>(&params)) {
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