1 /* $Id: sdbapi.cpp 633613 2021-06-22 17:38:34Z ivanov $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Pavel Ivanov
27  *
28  */
29 
30 #include <ncbi_pch.hpp>
31 
32 #include <corelib/ncbi_safe_static.hpp>
33 #include <connect/ncbi_core_cxx.hpp>
34 
35 #include <dbapi/driver/dbapi_driver_conn_params.hpp>
36 #include <dbapi/driver/dbapi_svc_mapper.hpp>
37 #include <dbapi/driver/drivers.hpp>
38 #include <dbapi/driver/impl/dbapi_impl_context.hpp>
39 #include <dbapi/driver/util/blobstore.hpp>
40 #include <dbapi/error_codes.hpp>
41 
42 #include "sdbapi_impl.hpp"
43 #include "../rw_impl.hpp"
44 
45 
46 #define NCBI_USE_ERRCODE_X  Dbapi_Sdbapi
47 
48 
49 #ifdef HAVE_LIBCONNEXT
50 #  include "sdb_decryptor.cpp"
51 #endif
52 
53 
54 BEGIN_NCBI_SCOPE
55 
56 
57 #define SDBAPI_CATCH_LOWLEVEL()                             \
58     catch (CDB_DeadlockEx& ex) {                            \
59         NCBI_RETHROW(ex, CSDB_DeadlockException, eLowLevel, ""); \
60     } catch (CDB_Exception& ex) {                           \
61         NCBI_RETHROW(ex, CSDB_Exception, eLowLevel, "");    \
62     }
63 
64 #define SDBAPI_THROW(code, msg) \
65     NCBI_THROW(CSDB_Exception, code, \
66                CDB_Exception::SMessageInContext(msg, x_GetContext()))
67 
68 
69 
70 const char*
GetErrCodeString(void) const71 CSDB_Exception::GetErrCodeString(void) const
72 {
73     switch (GetErrCode()) {
74     case eURLFormat:    return "eURLFormat";
75     case eClosed:       return "eClosed";
76     case eStarted:      return "eStarted";
77     case eNotInOrder:   return "eNotInOrder";
78     case eInconsistent: return "eInconsistent";
79     case eUnsupported:  return "eUnsupported";
80     case eNotExist:     return "eNotExist";
81     case eOutOfBounds:  return "eOutOfBounds";
82     case eLowLevel:     return "eLowLevel";
83     case eWrongParams:  return "eWrongParams";
84     default:            return CException::GetErrCodeString();
85     }
86 }
87 
88 static CSafeStatic<CDB_Exception::SContext> kEmptyContext;
89 
ReportExtra(ostream & os) const90 void CSDB_Exception::ReportExtra(ostream& os) const
91 {
92     os << *m_Context;
93 }
94 
x_Init(const CDiagCompileInfo &,const string &,const CException * prev,EDiagSev)95 void CSDB_Exception::x_Init(const CDiagCompileInfo&, const string&,
96                             const CException* prev, EDiagSev)
97 {
98     const CDB_Exception* dbex = dynamic_cast<const CDB_Exception*>(prev);
99     if (dbex == NULL) {
100         if (m_Context.Empty()) {
101             m_Context.Reset(&kEmptyContext.Get());
102         }
103     } else if (m_Context.Empty()) {
104         m_Context.Reset(&dbex->GetContext());
105     } else {
106         const_cast<CDB_Exception::SContext&>(*m_Context)
107             .UpdateFrom(dbex->GetContext());
108     }
109 }
110 
x_Assign(const CException & src)111 void CSDB_Exception::x_Assign(const CException& src)
112 {
113     CException::x_Assign(src);
114     const CSDB_Exception* sdb_src = dynamic_cast<const CSDB_Exception*>(&src);
115     _ASSERT(sdb_src != NULL);
116     m_Context = sdb_src->m_Context;
117 }
118 
GetRetriable(void) const119 ERetriable CSDB_Exception::GetRetriable(void) const
120 {
121     ERetriable  retriable = CException::GetRetriable();
122 
123     if (retriable == eRetriable_Unknown) {
124         const CException*   predecessor = GetPredecessor();
125         if (predecessor)
126             return predecessor->GetRetriable();
127     }
128     return retriable;
129 }
130 
131 
132 static EDB_Type
s_ConvertType(ESDB_Type type)133 s_ConvertType(ESDB_Type type)
134 {
135     switch (type)
136     {
137     case eSDB_Byte:
138         return eDB_TinyInt;
139     case eSDB_Short:
140         return eDB_SmallInt;
141     case eSDB_Int4:
142         return eDB_Int;
143     case eSDB_Int8:
144         return eDB_BigInt;
145     case eSDB_Float:
146         return eDB_Float;
147     case eSDB_Double:
148         return eDB_Double;
149     case eSDB_String:
150     case eSDB_StringUCS2:
151         return eDB_VarChar;
152     case eSDB_Binary:
153         return eDB_VarBinary;
154     case eSDB_DateTime:
155         return eDB_DateTime;
156     case eSDB_BigDateTime:
157         return eDB_BigDateTime;
158     case eSDB_Text:
159     case eSDB_TextUCS2:
160         return eDB_Text;
161     case eSDB_Image:
162         return eDB_Image;
163     case eSDB_StringMax:
164     case eSDB_StringMaxUCS2:
165         return eDB_VarCharMax;
166     case eSDB_BinaryMax:
167         return eDB_VarBinaryMax;
168     case eSDB_Bit:
169         return eDB_Bit;
170     }
171     return eDB_UnsupportedType;
172 }
173 
174 static ESDB_Type
s_ConvertType(EDB_Type type)175 s_ConvertType(EDB_Type type)
176 {
177     switch (type) {
178     case eDB_Int:
179         return eSDB_Int4;
180     case eDB_SmallInt:
181         return eSDB_Short;
182     case eDB_TinyInt:
183         return eSDB_Byte;
184     case eDB_BigInt:
185         return eSDB_Int8;
186     case eDB_Numeric:
187     case eDB_VarChar:
188     case eDB_Char:
189     case eDB_LongChar:
190         return eSDB_String;
191     case eDB_VarBinary:
192     case eDB_Binary:
193     case eDB_LongBinary:
194         return eSDB_Binary;
195     case eDB_Float:
196         return eSDB_Float;
197     case eDB_Double:
198         return eSDB_Double;
199     case eDB_DateTime:
200     case eDB_SmallDateTime:
201         return eSDB_DateTime;
202     case eDB_BigDateTime:
203         return eSDB_BigDateTime;
204     case eDB_Text:
205         return eSDB_Text;
206     case eDB_Image:
207         return eSDB_Image;
208     case eDB_VarCharMax:
209         return eSDB_StringMax;
210     case eDB_VarBinaryMax:
211         return eSDB_BinaryMax;
212     case eDB_Bit:
213         return eSDB_Bit;
214     case eDB_UnsupportedType:
215         break;
216     }
217     _ASSERT(false);
218     return eSDB_String;
219 }
220 
221 static IBulkInsert::EHints
s_ConvertHints(CBulkInsert::EHintsWithValue hints)222 s_ConvertHints(CBulkInsert::EHintsWithValue hints)
223 {
224     switch (hints) {
225     case CBulkInsert::eRowsPerBatch:      return IBulkInsert::eRowsPerBatch;
226     case CBulkInsert::eKilobytesPerBatch: return IBulkInsert::eKilobytesPerBatch;
227     }
228     _ASSERT(false);
229     return IBulkInsert::EHints(hints);
230 }
231 
232 static IBulkInsert::EHints
s_ConvertHints(CBulkInsert::EHints hints)233 s_ConvertHints(CBulkInsert::EHints hints)
234 {
235     switch (hints) {
236     case CBulkInsert::eTabLock:          return IBulkInsert::eTabLock;
237     case CBulkInsert::eCheckConstraints: return IBulkInsert::eCheckConstraints;
238     case CBulkInsert::eFireTriggers:     return IBulkInsert::eFireTriggers;
239     }
240     _ASSERT(false);
241     return IBulkInsert::EHints(hints);
242 }
243 
244 static void
s_ConvertionNotSupported(const char * one_type,EDB_Type other_type)245 s_ConvertionNotSupported(const char* one_type, EDB_Type other_type)
246 {
247     NCBI_THROW(CSDB_Exception, eUnsupported | Retriable(eRetriable_No),
248                "Conversion between " + string(one_type) + " and "
249                + CDB_Object::GetTypeName(other_type, false)
250                + " is not supported");
251 }
252 
253 #ifdef NCBI_COMPILER_WORKSHOP
254 #define CONVERTVALUE_STATIC
255 #else
256 #define CONVERTVALUE_STATIC static
257 #endif
258 
259 CONVERTVALUE_STATIC void
s_ConvertValue(const CTime & from_val,CVariant & to_var)260 s_ConvertValue(const CTime& from_val, CVariant& to_var)
261 {
262     switch (to_var.GetType()) {
263     case eDB_DateTime:
264     case eDB_SmallDateTime:
265     case eDB_BigDateTime:
266         to_var = from_val;
267         break;
268     case eDB_VarChar:
269     case eDB_Char:
270     case eDB_LongChar:
271         to_var = from_val.AsString();
272         break;
273     case eDB_Text:
274     case eDB_VarCharMax:
275         {
276             string str_val = from_val.AsString();
277             to_var.Truncate();
278             to_var.Append(str_val.data(), str_val.size());
279             break;
280         }
281     default:
282         s_ConvertionNotSupported("CTime", to_var.GetType());
283     }
284 }
285 
286 CONVERTVALUE_STATIC void
s_ConvertValue(Int8 from_val,CVariant & to_var)287 s_ConvertValue(Int8 from_val, CVariant& to_var)
288 {
289     switch (to_var.GetType()) {
290     case eDB_BigInt:
291         to_var = Int8(from_val);
292         break;
293     case eDB_VarChar:
294     case eDB_Char:
295     case eDB_LongChar:
296         to_var = NStr::Int8ToString(from_val);
297         break;
298     case eDB_Text:
299     case eDB_VarCharMax:
300         {
301             string str_val = NStr::Int8ToString(from_val);
302             to_var.Truncate();
303             to_var.Append(str_val.data(), str_val.size());
304             break;
305         }
306     default:
307         s_ConvertionNotSupported("Int8", to_var.GetType());
308     }
309 }
310 
311 CONVERTVALUE_STATIC void
s_ConvertValue(Int4 from_val,CVariant & to_var)312 s_ConvertValue(Int4 from_val, CVariant& to_var)
313 {
314     switch (to_var.GetType()) {
315     case eDB_BigInt:
316         to_var = Int8(from_val);
317         break;
318     case eDB_Int:
319         to_var = Int4(from_val);
320         break;
321     case eDB_VarChar:
322     case eDB_Char:
323     case eDB_LongChar:
324         to_var = NStr::IntToString(from_val);
325         break;
326     case eDB_Text:
327     case eDB_VarCharMax:
328         {
329             string str_val = NStr::IntToString(from_val);
330             to_var.Truncate();
331             to_var.Append(str_val.data(), str_val.size());
332             break;
333         }
334     default:
335         s_ConvertionNotSupported("Int4", to_var.GetType());
336     }
337 }
338 
339 CONVERTVALUE_STATIC void
s_ConvertValue(short from_val,CVariant & to_var)340 s_ConvertValue(short from_val, CVariant& to_var)
341 {
342     switch (to_var.GetType()) {
343     case eDB_BigInt:
344         to_var = Int8(from_val);
345         break;
346     case eDB_Int:
347         to_var = Int4(from_val);
348         break;
349     case eDB_SmallInt:
350         to_var = Int2(from_val);
351         break;
352     case eDB_VarChar:
353     case eDB_Char:
354     case eDB_LongChar:
355         to_var = NStr::IntToString(from_val);
356         break;
357     case eDB_Text:
358     case eDB_VarCharMax:
359         {
360             string str_val = NStr::IntToString(from_val);
361             to_var.Truncate();
362             to_var.Append(str_val.data(), str_val.size());
363             break;
364         }
365     default:
366         s_ConvertionNotSupported("short", to_var.GetType());
367     }
368 }
369 
370 CONVERTVALUE_STATIC void
s_ConvertValue(unsigned char from_val,CVariant & to_var)371 s_ConvertValue(unsigned char from_val, CVariant& to_var)
372 {
373     switch (to_var.GetType()) {
374     case eDB_BigInt:
375         to_var = Int8(from_val);
376         break;
377     case eDB_Int:
378         to_var = Int4(from_val);
379         break;
380     case eDB_SmallInt:
381         to_var = Int2(from_val);
382         break;
383     case eDB_TinyInt:
384         to_var = Uint1(from_val);
385         break;
386     case eDB_VarChar:
387     case eDB_Char:
388     case eDB_LongChar:
389         to_var = NStr::IntToString(from_val);
390         break;
391     case eDB_Text:
392     case eDB_VarCharMax:
393         {
394             string str_val = NStr::IntToString(from_val);
395             to_var.Truncate();
396             to_var.Append(str_val.data(), str_val.size());
397             break;
398         }
399     default:
400         s_ConvertionNotSupported("unsigned char", to_var.GetType());
401     }
402 }
403 
404 CONVERTVALUE_STATIC void
s_ConvertValue(bool from_val,CVariant & to_var)405 s_ConvertValue(bool from_val, CVariant& to_var)
406 {
407     switch (to_var.GetType()) {
408     case eDB_BigInt:
409         to_var = Int8(from_val);
410         break;
411     case eDB_Int:
412         to_var = Int4(from_val);
413         break;
414     case eDB_SmallInt:
415         to_var = Int2(from_val);
416         break;
417     case eDB_TinyInt:
418         to_var = Uint1(from_val);
419         break;
420     case eDB_Bit:
421         to_var = bool(from_val);
422         break;
423     case eDB_VarChar:
424     case eDB_Char:
425     case eDB_LongChar:
426         to_var = NStr::BoolToString(from_val);
427         break;
428     case eDB_Text:
429     case eDB_VarCharMax:
430         {
431             string str_val = NStr::BoolToString(from_val);
432             to_var.Truncate();
433             to_var.Append(str_val.data(), str_val.size());
434             break;
435         }
436     default:
437         s_ConvertionNotSupported("bool", to_var.GetType());
438     }
439 }
440 
441 CONVERTVALUE_STATIC void
s_ConvertValue(const float & from_val,CVariant & to_var)442 s_ConvertValue(const float& from_val, CVariant& to_var)
443 {
444     switch (to_var.GetType()) {
445     case eDB_Float:
446         to_var = float(from_val);
447         break;
448     case eDB_Double:
449         to_var = double(from_val);
450         break;
451     case eDB_VarChar:
452     case eDB_Char:
453     case eDB_LongChar:
454         to_var = NStr::DoubleToString(from_val);
455         break;
456     case eDB_Text:
457     case eDB_VarCharMax:
458         {
459             string str_val = NStr::DoubleToString(from_val);
460             to_var.Truncate();
461             to_var.Append(str_val.data(), str_val.size());
462             break;
463         }
464     default:
465         s_ConvertionNotSupported("float", to_var.GetType());
466     }
467 }
468 
469 CONVERTVALUE_STATIC void
s_ConvertValue(const double & from_val,CVariant & to_var)470 s_ConvertValue(const double& from_val, CVariant& to_var)
471 {
472     switch (to_var.GetType()) {
473     case eDB_Double:
474         to_var = double(from_val);
475         break;
476     case eDB_VarChar:
477     case eDB_Char:
478     case eDB_LongChar:
479         to_var = NStr::DoubleToString(from_val);
480         break;
481     case eDB_Text:
482     case eDB_VarCharMax:
483         {
484             string str_val = NStr::DoubleToString(from_val);
485             to_var.Truncate();
486             to_var.Append(str_val.data(), str_val.size());
487             break;
488         }
489     default:
490         s_ConvertionNotSupported("double", to_var.GetType());
491     }
492 }
493 
494 CONVERTVALUE_STATIC void
s_ConvertValue(const string & from_val,CVariant & to_var)495 s_ConvertValue(const string& from_val, CVariant& to_var)
496 {
497     switch (to_var.GetType()) {
498     case eDB_Char:
499     case eDB_VarChar:
500     case eDB_LongChar:
501         to_var = from_val;
502         break;
503     case eDB_Binary:
504     case eDB_LongBinary:
505     case eDB_VarBinary:
506         to_var = CVariant::VarBinary(from_val.data(), from_val.size());
507         break;
508     case eDB_Text:
509     case eDB_VarCharMax:
510         to_var.Truncate();
511         to_var.Append(from_val);
512         break;
513     case eDB_Image:
514     case eDB_VarBinaryMax:
515         to_var.Truncate();
516         to_var.Append(from_val.data(), from_val.size());
517         break;
518     case eDB_Bit:
519         to_var = NStr::StringToBool(from_val);
520         break;
521     case eDB_Int:
522         to_var = NStr::StringToInt(from_val);
523         break;
524     case eDB_SmallInt:
525         to_var = NStr::StringToNumeric<Int2>(from_val);
526         break;
527     case eDB_TinyInt:
528         to_var = NStr::StringToNumeric<Uint1>(from_val);
529         break;
530     case eDB_BigInt:
531         to_var = NStr::StringToInt8(from_val);
532         break;
533     case eDB_Double:
534         to_var = NStr::StringToDouble(from_val);
535         break;
536     case eDB_DateTime:
537     case eDB_SmallDateTime:
538     case eDB_BigDateTime:
539         to_var = CTime(from_val);
540         break;
541     default:
542         s_ConvertionNotSupported("string", to_var.GetType());
543     }
544 }
545 
546 CONVERTVALUE_STATIC void
s_ConvertValue(const char * from_val,CVariant & to_var)547 s_ConvertValue(const char* from_val, CVariant& to_var)
548 {
549     s_ConvertValue(string(from_val), to_var);
550 }
551 
552 CONVERTVALUE_STATIC void
s_ConvertValue(const TStringUCS2 & from_val,CVariant & to_var)553 s_ConvertValue(const TStringUCS2& from_val, CVariant& to_var)
554 {
555     switch (to_var.GetType()) {
556     case eDB_Char:
557     case eDB_VarChar:
558     case eDB_LongChar:
559         to_var = from_val;
560         break;
561     case eDB_Binary:
562     case eDB_LongBinary:
563     case eDB_VarBinary:
564         s_ConvertValue(string(reinterpret_cast<const char*>(from_val.data()),
565                               from_val.size() * sizeof(TCharUCS2)),
566                        to_var);
567         break;
568     case eDB_Text:
569     case eDB_VarCharMax:
570         to_var.Truncate();
571         to_var.Append(from_val);
572         break;
573     case eDB_Image:
574     case eDB_VarBinaryMax:
575         to_var.Truncate();
576         to_var.Append(reinterpret_cast<const char*>(from_val.data()),
577                       from_val.size() * sizeof(TCharUCS2));
578         break;
579     case eDB_Bit:
580     case eDB_Int:
581     case eDB_BigInt:
582     case eDB_Double:
583     case eDB_DateTime:
584     case eDB_SmallDateTime:
585     case eDB_BigDateTime:
586         s_ConvertValue(CUtf8::AsUTF8(from_val), to_var);
587         break;
588     default:
589         s_ConvertionNotSupported("UCS2 string", to_var.GetType());
590     }
591 }
592 
593 #undef CONVERTVALUE_STATIC
594 
595 static void
s_ConvertValue(const CVariant & from_var,CTime & to_val)596 s_ConvertValue(const CVariant& from_var, CTime& to_val)
597 {
598     switch (from_var.GetType()) {
599     case eDB_DateTime:
600     case eDB_SmallDateTime:
601     case eDB_BigDateTime:
602         to_val = from_var.GetCTime();
603         break;
604     case eDB_VarChar:
605     case eDB_Char:
606     case eDB_LongChar:
607     case eDB_Text:
608     case eDB_VarCharMax:
609     {
610         string s = from_var.GetString();
611         try {
612             to_val = CTime(s);
613         } catch (CTimeException&) {
614             auto id = CDB_BigDateTime::Identify(s);
615             to_val = CTime(s, CDB_BigDateTime::GetTimeFormat(id.first,
616                                                              id.second));
617         }
618         break;
619     }
620     default:
621         s_ConvertionNotSupported("CTime", from_var.GetType());
622     }
623 }
624 
625 static void
s_ConvertValue(const CVariant & from_var,Int8 & to_val)626 s_ConvertValue(const CVariant& from_var, Int8& to_val)
627 {
628     switch (from_var.GetType()) {
629     case eDB_BigInt:
630     case eDB_Int:
631     case eDB_SmallInt:
632     case eDB_TinyInt:
633         to_val = from_var.GetInt8();
634         break;
635     case eDB_Bit:
636         to_val = Int8(from_var.GetBit());
637         break;
638     case eDB_VarChar:
639     case eDB_Char:
640     case eDB_LongChar:
641     case eDB_Text:
642     case eDB_VarCharMax:
643         to_val = NStr::StringToInt8(from_var.GetString());
644         break;
645     default:
646         s_ConvertionNotSupported("Int8", from_var.GetType());
647     }
648 }
649 
650 static void
s_ConvertValue(const CVariant & from_var,Int4 & to_val)651 s_ConvertValue(const CVariant& from_var, Int4& to_val)
652 {
653     Int8 temp_val;
654     switch (from_var.GetType()) {
655     case eDB_BigInt:
656         temp_val = from_var.GetInt8();
657         break;
658     case eDB_Int:
659     case eDB_SmallInt:
660     case eDB_TinyInt:
661         to_val = from_var.GetInt4();
662         return;
663     case eDB_Bit:
664         to_val = Int4(from_var.GetBit());
665         return;
666     case eDB_VarChar:
667     case eDB_Char:
668     case eDB_LongChar:
669     case eDB_Text:
670     case eDB_VarCharMax:
671         temp_val = NStr::StringToInt8(from_var.GetString());
672         break;
673     default:
674         s_ConvertionNotSupported("Int4", from_var.GetType());
675         return;
676     }
677     if (temp_val < numeric_limits<Int4>::min()
678         ||  temp_val > numeric_limits<Int4>::max())
679     {
680         NCBI_THROW(CSDB_Exception, eOutOfBounds | Retriable(eRetriable_No),
681                    "Value for Int4 is out of bounds: "
682                    + NStr::NumericToString(temp_val));
683     }
684     to_val = Int4(temp_val);
685 }
686 
687 static void
s_ConvertValue(const CVariant & from_var,short & to_val)688 s_ConvertValue(const CVariant& from_var, short& to_val)
689 {
690     Int8 temp_val;
691     switch (from_var.GetType()) {
692     case eDB_BigInt:
693     case eDB_Int:
694         temp_val = from_var.GetInt8();
695         break;
696     case eDB_SmallInt:
697     case eDB_TinyInt:
698         to_val = from_var.GetInt2();
699         return;
700     case eDB_Bit:
701         to_val = Int2(from_var.GetBit());
702         return;
703     case eDB_VarChar:
704     case eDB_Char:
705     case eDB_LongChar:
706     case eDB_Text:
707     case eDB_VarCharMax:
708         temp_val = NStr::StringToInt8(from_var.GetString());
709         break;
710     default:
711         s_ConvertionNotSupported("short", from_var.GetType());
712         return;
713     }
714     if (temp_val < numeric_limits<short>::min()
715         ||  temp_val > numeric_limits<short>::max())
716     {
717         NCBI_THROW(CSDB_Exception, eOutOfBounds | Retriable(eRetriable_No),
718                    "Value for short is out of bounds: "
719                    + NStr::NumericToString(temp_val));
720     }
721     to_val = short(temp_val);
722 }
723 
724 static void
s_ConvertValue(const CVariant & from_var,unsigned char & to_val)725 s_ConvertValue(const CVariant& from_var, unsigned char& to_val)
726 {
727     Int8 temp_val;
728     switch (from_var.GetType()) {
729     case eDB_BigInt:
730     case eDB_Int:
731     case eDB_SmallInt:
732         temp_val = from_var.GetInt8();
733         break;
734     case eDB_TinyInt:
735         to_val = from_var.GetByte();
736         return;
737     case eDB_Bit:
738         to_val = static_cast<unsigned char>(from_var.GetBit());
739         return;
740     case eDB_VarChar:
741     case eDB_Char:
742     case eDB_LongChar:
743     case eDB_Text:
744     case eDB_VarCharMax:
745         temp_val = NStr::StringToInt8(from_var.GetString());
746         break;
747     default:
748         s_ConvertionNotSupported("unsigned char", from_var.GetType());
749         return;
750     }
751     if (temp_val < numeric_limits<unsigned char>::min()
752         ||  temp_val > numeric_limits<unsigned char>::max())
753     {
754         NCBI_THROW(CSDB_Exception, eOutOfBounds | Retriable(eRetriable_No),
755                    "Value for unsigned char is out of bounds: "
756                    + NStr::NumericToString(temp_val));
757     }
758     to_val = static_cast<unsigned char>(temp_val);
759 }
760 
761 static void
s_ConvertValue(const CVariant & from_var,bool & to_val)762 s_ConvertValue(const CVariant& from_var, bool& to_val)
763 {
764     Int8 temp_val;
765     switch (from_var.GetType()) {
766     case eDB_BigInt:
767     case eDB_Int:
768     case eDB_SmallInt:
769     case eDB_TinyInt:
770         temp_val = from_var.GetInt8();
771         break;
772     case eDB_Bit:
773         to_val = from_var.GetBit();
774         return;
775     case eDB_VarChar:
776     case eDB_Char:
777     case eDB_LongChar:
778     case eDB_Text:
779     case eDB_VarCharMax:
780         temp_val = NStr::StringToInt8(from_var.GetString());
781         break;
782     default:
783         s_ConvertionNotSupported("bool", from_var.GetType());
784         return;
785     }
786     if (temp_val != 0  &&  temp_val != 1)
787     {
788         NCBI_THROW(CSDB_Exception, eOutOfBounds | Retriable(eRetriable_No),
789                    "Value for bool is out of bounds: "
790                    + NStr::NumericToString(temp_val));
791     }
792     to_val = temp_val == 1;
793 }
794 
795 static void
s_ConvertValue(const CVariant & from_var,float & to_val)796 s_ConvertValue(const CVariant& from_var, float& to_val)
797 {
798     switch (from_var.GetType()) {
799     case eDB_Float:
800         to_val = from_var.GetFloat();
801         break;
802     case eDB_VarChar:
803     case eDB_Char:
804     case eDB_LongChar:
805     case eDB_Text:
806     case eDB_VarCharMax:
807         to_val = float(NStr::StringToDouble(from_var.GetString()));
808         break;
809     default:
810         s_ConvertionNotSupported("float", from_var.GetType());
811     }
812 }
813 
814 static void
s_ConvertValue(const CVariant & from_var,double & to_val)815 s_ConvertValue(const CVariant& from_var, double& to_val)
816 {
817     switch (from_var.GetType()) {
818     case eDB_Float:
819     case eDB_Double:
820         to_val = from_var.GetDouble();
821         break;
822     case eDB_VarChar:
823     case eDB_Char:
824     case eDB_LongChar:
825     case eDB_Text:
826     case eDB_VarCharMax:
827         to_val = NStr::StringToDouble(from_var.GetString());
828         break;
829     default:
830         s_ConvertionNotSupported("double", from_var.GetType());
831     }
832 }
833 
834 static void
s_ConvertValue(const CVariant & from_var,string & to_val)835 s_ConvertValue(const CVariant& from_var, string& to_val)
836 {
837     switch (from_var.GetType()) {
838     case eDB_BigInt:
839     case eDB_Int:
840     case eDB_SmallInt:
841     case eDB_TinyInt:
842         to_val = NStr::Int8ToString(from_var.GetInt8());
843         break;
844     case eDB_Bit:
845         to_val = NStr::BoolToString(from_var.GetBit());
846         break;
847     case eDB_Float:
848     case eDB_Double:
849         to_val = NStr::DoubleToString(from_var.GetDouble());
850         break;
851     case eDB_DateTime:
852     case eDB_SmallDateTime:
853     case eDB_BigDateTime:
854         to_val = from_var.GetCTime().AsString();
855         break;
856     case eDB_VarChar:
857     case eDB_Char:
858     case eDB_LongChar:
859     case eDB_Text:
860     case eDB_VarBinary:
861     case eDB_Binary:
862     case eDB_LongBinary:
863     case eDB_Image:
864     case eDB_Numeric:
865     case eDB_VarCharMax:
866     case eDB_VarBinaryMax:
867         to_val = from_var.GetString();
868         break;
869     default:
870         s_ConvertionNotSupported("string", from_var.GetType());
871     }
872 }
873 
874 
s_CheckCompressionFlags(TNewBlobStoreFlags flags)875 static ECompressMethod s_CheckCompressionFlags(TNewBlobStoreFlags flags)
876 {
877     if ((flags & (fNBS_ZLib | fNBS_BZLib)) == (fNBS_ZLib | fNBS_BZLib)) {
878         NCBI_THROW(CSDB_Exception, eWrongParams,
879                    "fNBS_ZLib and fNBS_BZLib are mutually incompatible; "
880                    "please specify at most one compression algorithm.");
881     } else if ((flags & fNBS_ZLib) != 0) {
882         return eZLib;
883     } else if ((flags & fNBS_BZLib) != 0) {
884         return eBZLib;
885     } else {
886         return eNone;
887     }
888 }
889 
890 
891 static
s_TranslateBlobStoreFlags(TNewBlobStoreFlags flags)892 CSimpleBlobStore::TFlags s_TranslateBlobStoreFlags(TNewBlobStoreFlags flags)
893 {
894     CSimpleBlobStore::TFlags result = CSimpleBlobStore::kDefaults;
895     if ((flags & fNBS_LogIt) != 0) {
896         result |= CSimpleBlobStore::fLogBlobs;
897     }
898     if ((flags & fNBS_IsText) != 0) {
899         result |= CSimpleBlobStore::fIsText;
900     }
901     if ((flags & fNBS_Preallocated) != 0) {
902         result |= CSimpleBlobStore::fPreallocated;
903     }
904     return result;
905 }
906 
907 
908 static const char kDefaultDriverName[] = "ftds";
909 static AutoPtr<char, CDeleter<char> > s_DriverName;
910 
911 class CDataSourceInitializer : protected CConnIniter
912 {
913 public:
CDataSourceInitializer(void)914     CDataSourceInitializer(void)
915     {
916         DBLB_INSTALL_DEFAULT();
917         DBAPI_RegisterDriver_FTDS();
918         DBAPI_RegisterDriver_FTDS95();
919         DBAPI_RegisterDriver_FTDS100();
920 
921         if (s_DriverName.get() == NULL) {
922             s_DriverName.reset(strdup(kDefaultDriverName));
923         }
924 
925         {{
926             CNcbiApplication* app = CNcbiApplication::Instance();
927             if (app != NULL) {
928                 string driver_name
929                     = app->GetConfig().GetString("sdbapi", "use_driver",
930                                                  s_DriverName.get());
931                 if (driver_name == kDefaultDriverName
932                     ||  driver_name == "ftds95"
933                     ||  driver_name == "ftds100") {
934                     s_DriverName.reset(strdup(driver_name.c_str()));
935                 } else {
936                     ERR_POST_X(15, "Unsupported driver name " << driver_name
937                                << "; sticking with " << s_DriverName.get());
938                 }
939             }
940         }}
941 
942         CDBConnParamsBase params;
943         params.SetDriverName(s_DriverName.get());
944         params.SetEncoding(eEncoding_UTF8);
945         IDataSource* ds
946             = CDriverManager::GetInstance().MakeDs(params, ".sdbapi");
947         I_DriverContext* ctx = ds->GetDriverContext();
948         ctx->PushCntxMsgHandler(new CDB_UserHandler_Exception, eTakeOwnership);
949         ctx->PushDefConnMsgHandler(new CDB_UserHandler_Exception, eTakeOwnership);
950     }
951 };
952 
953 
954 static CSafeStatic<CDataSourceInitializer> ds_init;
955 
956 inline
s_GetDataSource(void)957 static IDataSource* s_GetDataSource(void)
958 {
959     ds_init.Get();
960     return CDriverManager::GetInstance().CreateDs
961         (s_DriverName.get(), ".sdbapi");
962 }
963 
964 inline
s_GetDBContext(void)965 static impl::CDriverContext* s_GetDBContext(void)
966 {
967     IDataSource* ds = s_GetDataSource();
968     return static_cast<impl::CDriverContext*>(ds->GetDriverContext());
969 }
970 
971 
x_GetKey(const CTempString & key_id)972 string CSDB_Decryptor::x_GetKey(const CTempString& key_id)
973 {
974     string key;
975     CNcbiApplication* app = CNcbiApplication::Instance();
976     if (app != NULL) {
977         key = app->GetConfig().GetString("DBAPI_key", key_id, kEmptyStr);
978     }
979 
980 #ifdef HAVE_LIBCONNEXT
981     if (key.empty()) {
982         key = s_GetBuiltInKey(key_id);
983     }
984 #endif
985     if (key.empty()) {
986         NCBI_THROW(CSDB_Exception, eWrongParams | Retriable(eRetriable_No),
987                    "Unknown password decryption key ID " + string(key_id));
988     }
989     return key;
990 }
991 
992 
993 DEFINE_STATIC_FAST_MUTEX(s_DecryptorMutex);
994 static CSafeStatic<CRef<CSDB_Decryptor> > s_Decryptor;
995 static bool s_DecryptorInitialized = false;
996 
SetGlobalDecryptor(CRef<CSDB_Decryptor> decryptor)997 void CSDB_ConnectionParam::SetGlobalDecryptor(CRef<CSDB_Decryptor> decryptor)
998 {
999     CFastMutexGuard GUARD(s_DecryptorMutex);
1000     s_Decryptor->Reset(decryptor);
1001     s_DecryptorInitialized = true;
1002 }
1003 
GetGlobalDecryptor(void)1004 CRef<CSDB_Decryptor> CSDB_ConnectionParam::GetGlobalDecryptor(void)
1005 {
1006     CFastMutexGuard GUARD(s_DecryptorMutex);
1007 #ifdef HAVE_LIBCONNEXT
1008     if ( !s_DecryptorInitialized ) {
1009         s_Decryptor->Reset(new CSDB_Decryptor);
1010         s_DecryptorInitialized = true;
1011     }
1012 #endif
1013     return *s_Decryptor;
1014 }
1015 
1016 
1017 string
ComposeUrl(TComposeUrlFlags flags) const1018 CSDB_ConnectionParam::ComposeUrl(TComposeUrlFlags flags) const
1019 {
1020     if ((flags & fThrowIfIncomplete) != 0
1021         &&  (m_Url.GetHost().empty()  ||  m_Url.GetUser().empty()
1022              ||  (m_Url.GetPassword().empty()  &&  Get(ePasswordFile).empty())
1023              ||  m_Url.GetPath().empty()  ||  m_Url.GetPath() == "/"))
1024     {
1025         TComposeUrlFlags fl = (flags & ~fThrowIfIncomplete) | fHidePassword;
1026         NCBI_THROW(CSDB_Exception, eURLFormat | Retriable(eRetriable_No),
1027                    "Connection parameters miss at least one essential part"
1028                    " (host, user, password, or database [as \"path\"]): "
1029                    + ComposeUrl(fl));
1030     }
1031 
1032     if ((flags & fHidePassword) != 0  &&  !m_Url.GetPassword().empty()) {
1033         CUrl redactee = m_Url;
1034         redactee.SetPassword("(redacted)");
1035         return redactee.ComposeUrl(CUrlArgs::eAmp_Char);
1036     }
1037     return m_Url.ComposeUrl(CUrlArgs::eAmp_Char);
1038 }
1039 
IsEmpty(void) const1040 bool CSDB_ConnectionParam::IsEmpty(void) const
1041 {
1042     // Can't use m_Url.IsEmpty(), because the URL will still have a
1043     // scheme ("dbapi").
1044     if ( !m_Url.GetUser().empty()  ||  !m_Url.GetPassword().empty()
1045         ||  !m_Url.GetHost().empty()  ||  !m_Url.GetPort().empty()
1046         ||  !m_Url.GetPath().empty() ) {
1047         return false;
1048     } else if ( !m_Url.HaveArgs() ) {
1049         return true;
1050     }
1051 
1052     ITERATE (CUrlArgs::TArgs, it, m_Url.GetArgs().GetArgs()) {
1053         if ( !it->value.empty() ) {
1054             return false;
1055         }
1056     }
1057     return true;
1058 }
1059 
1060 CSDB_ConnectionParam&
Set(const CSDB_ConnectionParam & param,TSetFlags flags)1061 CSDB_ConnectionParam::Set(const CSDB_ConnectionParam& param,
1062                           TSetFlags flags)
1063 {
1064     for (int i = eUsername;  i <= eArgsString;  ++i) {
1065         EParam ii = static_cast<EParam>(i);
1066         Set(ii, param.Get(ii), flags);
1067     }
1068     return *this;
1069 }
1070 
x_FillParamMap(void)1071 void CSDB_ConnectionParam::x_FillParamMap(void)
1072 {
1073     static const char* kDefault = "default";
1074     m_ParamMap.clear();
1075 
1076     impl::SDBConfParams conf_params;
1077     try {
1078         s_GetDBContext()->ReadDBConfParams(m_Url.GetHost(), &conf_params);
1079     } SDBAPI_CATCH_LOWLEVEL()
1080 
1081 #define COPY_PARAM_EX(en, gn, fn) \
1082     if (conf_params.Is##gn##Set()) \
1083         m_ParamMap[e##en] = conf_params.fn
1084 #define COPY_PARAM(en, fn) COPY_PARAM_EX(en, en, fn)
1085 #define COPY_BOOL_PARAM(en, gn, fn) \
1086     if (conf_params.Is##gn##Set()) \
1087         m_ParamMap[e##en] = conf_params.fn.empty() ? "default" \
1088             : NStr::StringToBool(conf_params.fn) ? "true" : "false"
1089 #define COPY_NUM_PARAM(en, gn, fn) \
1090     if (conf_params.Is##gn##Set()) \
1091         m_ParamMap[e##en] = conf_params.fn.empty() ? kDefault : conf_params.fn
1092 
1093     COPY_PARAM_EX(Service, Server, server);
1094 
1095     COPY_PARAM(Username, username);
1096     COPY_PARAM(Password, password);
1097     COPY_PARAM(Port,     port);
1098     COPY_PARAM(Database, database);
1099     COPY_PARAM(PasswordFile, password_file);
1100     COPY_PARAM_EX(PasswordKeyID, PasswordKey, password_key_id);
1101 
1102     COPY_NUM_PARAM (LoginTimeout,    LoginTimeout, login_timeout);
1103     COPY_NUM_PARAM (IOTimeout,       IOTimeout,    io_timeout);
1104     COPY_BOOL_PARAM(ExclusiveServer, SingleServer, single_server);
1105     COPY_BOOL_PARAM(UseConnPool,     Pooled,       is_pooled);
1106     COPY_NUM_PARAM (ConnPoolMinSize, PoolMinSize,  pool_minsize);
1107     COPY_NUM_PARAM (ConnPoolMaxSize, PoolMaxSize,  pool_maxsize);
1108     COPY_NUM_PARAM (ConnPoolIdleTime, PoolIdleTime, pool_idle_time);
1109     COPY_NUM_PARAM (ConnPoolWaitTime, PoolWaitTime, pool_wait_time);
1110     COPY_BOOL_PARAM(ConnPoolAllowTempOverflow, PoolAllowTempOverflow,
1111                     pool_allow_temp_overflow);
1112     COPY_BOOL_PARAM(ContinueAfterRaiserror, ContinueAfterRaiserror,
1113                     continue_after_raiserror);
1114     COPY_NUM_PARAM (ConnPoolMaxConnUse, PoolMaxConnUse,  pool_max_conn_use);
1115 
1116 #undef COPY_PARAM_EX
1117 #undef COPY_PARAM
1118 #undef COPY_BOOL_PARAM
1119 #undef COPY_NUM_PARAM
1120 
1121     if ( !conf_params.args.empty() ) {
1122         m_ParamMap[eArgsString] = conf_params.args;
1123     }
1124 
1125     // Check for overridden settings
1126     ITERATE (TParamMap, it, m_ParamMap) {
1127         string s = Get(it->first, eWithoutOverrides);
1128         if ( !s.empty() ) {
1129             x_ReportOverride(x_GetName(it->first), s, it->second);
1130         }
1131     }
1132 
1133     if (conf_params.IsPasswordSet()  &&  conf_params.IsPasswordFileSet()) {
1134         NCBI_THROW(CSDB_Exception, eWrongParams | Retriable(eRetriable_No),
1135                    '[' + m_Url.GetHost() + ".dbservice] password and"
1136                    " password_file parameters are mutually exclusive.");
1137     }
1138 }
1139 
x_GetPassword() const1140 string CSDB_ConnectionParam::x_GetPassword() const
1141 {
1142     string password;
1143 
1144     // Account for possible indirection.  Formal search order:
1145     // 1. Password directly in configuration file.
1146     // 2. Password filename in configuration file.
1147     // 3. Password filename in URL.
1148     // 4. Password directly in URL.
1149     // However, if either the configuration file or the URL supplies
1150     // both a direct password and a password filename, SDBAPI will
1151     // throw an exception, so it doesn't matter that 3 and 4 are
1152     // backward relative to 1 and 2.
1153     TParamMap::const_iterator it = m_ParamMap.find(ePassword);
1154     if (it != m_ParamMap.end()) {
1155         password = it->second;
1156     } else {
1157         string pwfile = Get(ePasswordFile, eWithOverrides);
1158         if (pwfile.empty()) {
1159             password = Get(ePassword);
1160         } else {
1161             CNcbiIfstream in(pwfile.c_str(), IOS_BASE::in | IOS_BASE::binary);
1162             if ( !in ) {
1163                 NCBI_THROW(CSDB_Exception,
1164                            eNotExist | Retriable(eRetriable_No), // eWrongParams?
1165                            "Unable to open password file " + pwfile + ": " +
1166                            NCBI_ERRNO_STR_WRAPPER(NCBI_ERRNO_CODE_WRAPPER()));
1167             }
1168             NcbiGetline(in, password, "\r\n");
1169         }
1170     }
1171 
1172     // Account for possible encryption.
1173     string key_id = Get(ePasswordKeyID, eWithOverrides);
1174     if ( !key_id.empty() ) {
1175         CRef<CSDB_Decryptor> decryptor = GetGlobalDecryptor();
1176         if (decryptor.NotEmpty()) {
1177             password = decryptor->Decrypt(password, key_id);
1178             ITERATE (string, pit, password) {
1179                 if ( !isprint((unsigned char)*pit) ) {
1180                     NCBI_THROW(CSDB_Exception,
1181                                eWrongParams | Retriable(eRetriable_No),
1182                                "Invalid character in supposedly decrypted"
1183                                " password.");
1184                 }
1185             }
1186         }
1187     }
1188 
1189     return password;
1190 }
1191 
1192 void
x_FillLowerParams(CDBConnParamsBase * params) const1193 CSDB_ConnectionParam::x_FillLowerParams(CDBConnParamsBase* params) const
1194 {
1195     params->SetServerName  (Get(eService,  eWithOverrides));
1196     params->SetUserName    (Get(eUsername, eWithOverrides));
1197     params->SetPassword    (x_GetPassword());
1198     params->SetDatabaseName(Get(eDatabase, eWithOverrides));
1199 
1200     string port = Get(ePort, eWithOverrides);
1201     if ( !port.empty() ) {
1202         params->SetPort(NStr::StringToInt(port));
1203     }
1204 
1205     params->SetParam("login_timeout", Get(eLoginTimeout,    eWithOverrides));
1206     params->SetParam("io_timeout",    Get(eIOTimeout,       eWithOverrides));
1207     x_FillBoolParam(params, "single_server", eExclusiveServer);
1208     x_FillBoolParam(params, "is_pooled",     eUseConnPool);
1209     if (params->GetParam("is_pooled") == "true") { // canonicalized above
1210         params->SetParam("pool_name", params->GetServerName() + ".pool");
1211     }
1212     string explicit_pool_name = Get(eConnPoolName, eWithOverrides);
1213     if ( !explicit_pool_name.empty() ) {
1214         params->SetParam("pool_name", explicit_pool_name);
1215     }
1216     params->SetParam("pool_minsize",  Get(eConnPoolMinSize, eWithOverrides));
1217     params->SetParam("pool_maxsize",  Get(eConnPoolMaxSize, eWithOverrides));
1218     params->SetParam("pool_idle_time", Get(eConnPoolIdleTime, eWithOverrides));
1219     params->SetParam("pool_wait_time", Get(eConnPoolWaitTime, eWithOverrides));
1220     params->SetParam("pool_max_conn_use",
1221                      Get(eConnPoolMaxConnUse, eWithOverrides));
1222 
1223     x_FillBoolParam(params, "pool_allow_temp_overflow",
1224                     eConnPoolAllowTempOverflow);
1225     x_FillBoolParam(params, "continue_after_raiserror",
1226                     eContinueAfterRaiserror);
1227 
1228     // Generic named parameters.  The historic version of this logic
1229     // had two quirks, which I [AMU] have not carried over:
1230     // * A setting found in the configuration file counted only when
1231     //   the original URL mentioned the same parameter.
1232     // * If a key appeared more than once in either list, the old code
1233     //   favored the last appearance in the URL but the first one in
1234     //   the configuration file.  It now always favors the last one.
1235     typedef vector<CUrlArgs*> TAllArgs;
1236     TAllArgs all_args;
1237     all_args.push_back(&m_Url.GetArgs());
1238 
1239     unique_ptr<CUrlArgs> conf_args;
1240     {{
1241         TParamMap::const_iterator it = m_ParamMap.find(eArgsString);
1242         if (it != m_ParamMap.end()) {
1243             conf_args.reset(new CUrlArgs(it->second));
1244             all_args.push_back(conf_args.get());
1245         }
1246     }}
1247 
1248     ITERATE (TAllArgs, ait, all_args) {
1249         const CUrlArgs::TArgs& args = (*ait)->GetArgs();
1250         ITERATE (CUrlArgs::TArgs, uit, args) {
1251             const string& param_name  = uit->name;
1252             const string& param_value = uit->value;
1253             if ( !x_IsKnownArg(param_name) ) {
1254                 params->SetParam(param_name, param_value);
1255             }
1256         }
1257     }
1258 
1259     params->SetParam("do_not_read_conf", "true");
1260 }
1261 
1262 void
x_FillBoolParam(CDBConnParamsBase * params,const string & name,EParam id) const1263 CSDB_ConnectionParam::x_FillBoolParam(CDBConnParamsBase* params,
1264                                       const string& name, EParam id) const
1265 {
1266     string value = Get(id, eWithOverrides);
1267     if ( !value.empty()  &&  value != "default") {
1268         value = NStr::BoolToString(NStr::StringToBool(value));
1269     }
1270     params->SetParam(name, value);
1271 }
1272 
1273 
x_ReportOverride(const CTempString & name,CTempString code_value,CTempString reg_value) const1274 void CSDB_ConnectionParam::x_ReportOverride(const CTempString& name,
1275                                             CTempString code_value,
1276                                             CTempString reg_value) const
1277 {
1278     if (code_value == reg_value) {
1279         return;
1280     } else if (name == x_GetName(eArgsString)) {
1281         // Analyze individually
1282         typedef map<string, string> TConfMap;
1283         TConfMap conf_map;
1284         CUrlArgs conf_args(reg_value);
1285         ITERATE (CUrlArgs::TArgs, it, conf_args.GetArgs()) {
1286             if ( !x_IsKnownArg(it->name) ) {
1287                 conf_map[it->name] = it->value;
1288             }
1289         }
1290         ITERATE (CUrlArgs::TArgs, uait, m_Url.GetArgs().GetArgs()) {
1291             TConfMap::const_iterator cmit = conf_map.find(uait->name);
1292             if (cmit != conf_map.end()) {
1293                 x_ReportOverride(uait->name, uait->value, cmit->second);
1294             }
1295         }
1296     } else if (name == x_GetName(eService)) {
1297         ERR_POST_X(18, Info << "Using privately configured service alias "
1298                    << code_value << " -> " << reg_value);
1299     } else {
1300         if (name == x_GetName(ePassword)) {
1301             code_value = "(redacted)";
1302             reg_value  = "(redacted)";
1303         }
1304         ERR_POST_X(17, Warning << "Ignoring program-defined " << name
1305                    << " parameter value " << code_value << " for "
1306                    << m_Url.GetHost() << " in favor of configured value "
1307                    << reg_value);
1308     }
1309 }
1310 
1311 
1312 bool
Init(void)1313 CSDBAPI::Init(void)
1314 {
1315     CNcbiApplication* app = CNcbiApplication::Instance();
1316     if (!app)
1317         return true;
1318     const IRegistry& reg = app->GetConfig();
1319     bool result = true;
1320     list<string> sections;
1321     reg.EnumerateSections(&sections);
1322     ITERATE(list<string>, it, sections) {
1323         const string& name = *it;
1324         if (name.size() <= 10
1325             ||  NStr::CompareCase(name, name.size() - 10, 10, ".dbservice") != 0)
1326         {
1327             continue;
1328         }
1329         impl::CDriverContext* ctx = s_GetDBContext();
1330         CDBConnParamsBase lower_params;
1331         CSDB_ConnectionParam conn_params(name.substr(0, name.size() - 10));
1332         conn_params.x_FillLowerParams(&lower_params);
1333         if (lower_params.GetParam("is_pooled") == "true") {
1334             result &= ctx->SatisfyPoolMinimum(lower_params);
1335         }
1336     }
1337     return result;
1338 }
1339 
1340 
SetApplicationName(const CTempString & name)1341 void CSDBAPI::SetApplicationName(const CTempString& name)
1342 {
1343     s_GetDBContext()->SetApplicationName(name);
1344 }
1345 
GetApplicationName(void)1346 string CSDBAPI::GetApplicationName(void)
1347 {
1348     return s_GetDBContext()->GetApplicationName();
1349 }
1350 
1351 
UseDriver(EDriver driver)1352 void CSDBAPI::UseDriver(EDriver driver)
1353 {
1354     if (s_DriverName.get() != NULL) {
1355         NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
1356                    "CSDBAPI::UseDriver called with SDBAPI already in use.");
1357     }
1358     switch (driver) {
1359     case eDriver_FTDS95:  s_DriverName.reset(strdup("ftds95"));  break;
1360     case eDriver_FTDS100: s_DriverName.reset(strdup("ftds100")); break;
1361     }
1362 }
1363 
1364 
1365 enum EMirrorConnState {
1366     eConnInitializing,
1367     eConnNotConnected,
1368     eConnEstablished
1369 };
1370 
1371 struct SMirrorServInfo
1372 {
1373     string               server_name;
1374     AutoPtr<IConnection> conn;
1375     EMirrorConnState     state;
1376 };
1377 
1378 typedef list< AutoPtr<SMirrorServInfo> >  TMirrorServList;
1379 
1380 struct SMirrorInfo
1381 {
1382     AutoPtr<CDBConnParamsBase>  conn_params;
1383     TMirrorServList             servers;
1384     string                      master;
1385 };
1386 
1387 typedef map<string, SMirrorInfo>  TMirrorsDataMap;
1388 static CSafeStatic<TMirrorsDataMap> s_MirrorsData;
1389 
1390 
1391 class CUpdMirrorServerParams: public CDBConnParamsBase
1392 {
1393 public:
CUpdMirrorServerParams(const CDBConnParams & other)1394     CUpdMirrorServerParams(const CDBConnParams& other)
1395         : m_Other(other)
1396     {}
1397 
~CUpdMirrorServerParams(void)1398     ~CUpdMirrorServerParams(void)
1399     {}
1400 
GetProtocolVersion(void) const1401     virtual Uint4 GetProtocolVersion(void) const
1402     {
1403         return m_Other.GetProtocolVersion();
1404     }
1405 
GetEncoding(void) const1406     virtual EEncoding GetEncoding(void) const
1407     {
1408         return m_Other.GetEncoding();
1409     }
1410 
GetServerName(void) const1411     virtual string GetServerName(void) const
1412     {
1413         return CDBConnParamsBase::GetServerName();
1414     }
1415 
GetDatabaseName(void) const1416     virtual string GetDatabaseName(void) const
1417     {
1418         return kEmptyStr;
1419     }
1420 
GetUserName(void) const1421     virtual string GetUserName(void) const
1422     {
1423         return m_Other.GetUserName();
1424     }
1425 
GetPassword(void) const1426     virtual string GetPassword(void) const
1427     {
1428         return m_Other.GetPassword();
1429     }
1430 
GetServerType(void) const1431     virtual EServerType GetServerType(void) const
1432     {
1433         return m_Other.GetServerType();
1434     }
1435 
GetHost(void) const1436     virtual Uint4 GetHost(void) const
1437     {
1438         return m_Other.GetHost();
1439     }
1440 
GetPort(void) const1441     virtual Uint2 GetPort(void) const
1442     {
1443         return m_Other.GetPort();
1444     }
1445 
GetConnValidator(void) const1446     virtual CRef<IConnValidator> GetConnValidator(void) const
1447     {
1448         return m_Other.GetConnValidator();
1449     }
1450 
GetParam(const string & key) const1451     virtual string GetParam(const string& key) const
1452     {
1453         string result(CDBConnParamsBase::GetParam(key));
1454         if (result.empty())
1455             return m_Other.GetParam(key);
1456         else
1457             return result;
1458     }
1459 
1460 private:
1461     const CDBConnParams& m_Other;
1462 };
1463 
1464 
1465 CSDBAPI::EMirrorStatus
UpdateMirror(const string & dbservice,list<string> * servers,string * error_message)1466 CSDBAPI::UpdateMirror(const string& dbservice,
1467                       list<string>* servers /* = NULL */,
1468                       string*       error_message /* = NULL */)
1469 {
1470     if (dbservice.empty()) {
1471         NCBI_THROW(CSDB_Exception, eWrongParams | Retriable(eRetriable_No),
1472                    "Mirrored database service name cannot be empty");
1473     }
1474 
1475     bool first_execution = false;
1476     SMirrorInfo& mir_info = (*s_MirrorsData)[dbservice];
1477     if (!mir_info.conn_params.get()) {
1478         mir_info.conn_params.reset(new CDBConnParamsBase);
1479         first_execution = true;
1480     }
1481     CDBConnParamsBase& conn_params = *mir_info.conn_params.get();
1482     TMirrorServList& serv_list = mir_info.servers;
1483 
1484     ds_init.Get();
1485     IDBConnectionFactory* i_factory
1486                 = CDbapiConnMgr::Instance().GetConnectionFactory().GetPointer();
1487     if (conn_params.GetServerName().empty()) {
1488         first_execution = true;
1489         CSDB_ConnectionParam sdb_params(dbservice);
1490         sdb_params.x_FillLowerParams(&conn_params);
1491         if (conn_params.GetParam("single_server") != "true") {
1492             NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
1493                        "UpdateMirror cannot be used when configuration file "
1494                        "doesn't have exclusive_server=true (for " + dbservice
1495                        + ')');
1496         }
1497         CDBConnectionFactory* factory
1498                               = dynamic_cast<CDBConnectionFactory*>(i_factory);
1499         if (!factory) {
1500             NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
1501                        "UpdateMirror cannot work with non-standard "
1502                        "connection factory");
1503         }
1504     }
1505     if (servers)
1506         servers->clear();
1507     if (error_message)
1508         error_message->clear();
1509 
1510     CDBConnectionFactory* factory = static_cast<CDBConnectionFactory*>(i_factory);
1511     string service_name(conn_params.GetServerName());
1512     string db_name(conn_params.GetDatabaseName());
1513     bool need_reread_servers = false;
1514     bool servers_reread = false;
1515     bool has_master = false;
1516     int cnt_switches = 0;
1517 
1518     do {
1519         if (serv_list.empty()) {
1520             list<string> servers;
1521             factory->GetServersList(db_name, service_name, &servers);
1522             ITERATE(list<string>, it, servers) {
1523                 serv_list.push_back(new SMirrorServInfo());
1524                 serv_list.back()->server_name = *it;
1525                 serv_list.back()->state = eConnInitializing;
1526             }
1527             servers_reread = true;
1528         }
1529         else if (need_reread_servers) {
1530             list<string> new_serv_info;
1531             factory->GetServersList(db_name, service_name, &new_serv_info);
1532             ERASE_ITERATE(TMirrorServList, old_it, serv_list) {
1533                 bool found = false;
1534                 ITERATE(list<string>, it, new_serv_info) {
1535                     if (*it == (*old_it)->server_name) {
1536                         found = true;
1537                         break;
1538                     }
1539                 }
1540                 if (!found) {
1541                     serv_list.erase(old_it);
1542                 }
1543             }
1544             ITERATE(list<string>, new_it, new_serv_info) {
1545                 bool found = false;
1546                 ITERATE(TMirrorServList, old_it, serv_list) {
1547                     if ((*old_it)->server_name == *new_it) {
1548                         found = true;
1549                         break;
1550                     }
1551                 }
1552                 if (!found) {
1553                     serv_list.push_back(new SMirrorServInfo());
1554                     serv_list.back()->server_name = *new_it;
1555                     serv_list.back()->state = eConnInitializing;
1556                 }
1557             }
1558             servers_reread = true;
1559         }
1560         if (serv_list.empty()) {
1561             factory->WorkWithSingleServer("", service_name, kEmptyStr);
1562             ERR_POST_X(3, "No servers for service '" << service_name
1563                           << "' are available");
1564             if (error_message) {
1565                 *error_message = "No servers for service '" + service_name
1566                                  + "' are available";
1567             }
1568             return eMirror_Unavailable;
1569         }
1570 
1571         for (TMirrorServList::iterator it = serv_list.end(); it != serv_list.begin(); )
1572         {
1573             --it;
1574             AutoPtr<IConnection>& conn  = (*it)->conn;
1575             const string& server_name   = (*it)->server_name;
1576             EMirrorConnState& state     = (*it)->state;
1577             if (!conn.get()) {
1578                 conn = s_GetDataSource()->CreateConnection(eTakeOwnership);
1579             }
1580             if (!conn->IsAlive()) {
1581                 if (state == eConnEstablished) {
1582                     ERR_POST_X(7, "Connection to server '" << server_name
1583                                   << "' for service '" << service_name
1584                                   << "' is lost.");
1585                     state = eConnNotConnected;
1586                 }
1587                 CUpdMirrorServerParams serv_params(conn_params);
1588                 serv_params.SetServerName(server_name);
1589                 serv_params.SetParam("is_pooled", "false");
1590                 serv_params.SetParam("do_not_dispatch", "true");
1591                 try {
1592                     conn->Close();
1593                     conn->Connect(serv_params);
1594                     if (state == eConnNotConnected) {
1595                         ERR_POST_X(8, "Connection to server '" << server_name
1596                                       << "' for service '" << service_name
1597                                       << "' is restored.");
1598                     }
1599                     state = eConnEstablished;
1600                 }
1601                 catch (CDB_Exception& /*ex*/) {
1602                     //ERR_POST_X(1, Info << "Error connecting to the server '"
1603                     //                   << server_name << "': " << ex);
1604                     if (state == eConnInitializing) {
1605                         ERR_POST_X(9, "Cannot establish connection to server '"
1606                                    << server_name << "' for service '"
1607                                    << service_name << "'.");
1608                         state = eConnNotConnected;
1609                     }
1610                     conn->Close();
1611                     need_reread_servers = true;
1612                 }
1613             }
1614             bool success = false;
1615             if (conn->IsAlive()) {
1616                 try {
1617                     AutoPtr<IStatement> stmt(conn->GetStatement());
1618                     stmt->ExecuteUpdate("use " + db_name);
1619                     success = true;
1620                 }
1621                 catch (CDB_Exception& /*ex*/) {
1622                     // success is already false
1623                     //ERR_POST_X(2, Info << "Check for mirror principal state failed: "
1624                     //                   << ex);
1625                 }
1626             }
1627             if (!success  &&  server_name == mir_info.master) {
1628                 factory->WorkWithSingleServer("", service_name, kEmptyStr);
1629                 string  pool_name = conn_params.GetParam("pool_name");
1630                 size_t  invalidated_count = s_GetDBContext()->CloseConnsForPool(pool_name);
1631                 ERR_POST_X(4, "The master for database '" << db_name <<
1632                               "' (service '" << service_name <<
1633                               "') has become inaccessible. Because of this " <<
1634                               invalidated_count <<
1635                               " active connections got invalidated in the pool '" <<
1636                               pool_name << "'.");
1637                 mir_info.master.clear();
1638                 need_reread_servers = true;
1639             }
1640             else if (success  &&  server_name != mir_info.master) {
1641                 factory->WorkWithSingleServer("", service_name, server_name);
1642                 string  pool_name = conn_params.GetParam("pool_name");
1643                 size_t  invalidated_count =
1644                             s_GetDBContext()->CloseConnsForPool(
1645                                             pool_name,
1646                                             conn->GetCDB_Connection()->Host(),
1647                                             conn->GetCDB_Connection()->Port());
1648                 string  msg_start = "The master for database '" + db_name +
1649                                     "' (for service '" + service_name +
1650                                     "') switched to '" + server_name + "' from ";
1651                 string  msg_end = ". Because of this " +
1652                                   NStr::NumericToString(invalidated_count) +
1653                                   " active connections got invalidated in the pool '" +
1654                                   pool_name + "'.";
1655 
1656                 if (mir_info.master.empty()) {
1657                     // Switching from no master to something => Message
1658                     ERR_POST_X(5, Message << msg_start << "NONE" << msg_end);
1659                 } else {
1660                     // Switching from one master to another => Warning
1661                     ERR_POST_X(5, Warning << msg_start << "'" <<
1662                                   mir_info.master << "'" << msg_end);
1663                 }
1664                 s_GetDBContext()->SatisfyPoolMinimum(conn_params);
1665 
1666                 has_master = true;
1667                 mir_info.master = server_name;
1668                 serv_list.push_front(*it);
1669                 TMirrorServList::iterator it_del = it++;
1670                 serv_list.erase(it_del);
1671                 need_reread_servers = true;
1672 
1673                 if (++cnt_switches == 10) {
1674                     NCBI_THROW(CSDB_Exception,
1675                                eInconsistent | Retriable(eRetriable_No),
1676                                "Mirror database switches too frequently or "
1677                                "it isn't mirrored: " + service_name);
1678                 }
1679             }
1680             else if (success  &&  server_name == mir_info.master) {
1681                 has_master = true;
1682             }
1683         }
1684     }
1685     while (need_reread_servers  &&  !servers_reread);
1686 
1687     if (first_execution  &&  !has_master) {
1688         factory->WorkWithSingleServer("", service_name, kEmptyStr);
1689         string  pool_name = conn_params.GetParam("pool_name");
1690         size_t  invalidated_count = s_GetDBContext()->CloseConnsForPool(pool_name);
1691         ERR_POST_X(10, "The master for database '" << db_name <<
1692                        "' (service '" << service_name <<
1693                        "') is not accessible. Because of this " <<
1694                        invalidated_count <<
1695                        " active connections got invalidated in the pool '" <<
1696                        pool_name << "'.");
1697     }
1698 
1699     if (servers) {
1700         ITERATE(TMirrorServList, it, serv_list) {
1701             servers->push_back((*it)->server_name);
1702         }
1703     }
1704     if (!has_master) {
1705         if (error_message)
1706             *error_message = "All database instances are inaccessible";
1707         return eMirror_Unavailable;
1708     }
1709     return cnt_switches == 0? eMirror_Steady: eMirror_NewMaster;
1710 }
1711 
NewBlobStore(const CSDB_ConnectionParam & param,const string & table_name,TNewBlobStoreFlags flags,size_t image_limit)1712 CBlobStoreDynamic* CSDBAPI::NewBlobStore(const CSDB_ConnectionParam& param,
1713                                          const string& table_name,
1714                                          TNewBlobStoreFlags flags,
1715                                          size_t image_limit)
1716 {
1717     ECompressMethod cm = s_CheckCompressionFlags(flags);
1718     if ((flags & fNBS_IsText) != 0) {
1719         ERR_POST_X_ONCE(20, Warning << "Explicit fNBS_IsText flag passed to a"
1720                         " variant of NewBlobStore that inspects column types"
1721                         " itself.");
1722     }
1723     return new CBlobStoreDynamic(s_GetDBContext(),
1724                                  param.Get(CSDB_ConnectionParam::eService),
1725                                  param.Get(CSDB_ConnectionParam::eUsername),
1726                                  param.Get(CSDB_ConnectionParam::ePassword),
1727                                  table_name, cm, image_limit,
1728                                  s_TranslateBlobStoreFlags(flags));
1729 }
1730 
1731 
HandleMessage(int severity,int msgnum,const string & message)1732 bool CSDB_UserHandler::HandleMessage(int severity, int msgnum,
1733                                      const string& message)
1734 {
1735     if (severity == 0) {
1736         m_Conn.m_PrintOutput.push_back(message);
1737         return true;
1738     } else if (m_Conn.m_ContinueAfterRaiserror
1739                &&  (severity == 16  ||  (severity == 10  &&  msgnum > 0))) {
1740         // Sybase servers use severity 16 for all user-defined messages,
1741         // even if they're not intended to abort execution.  Also, some
1742         // standard minor errors (such as Duplicate key was ignored, #3604)
1743         // normally map to DBAPI exceptions with severity warning.
1744         // Optionally intercept all of these messages and report them as
1745         // warnings (vs. exceptions).
1746         CDB_DSEx ex(DIAG_COMPILE_INFO, NULL, message + *m_Conn.m_Context,
1747                     eDiag_Error, msgnum);
1748         ERR_POST_X(19, Warning << ex);
1749         return true;
1750     } else {
1751         return CDB_UserHandler_Exception::HandleMessage
1752             (severity, msgnum, message);
1753     }
1754 }
1755 
1756 
1757 inline
CConnHolder(IConnection * conn,const CSDB_ConnectionParam & params)1758 CConnHolder::CConnHolder(IConnection* conn, const CSDB_ConnectionParam& params)
1759     : m_Conn(conn),
1760       m_DefaultTimeout(0),
1761       m_HasCustomTimeout(false),
1762       m_ContinueAfterRaiserror
1763         (params.Get(CSDB_ConnectionParam::eContinueAfterRaiserror) == "true"),
1764       m_CntOpen(0),
1765       m_Context(new CDB_Exception::SContext),
1766       m_Handler(new CSDB_UserHandler(*this))
1767 {
1768     if (conn != NULL) {
1769         m_DefaultTimeout         = conn->GetTimeout();
1770         m_Context->server_name   = conn->GetCDB_Connection()->ServerName();
1771         m_Context->username      = conn->GetCDB_Connection()->UserName();
1772         m_Context->database_name = conn->GetDatabase();
1773         conn->GetCDB_Connection()->PushMsgHandler(&*m_Handler, eNoOwnership);
1774     }
1775 }
1776 
~CConnHolder(void)1777 CConnHolder::~CConnHolder(void)
1778 {
1779     delete m_Conn;
1780 }
1781 
1782 inline IConnection*
GetConn(void) const1783 CConnHolder::GetConn(void) const
1784 {
1785     return m_Conn;
1786 }
1787 
1788 inline void
AddOpenRef(void)1789 CConnHolder::AddOpenRef(void)
1790 {
1791     CMutexGuard mg(m_Mutex);
1792     ++m_CntOpen;
1793 }
1794 
1795 inline void
CloseRef(void)1796 CConnHolder::CloseRef(void)
1797 {
1798     CMutexGuard mg(m_Mutex);
1799     if (--m_CntOpen == 0) {
1800         m_Conn->GetCDB_Connection()->PopMsgHandler(&*m_Handler);
1801         m_Conn->Close();
1802     }
1803 }
1804 
SetTimeout(const CTimeout & timeout)1805 void CConnHolder::SetTimeout(const CTimeout& timeout)
1806 {
1807     CMutexGuard mg(m_Mutex);
1808     if (timeout.IsDefault()) {
1809         ResetTimeout();
1810     } else if (m_CntOpen > 0) {
1811         unsigned int sec, nanosec;
1812         timeout.GetNano(&sec, &nanosec);
1813         if (nanosec > 0  &&  sec != kMax_UInt) { // round up as appropriate
1814             ++sec;
1815         }
1816         m_HasCustomTimeout = true;
1817         m_Conn->SetTimeout(sec);
1818     }
1819 }
1820 
ResetTimeout(void)1821 void CConnHolder::ResetTimeout(void)
1822 {
1823     CMutexGuard mg(m_Mutex);
1824     if (m_HasCustomTimeout  &&  m_CntOpen > 0) {
1825         m_Conn->SetTimeout(m_DefaultTimeout);
1826     }
1827     m_HasCustomTimeout = false;
1828 }
1829 
1830 inline
GetPrintOutput(void) const1831 const list<string>& CConnHolder::GetPrintOutput(void) const
1832 {
1833     return m_PrintOutput;
1834 }
1835 
1836 inline
ResetPrintOutput(void)1837 void CConnHolder::ResetPrintOutput(void)
1838 {
1839     CMutexGuard mg(m_Mutex);
1840     m_PrintOutput.clear();
1841 }
1842 
1843 inline
GetContext(void) const1844 const CDB_Exception::SContext& CConnHolder::GetContext(void) const
1845 {
1846     return *m_Context;
1847 }
1848 
1849 
1850 inline void
Connect(const CSDB_ConnectionParam & params)1851 CDatabaseImpl::Connect(const CSDB_ConnectionParam& params)
1852 {
1853     CDBConnParamsBase lower_params;
1854     params.x_FillLowerParams(&lower_params);
1855     AutoPtr<IConnection> conn = s_GetDataSource()->CreateConnection();
1856     conn->Connect(lower_params);
1857     m_Conn.Reset(new CConnHolder(conn.release(), params));
1858     m_IsOpen = m_EverConnected = true;
1859     m_Conn->AddOpenRef();
1860 }
1861 
1862 inline
CDatabaseImpl(void)1863 CDatabaseImpl::CDatabaseImpl(void)
1864     : m_IsOpen(false), m_EverConnected(false)
1865 {
1866 }
1867 
1868 inline
CDatabaseImpl(const CDatabaseImpl & other)1869 CDatabaseImpl::CDatabaseImpl(const CDatabaseImpl& other)
1870     : m_Conn(other.m_Conn),
1871       m_IsOpen(other.m_IsOpen),
1872       m_EverConnected(other.m_EverConnected)
1873 {
1874     if (m_IsOpen)
1875         m_Conn->AddOpenRef();
1876 }
1877 
1878 inline void
Close(void)1879 CDatabaseImpl::Close(void)
1880 {
1881     if (m_IsOpen) {
1882         m_IsOpen = false;
1883         m_Conn->CloseRef();
1884     }
1885 }
1886 
1887 inline
~CDatabaseImpl(void)1888 CDatabaseImpl::~CDatabaseImpl(void)
1889 {
1890     try {
1891         Close();
1892     }
1893     STD_CATCH_ALL_X(11, "CDatabaseImpl::~CDatabaseImpl")
1894 }
1895 
1896 inline bool
IsOpen(void) const1897 CDatabaseImpl::IsOpen(void) const
1898 {
1899     return m_IsOpen;
1900 }
1901 
1902 inline bool
EverConnected(void) const1903 CDatabaseImpl::EverConnected(void) const
1904 {
1905     return m_EverConnected;
1906 }
1907 
1908 inline IConnection*
GetConnection(void)1909 CDatabaseImpl::GetConnection(void)
1910 {
1911     return m_Conn->GetConn();
1912 }
1913 
1914 inline void
SetTimeout(const CTimeout & timeout)1915 CDatabaseImpl::SetTimeout(const CTimeout& timeout)
1916 {
1917     m_Conn->SetTimeout(timeout);
1918 }
1919 
1920 inline void
ResetTimeout(void)1921 CDatabaseImpl::ResetTimeout(void)
1922 {
1923     m_Conn->ResetTimeout();
1924 }
1925 
1926 inline
GetPrintOutput(void) const1927 const list<string>& CDatabaseImpl::GetPrintOutput(void) const
1928 {
1929     return m_Conn->GetPrintOutput();
1930 }
1931 
1932 inline
ResetPrintOutput(void)1933 void CDatabaseImpl::ResetPrintOutput(void)
1934 {
1935     m_Conn->ResetPrintOutput();
1936 }
1937 
1938 inline
1939 const CDB_Exception::SContext&
GetContext(void) const1940 CDatabaseImpl::GetContext(void) const
1941 {
1942     return m_Conn->GetContext();
1943 }
1944 
1945 
CDatabase(void)1946 CDatabase::CDatabase(void)
1947     : m_Impl(new CDatabaseImpl)
1948 {}
1949 
CDatabase(const CSDB_ConnectionParam & params)1950 CDatabase::CDatabase(const CSDB_ConnectionParam& params)
1951     : m_Params(params), m_Impl(new CDatabaseImpl)
1952 {}
1953 
CDatabase(const string & url_string)1954 CDatabase::CDatabase(const string& url_string)
1955     : m_Params(url_string), m_Impl(new CDatabaseImpl)
1956 {}
1957 
CDatabase(const CDatabase & db)1958 CDatabase::CDatabase(const CDatabase& db)
1959 {
1960     operator= (db);
1961 }
1962 
1963 CDatabase&
operator =(const CDatabase & db)1964 CDatabase::operator= (const CDatabase& db)
1965 {
1966     m_Params = db.m_Params;
1967     m_Impl   = db.m_Impl;
1968     return *this;
1969 }
1970 
~CDatabase(void)1971 CDatabase::~CDatabase(void)
1972 {
1973     if (m_Impl->IsOpen()) {
1974         try {
1975             m_Impl.Reset();
1976         }
1977         STD_CATCH_ALL_X(12, "CDatabase::~CDatabase");
1978     }
1979 }
1980 
1981 void
Connect(void)1982 CDatabase::Connect(void)
1983 {
1984     try {
1985         if (m_Impl->EverConnected()) {
1986             m_Impl.Reset(new CDatabaseImpl);
1987         }
1988         m_Impl->Connect(m_Params);
1989     }
1990     SDBAPI_CATCH_LOWLEVEL()
1991 }
1992 
1993 void
Close(void)1994 CDatabase::Close(void)
1995 {
1996     if ( !m_Impl->IsOpen() )
1997         return;
1998     try {
1999         m_Impl->Close();
2000     }
2001     SDBAPI_CATCH_LOWLEVEL()
2002 }
2003 
2004 bool
IsConnected(EConnectionCheckMethod check_method)2005 CDatabase::IsConnected(EConnectionCheckMethod check_method)
2006 {
2007     if ( !m_Impl->IsOpen() ) {
2008         return false;
2009     } else if (check_method == eNoCheck) {
2010         return true;
2011     }
2012 
2013     IConnection* conn = m_Impl->GetConnection();
2014     _ASSERT(conn != NULL);
2015     if ( !conn->IsAlive() ) {
2016         Close();
2017         return false;
2018     } else if (check_method == eFastCheck) {
2019         return true;
2020     }
2021 
2022     _ASSERT(check_method == eFullCheck);
2023     try {
2024         CQuery query = NewQuery("SELECT 1");
2025         query.Execute();
2026         query.RequireRowCount(1);
2027         CQuery::CRowIterator row = query.begin();
2028         bool ok = (row != query.end()  &&  row.GetTotalColumns() == 1
2029                    &&  row[1].AsInt4() == 1);
2030         query.VerifyDone();
2031         return ok;
2032     } catch (exception&) {
2033         Close();
2034         return false;
2035     }
2036 }
2037 
2038 CDatabase
Clone(void)2039 CDatabase::Clone(void)
2040 {
2041     CDatabase result(m_Params);
2042     if (IsConnected(eNoCheck)) {
2043         result.Connect();
2044     }
2045     return result;
2046 }
2047 
2048 #define CONNECT_AS_NEEDED() x_ConnectAsNeeded(NCBI_CURRENT_FUNCTION)
2049 
2050 CQuery
NewQuery(void)2051 CDatabase::NewQuery(void)
2052 {
2053     CONNECT_AS_NEEDED();
2054     return CQuery(m_Impl);
2055 }
2056 
2057 CBulkInsert
NewBulkInsert(const string & table_name,int autoflush)2058 CDatabase::NewBulkInsert(const string& table_name, int autoflush)
2059 {
2060     CONNECT_AS_NEEDED();
2061     return CBulkInsert(m_Impl, table_name, autoflush);
2062 }
2063 
2064 CBlobBookmark
NewBookmark(const string & table_name,const string & column_name,const string & search_conditions,CBlobBookmark::EBlobType column_type,ETriState has_legacy_type)2065 CDatabase::NewBookmark(const string& table_name, const string& column_name,
2066                        const string& search_conditions,
2067                        CBlobBookmark::EBlobType column_type,
2068                        ETriState has_legacy_type)
2069 {
2070     CONNECT_AS_NEEDED();
2071 
2072     CDB_BlobDescriptor::ETDescriptorType desc_type;
2073     switch (column_type) {
2074     case CBlobBookmark::eText:
2075         desc_type = CDB_BlobDescriptor::eText;
2076         break;
2077     case CBlobBookmark::eBinary:
2078         desc_type = CDB_BlobDescriptor::eBinary;
2079         break;
2080     default:
2081         desc_type = CDB_BlobDescriptor::eUnknown;
2082         break;
2083     }
2084 
2085     unique_ptr<I_BlobDescriptor> desc
2086         (new CDB_BlobDescriptor(table_name, column_name, search_conditions,
2087                                 desc_type, has_legacy_type));
2088 
2089     CRef<CBlobBookmarkImpl> bm(new CBlobBookmarkImpl(m_Impl, desc.release()));
2090     return CBlobBookmark(bm);
2091 }
2092 
2093 
NewBlobStore(const string & table_name,TNewBlobStoreFlags flags,size_t image_limit)2094 CBlobStoreStatic* CDatabase::NewBlobStore(const string& table_name,
2095                                           TNewBlobStoreFlags flags,
2096                                           size_t image_limit)
2097 {
2098     ECompressMethod cm = s_CheckCompressionFlags(flags);
2099     if ((flags & fNBS_IsText) != 0) {
2100         ERR_POST_X_ONCE(20, Warning << "Explicit fNBS_IsText flag passed to a"
2101                         " variant of NewBlobStore that inspects column types"
2102                         " itself.");
2103     }
2104     CONNECT_AS_NEEDED();
2105     return new CBlobStoreStatic(m_Impl->GetConnection()->GetCDB_Connection(),
2106                                 table_name, cm, image_limit,
2107                                 s_TranslateBlobStoreFlags(flags));
2108 }
2109 
2110 
NewBlobStore(const string & table_name,const string & key_col_name,const string & num_col_name,const vector<string> blob_col_names,TNewBlobStoreFlags flags,size_t image_limit)2111 CBlobStoreStatic* CDatabase::NewBlobStore(const string& table_name,
2112                                           const string& key_col_name,
2113                                           const string& num_col_name,
2114                                           const vector<string> blob_col_names,
2115                                           TNewBlobStoreFlags flags,
2116                                           size_t image_limit)
2117 {
2118     ECompressMethod cm = s_CheckCompressionFlags(flags);
2119     CONNECT_AS_NEEDED();
2120     return new CBlobStoreStatic(m_Impl->GetConnection()->GetCDB_Connection(),
2121                                 table_name, key_col_name, num_col_name,
2122                                 &blob_col_names[0], blob_col_names.size(),
2123                                 s_TranslateBlobStoreFlags(flags), cm,
2124                                 image_limit);
2125 }
2126 
2127 
x_ConnectAsNeeded(const char * operation)2128 void CDatabase::x_ConnectAsNeeded(const char* operation)
2129 {
2130     if ( !m_Impl->EverConnected() ) {
2131         _TRACE(operation << ": connecting on demand.");
2132         Connect();
2133     } else if ( !IsConnected(eNoCheck) ) {
2134         NCBI_THROW(CSDB_Exception, eClosed | Retriable(eRetriable_No),
2135                    string("Cannot call ") + operation
2136                    + " when not connected.");
2137     }
2138 }
2139 
2140 
2141 inline
CBulkInsertImpl(CDatabaseImpl * db_impl,const string & table_name,int autoflush)2142 CBulkInsertImpl::CBulkInsertImpl(CDatabaseImpl* db_impl,
2143                                  const string&  table_name,
2144                                  int            autoflush)
2145     : m_DBImpl(db_impl),
2146       m_BI(NULL),
2147       m_Autoflush(autoflush),
2148       m_RowsWritten(0),
2149       m_ColsWritten(0),
2150       m_WriteStarted(false),
2151       m_Context(new CDB_Exception::SContext(db_impl->GetContext()))
2152 {
2153     m_BI = db_impl->GetConnection()->GetBulkInsert(table_name);
2154     m_Context->extra_msg = "Bulk insertion into " + table_name;
2155 }
2156 
~CBulkInsertImpl(void)2157 CBulkInsertImpl::~CBulkInsertImpl(void)
2158 {
2159     try {
2160         if (m_BI  &&  m_RowsWritten != 0) {
2161             m_BI->Complete();
2162         }
2163     }
2164     STD_CATCH_ALL_X(13, "Error destroying CBulkInsert");
2165     delete m_BI;
2166 }
2167 
2168 inline
x_GetContext(void) const2169 const CDB_Exception::SContext& CBulkInsertImpl::x_GetContext(void) const
2170 {
2171     return *m_Context;
2172 }
2173 
2174 void
x_CheckCanWrite(int col)2175 CBulkInsertImpl::x_CheckCanWrite(int col)
2176 {
2177     if (!m_BI) {
2178         SDBAPI_THROW(eClosed | Retriable(eRetriable_No),
2179                      "Cannot write into completed CBulkInsert");
2180     }
2181     if (!m_DBImpl->IsOpen()) {
2182         m_BI->Cancel();
2183         delete m_BI;
2184         m_BI = NULL;
2185         SDBAPI_THROW(eClosed | Retriable(eRetriable_No),
2186                      "Cannot write into CBulkInsert when CDatabase was closed");
2187     }
2188     if (col != 0  &&  col > int(m_Cols.size())) {
2189         SDBAPI_THROW(eInconsistent | Retriable(eRetriable_No),
2190                      "Too many values were written to CBulkInsert: "
2191                      + NStr::NumericToString(col) + " > "
2192                      + NStr::NumericToString(m_Cols.size()));
2193     }
2194 }
2195 
2196 void
SetHints(CTempString hints)2197 CBulkInsertImpl::SetHints(CTempString hints)
2198 {
2199     x_CheckCanWrite(0);
2200     m_BI->SetHints(hints);
2201 }
2202 
2203 void
AddHint(IBulkInsert::EHints hint,unsigned int value)2204 CBulkInsertImpl::AddHint(IBulkInsert::EHints hint, unsigned int value)
2205 {
2206     x_CheckCanWrite(0);
2207     m_BI->AddHint(hint, value);
2208 }
2209 
2210 void
AddOrderHint(CTempString columns)2211 CBulkInsertImpl::AddOrderHint(CTempString columns)
2212 {
2213     x_CheckCanWrite(0);
2214     m_BI->AddOrderHint(columns);
2215 }
2216 
2217 inline void
Bind(int col,ESDB_Type type)2218 CBulkInsertImpl::Bind(int col, ESDB_Type type)
2219 {
2220     x_CheckCanWrite(0);
2221     if (m_WriteStarted) {
2222         SDBAPI_THROW(eStarted | Retriable(eRetriable_No),
2223                      "Cannot bind columns when already started to insert");
2224     }
2225     if (col - 1 != int(m_Cols.size())) {
2226         SDBAPI_THROW(eNotInOrder | Retriable(eRetriable_No),
2227                      "Cannot bind columns in CBulkInsert randomly");
2228     }
2229     m_Cols.push_back(CVariant(s_ConvertType(type)));
2230     if (type == eSDB_StringUCS2  ||  type == eSDB_TextUCS2
2231         ||  type == eSDB_StringMaxUCS2) {
2232         m_Cols.back().SetBulkInsertionEnc(eBulkEnc_UCS2FromChar);
2233     }
2234 }
2235 
2236 inline void
EndRow(void)2237 CBulkInsertImpl::EndRow(void)
2238 {
2239     x_CheckCanWrite(0);
2240     if (m_ColsWritten != int(m_Cols.size())) {
2241         SDBAPI_THROW(eInconsistent | Retriable(eRetriable_No),
2242                      "Not enough values were written to CBulkInsert: "
2243                      + NStr::NumericToString(m_ColsWritten) + " != "
2244                      + NStr::NumericToString(m_Cols.size()));
2245     }
2246     try {
2247         m_BI->AddRow();
2248         if (++m_RowsWritten == m_Autoflush) {
2249             m_BI->StoreBatch();
2250             m_RowsWritten = 0;
2251         }
2252         m_ColsWritten = 0;
2253     }
2254     SDBAPI_CATCH_LOWLEVEL()
2255 }
2256 
2257 inline void
Complete(void)2258 CBulkInsertImpl::Complete(void)
2259 {
2260     if (m_BI == NULL) {
2261         // Make idempotent, rather than letting x_CheckCanWrite throw.
2262         return;
2263     }
2264     x_CheckCanWrite(0);
2265     try {
2266         unique_ptr<IBulkInsert> bi(m_BI);
2267         m_BI = NULL;
2268         bi->Complete();
2269     }
2270     SDBAPI_CATCH_LOWLEVEL()
2271 }
2272 
2273 inline void
x_CheckWriteStarted(void)2274 CBulkInsertImpl::x_CheckWriteStarted(void)
2275 {
2276     x_CheckCanWrite(m_ColsWritten + 1);
2277     if (!m_WriteStarted) {
2278         m_WriteStarted = true;
2279         try {
2280             for (unsigned int i = 0; i < m_Cols.size(); ++i) {
2281                 m_BI->Bind(i + 1, &m_Cols[i]);
2282             }
2283         }
2284         SDBAPI_CATCH_LOWLEVEL()
2285     }
2286 }
2287 
2288 inline void
WriteNull(void)2289 CBulkInsertImpl::WriteNull(void)
2290 {
2291     x_CheckWriteStarted();
2292     m_Cols[m_ColsWritten++].SetNull();
2293 }
2294 
2295 template <class T>
2296 inline void
WriteVal(const T & val)2297 CBulkInsertImpl::WriteVal(const T& val)
2298 {
2299     x_CheckWriteStarted();
2300     s_ConvertValue(val, m_Cols[m_ColsWritten++]);
2301 }
2302 
2303 
CBulkInsert(void)2304 CBulkInsert::CBulkInsert(void)
2305 {}
2306 
CBulkInsert(CDatabaseImpl * db_impl,const string & table_name,int autoflush)2307 CBulkInsert::CBulkInsert(CDatabaseImpl* db_impl,
2308                          const string&  table_name,
2309                          int            autoflush)
2310 {
2311     m_Impl.Reset(new CBulkInsertImpl(db_impl, table_name, autoflush));
2312 }
2313 
CBulkInsert(const CBulkInsert & bi)2314 CBulkInsert::CBulkInsert(const CBulkInsert& bi)
2315     : m_Impl(bi.m_Impl)
2316 {}
2317 
~CBulkInsert(void)2318 CBulkInsert::~CBulkInsert(void)
2319 {}
2320 
2321 CBulkInsert&
operator =(const CBulkInsert & bi)2322 CBulkInsert::operator= (const CBulkInsert& bi)
2323 {
2324     m_Impl = bi.m_Impl;
2325     return *this;
2326 }
2327 
2328 void
SetHints(CTempString hints)2329 CBulkInsert::SetHints(CTempString hints)
2330 {
2331     m_Impl->SetHints(hints);
2332 }
2333 
2334 void
AddHint(EHints hint)2335 CBulkInsert::AddHint(EHints hint)
2336 {
2337     try {
2338         m_Impl->AddHint(s_ConvertHints(hint), 0);
2339     }
2340     SDBAPI_CATCH_LOWLEVEL()
2341 }
2342 
2343 void
AddHint(EHintsWithValue hint,unsigned int value)2344 CBulkInsert::AddHint(EHintsWithValue hint, unsigned int value)
2345 {
2346     try {
2347         m_Impl->AddHint(s_ConvertHints(hint), value);
2348     }
2349     SDBAPI_CATCH_LOWLEVEL()
2350 }
2351 
2352 void
AddOrderHint(CTempString columns)2353 CBulkInsert::AddOrderHint(CTempString columns)
2354 {
2355     m_Impl->AddOrderHint(columns);
2356 }
2357 
2358 void
Bind(int col,ESDB_Type type)2359 CBulkInsert::Bind(int col, ESDB_Type type)
2360 {
2361     m_Impl->Bind(col, type);
2362 }
2363 
2364 void
Complete(void)2365 CBulkInsert::Complete(void)
2366 {
2367     m_Impl->Complete();
2368 }
2369 
2370 CBulkInsert&
operator <<(const string & val)2371 CBulkInsert::operator<<(const string& val)
2372 {
2373     m_Impl->WriteVal(val);
2374     return *this;
2375 }
2376 
2377 CBulkInsert&
operator <<(const TStringUCS2 & val)2378 CBulkInsert::operator<<(const TStringUCS2& val)
2379 {
2380     m_Impl->WriteVal(val);
2381     return *this;
2382 }
2383 
2384 CBulkInsert&
operator <<(const char * val)2385 CBulkInsert::operator<<(const char* val)
2386 {
2387     m_Impl->WriteVal(val);
2388     return *this;
2389 }
2390 
2391 CBulkInsert&
operator <<(unsigned char val)2392 CBulkInsert::operator<<(unsigned char val)
2393 {
2394     m_Impl->WriteVal(val);
2395     return *this;
2396 }
2397 
2398 CBulkInsert&
operator <<(short val)2399 CBulkInsert::operator<<(short val)
2400 {
2401     m_Impl->WriteVal(val);
2402     return *this;
2403 }
2404 
2405 CBulkInsert&
operator <<(Int4 val)2406 CBulkInsert::operator<<(Int4 val)
2407 {
2408     m_Impl->WriteVal(val);
2409     return *this;
2410 }
2411 
2412 CBulkInsert&
operator <<(Int8 val)2413 CBulkInsert::operator<<(Int8 val)
2414 {
2415     m_Impl->WriteVal(val);
2416     return *this;
2417 }
2418 
2419 CBulkInsert&
operator <<(float val)2420 CBulkInsert::operator<<(float val)
2421 {
2422     m_Impl->WriteVal(val);
2423     return *this;
2424 }
2425 
2426 CBulkInsert&
operator <<(double val)2427 CBulkInsert::operator<<(double val)
2428 {
2429     m_Impl->WriteVal(val);
2430     return *this;
2431 }
2432 
2433 CBulkInsert&
operator <<(bool val)2434 CBulkInsert::operator<<(bool val)
2435 {
2436     m_Impl->WriteVal(val);
2437     return *this;
2438 }
2439 
2440 CBulkInsert&
operator <<(const CTime & val)2441 CBulkInsert::operator<<(const CTime& val)
2442 {
2443     m_Impl->WriteVal(val);
2444     return *this;
2445 }
2446 
2447 CBulkInsert&
operator <<(CBulkInsert & (* f)(CBulkInsert &))2448 CBulkInsert::operator<<(CBulkInsert& (*f)(CBulkInsert&))
2449 {
2450     return f(*this);
2451 }
2452 
2453 CBulkInsert&
NullValue(CBulkInsert & bi)2454 NullValue(CBulkInsert& bi)
2455 {
2456     bi.m_Impl->WriteNull();
2457     return bi;
2458 }
2459 
2460 CBulkInsert&
EndRow(CBulkInsert & bi)2461 EndRow(CBulkInsert& bi)
2462 {
2463     bi.m_Impl->EndRow();
2464     return bi;
2465 }
2466 
2467 
GetValue(void) const2468 const CVariant* CRemoteQFB::GetValue(void) const
2469 {
2470     return &m_Query.GetFieldValue(m_ColNum);
2471 }
2472 
x_GetContext(void) const2473 const CDB_Exception::SContext& CRemoteQFB::x_GetContext(void) const
2474 {
2475     return m_Query.x_GetContext();
2476 }
2477 
2478 
2479 inline
CQueryFieldImpl(CQueryImpl * q,CVariant * v,ESP_ParamType param_type)2480 CQueryFieldImpl::CQueryFieldImpl(CQueryImpl* q, CVariant* v,
2481                                  ESP_ParamType param_type)
2482     : m_Basis(new CParamQFB(v, q->x_GetContext(), param_type))
2483 {}
2484 
2485 inline
CQueryFieldImpl(CQueryImpl * q,unsigned int col_num)2486 CQueryFieldImpl::CQueryFieldImpl(CQueryImpl* q, unsigned int col_num)
2487     : m_Basis(new CRemoteQFB(*q, col_num))
2488 {}
2489 
2490 inline
CQueryFieldImpl(CQueryFieldImpl & qf)2491 CQueryFieldImpl::CQueryFieldImpl(CQueryFieldImpl& qf)
2492     : m_Basis(qf.m_Basis.release())
2493 {}
2494 
2495 inline
CQueryBlobImpl(CQueryBlobImpl & qb)2496 CQueryBlobImpl::CQueryBlobImpl(CQueryBlobImpl& qb)
2497     : CQueryFieldImpl(qb) // don't try to copy extra fields
2498 {}
2499 
2500 inline
CQueryBlobImpl(CQueryImpl * q,CVariant * v,ESP_ParamType param_type)2501 CQueryBlobImpl::CQueryBlobImpl(CQueryImpl* q, CVariant* v,
2502                                ESP_ParamType param_type)
2503     : CQueryFieldImpl(q, v, param_type)
2504 {}
2505 
2506 inline
CQueryBlobImpl(CQueryImpl * q,unsigned int col_num)2507 CQueryBlobImpl::CQueryBlobImpl(CQueryImpl* q, unsigned int col_num)
2508     : CQueryFieldImpl(q, col_num)
2509 {}
2510 
2511 
CField(const CField & f)2512 CQuery::CField::CField(const CField& f)
2513     : m_Impl(f.m_Impl)
2514 {}
2515 
2516 inline
CField(CQueryImpl * q,CVariant * v,ESP_ParamType param_type)2517 CQuery::CField::CField(CQueryImpl* q, CVariant* v, ESP_ParamType param_type)
2518 {
2519     if (CDB_Object::IsBlobType(v->GetType())) {
2520         m_Impl.Reset(new CQueryBlobImpl(q, v, param_type));
2521     } else {
2522         m_Impl.Reset(new CQueryFieldImpl(q, v, param_type));
2523     }
2524 }
2525 
2526 inline
CField(CQueryImpl * q,unsigned int col_num)2527 CQuery::CField::CField(CQueryImpl* q, unsigned int col_num)
2528 {
2529     switch (q->GetColumnType(col_num)) {
2530     case eSDB_Text:
2531     case eSDB_TextUCS2:
2532     case eSDB_Image:
2533     case eSDB_StringMax:
2534     case eSDB_StringMaxUCS2:
2535     case eSDB_BinaryMax:
2536         m_Impl.Reset(new CQueryBlobImpl(q, col_num));
2537         break;
2538     default:
2539         m_Impl.Reset(new CQueryFieldImpl(q, col_num));
2540         break;
2541     }
2542 }
2543 
~CField()2544 CQuery::CField::~CField()
2545 {}
2546 
CRow()2547 CQuery::CRow::CRow()
2548 {}
2549 
CRow(const CRow & r)2550 CQuery::CRow::CRow(const CRow& r)
2551     : m_Fields(r.m_Fields), m_MetaData(r.m_MetaData)
2552 {}
2553 
~CRow()2554 CQuery::CRow::~CRow()
2555 {}
2556 
2557 
2558 inline
x_CheckColumnNumber(unsigned int col) const2559 void CQuery::CRow::x_CheckColumnNumber(unsigned int col) const
2560 {
2561     if (col == 0  ||  col > m_Fields.size()) {
2562         NCBI_THROW(CSDB_Exception,
2563                    eNotExist | Retriable(eRetriable_No),
2564                    "No such column in the result set: "
2565                    + NStr::NumericToString(col) + ".  " + x_GetContext());
2566     }
2567 }
2568 
operator [](unsigned int col) const2569 const CQuery::CField& CQuery::CRow::operator[](unsigned int col) const
2570 {
2571     x_CheckColumnNumber(col);
2572     return m_Fields[col - 1];
2573 }
2574 
operator [](CTempString col) const2575 const CQuery::CField& CQuery::CRow::operator[](CTempString col) const
2576 {
2577     SQueryRSMetaData::TColNumsMap::const_iterator it
2578         = m_MetaData->col_nums.find(col);
2579     if (it == m_MetaData->col_nums.end()) {
2580         NCBI_THROW(CSDB_Exception,
2581                    eNotExist | Retriable(eRetriable_No),
2582                    "No such column in the result set: " + col + ".  "
2583                    + x_GetContext());
2584     } else {
2585         return m_Fields[it->second - 1];
2586     }
2587 }
2588 
GetTotalColumns(void) const2589 unsigned int CQuery::CRow::GetTotalColumns(void) const
2590 {
2591     return m_Fields.size();
2592 }
2593 
GetColumnName(unsigned int col) const2594 const string& CQuery::CRow::GetColumnName(unsigned int col) const
2595 {
2596     x_CheckColumnNumber(col);
2597     return m_MetaData->col_names[col - 1];
2598 }
2599 
GetColumnType(unsigned int col) const2600 ESDB_Type CQuery::CRow::GetColumnType(unsigned int col) const
2601 {
2602     x_CheckColumnNumber(col);
2603     return m_MetaData->col_types[col - 1];
2604 }
2605 
x_Reset(CQueryImpl & q,IResultSet & rs)2606 void CQuery::CRow::x_Reset(CQueryImpl& q, IResultSet& rs)
2607 {
2608     m_Fields.clear();
2609     m_MetaData.Reset(new SQueryRSMetaData);
2610     m_MetaData->exception_context.Reset(&q.x_GetContext());
2611 
2612     unsigned int cols_cnt = rs.GetTotalColumns();
2613     const IResultSetMetaData* meta = rs.GetMetaData();
2614     m_Fields.clear();
2615     m_Fields.reserve(cols_cnt);
2616     for (unsigned int i = 1; i <= cols_cnt; ++i) {
2617         m_Fields.emplace_back(CQuery::CField(&q, i));
2618         m_MetaData->col_nums[meta->GetName(i)] = i;
2619         m_MetaData->col_names.emplace_back(meta->GetName(i));
2620         m_MetaData->col_types.emplace_back(s_ConvertType(meta->GetType(i)));
2621     }
2622 }
2623 
2624 // NB: x_InitBeforeExec resets many of these fields.
2625 inline
CQueryImpl(CDatabaseImpl * db_impl)2626 CQueryImpl::CQueryImpl(CDatabaseImpl* db_impl)
2627     : m_DBImpl(db_impl),
2628       m_Stmt(NULL),
2629       m_CallStmt(NULL),
2630       m_CurRS(NULL),
2631       m_IgnoreBounds(true),
2632       m_HasExplicitMode(false),
2633       m_RSBeginned(false),
2634       m_RSFinished(true),
2635       m_Executed(false),
2636       m_ReportedWrongRowCount(false),
2637       m_IsSP(false),
2638       m_RowUnderConstruction(false),
2639       m_CurRSNo(0),
2640       m_CurRowNo(0),
2641       m_CurRelRowNo(0),
2642       m_MinRowCount(0),
2643       m_MaxRowCount(kMax_Auto),
2644       m_RowCount(-1),
2645       m_Status(-1),
2646       m_Context(new CDB_Exception::SContext(m_DBImpl->GetContext()))
2647 {
2648     m_Stmt = db_impl->GetConnection()->GetStatement();
2649 }
2650 
~CQueryImpl(void)2651 CQueryImpl::~CQueryImpl(void)
2652 {
2653     try {
2654         x_Close();
2655     }
2656     STD_CATCH_ALL_X(6, "Error destroying CQuery");
2657     x_ClearAllParams();
2658     delete m_Stmt;
2659 }
2660 
2661 const CDB_Exception::SContext&
x_GetContext(void) const2662 CQueryImpl::x_GetContext(void) const
2663 {
2664     if ( !m_Context->extra_msg.empty() ) {
2665         return *m_Context;
2666     }
2667 
2668     CNcbiOstrstream oss;
2669     oss << (m_IsSP ? "RPC: " : "SQL: ");
2670     oss << m_Sql;
2671     if ( !m_Params.empty() ) {
2672         string delim;
2673         oss << "; input parameter(s): ";
2674         ITERATE (TParamsMap, it, m_Params) {
2675             const CVariant* value = it->second.m_Impl->GetValue();
2676             oss << delim;
2677             oss << it->first << " = ";
2678             if (value == NULL  ||  value->IsNull()) {
2679                 oss << "NULL";
2680             } else {
2681                 oss << value->GetData()->GetLogString();
2682             }
2683             delim = ", ";
2684         }
2685     }
2686     m_Context->extra_msg = CNcbiOstrstreamToString(oss);
2687     return *m_Context;
2688 }
2689 
2690 void
x_CheckCanWork(bool need_rs) const2691 CQueryImpl::x_CheckCanWork(bool need_rs /* = false */) const
2692 {
2693     if (!m_DBImpl->IsOpen()) {
2694         SDBAPI_THROW(eClosed | Retriable(eRetriable_No),
2695                      "CQuery is not operational because CDatabase was closed");
2696     }
2697     if (need_rs  &&  !m_CurRS
2698         &&  !const_cast<CQueryImpl*>(this)->HasMoreResultSets())
2699     {
2700         SDBAPI_THROW(eClosed | Retriable(eRetriable_No),
2701                      "CQuery is closed or never executed");
2702     }
2703 }
2704 
2705 template <class T>
2706 inline void
SetParameter(CTempString name,const T & value,ESDB_Type type,ESP_ParamType param_type)2707 CQueryImpl::SetParameter(CTempString   name,
2708                          const T&      value,
2709                          ESDB_Type     type,
2710                          ESP_ParamType param_type)
2711 {
2712     x_CheckCanWork();
2713 
2714     EDB_Type var_type = s_ConvertType(type);
2715     TParamsMap::iterator it = m_Params.find(name);
2716     if (it == m_Params.end()) {
2717         CQuery::CField field(this, new CVariant(var_type), param_type);
2718         it = m_Params.insert(make_pair(name, field)).first;
2719     } else {
2720         it->second.x_Detach();
2721         static_cast<CParamQFB&>(*it->second.m_Impl->m_Basis)
2722             .SetParamType(param_type);
2723     }
2724     CQueryFieldImpl& field = *it->second.m_Impl;
2725     if (field.GetValue() == NULL
2726         ||  field.GetValue()->GetType() != var_type) {
2727         it->second = CQuery::CField(this, new CVariant(var_type), param_type);
2728     }
2729     s_ConvertValue(value,
2730                    const_cast<CVariant&>(*it->second.m_Impl->GetValue()));
2731 }
2732 
2733 inline void
SetNullParameter(CTempString name,ESDB_Type type,ESP_ParamType param_type)2734 CQueryImpl::SetNullParameter(CTempString   name,
2735                              ESDB_Type     type,
2736                              ESP_ParamType param_type)
2737 {
2738     x_CheckCanWork();
2739 
2740     EDB_Type var_type = s_ConvertType(type);
2741     TParamsMap::iterator it = m_Params.find(name);
2742     if (it == m_Params.end()) {
2743         CQuery::CField field(this, new CVariant(var_type), param_type);
2744         it = m_Params.insert(make_pair(name, field)).first;
2745     } else {
2746         it->second.x_Detach();
2747         static_cast<CParamQFB&>(*it->second.m_Impl->m_Basis)
2748             .SetParamType(param_type);
2749     }
2750     CQueryFieldImpl& field = *it->second.m_Impl;
2751     if (field.GetValue() == NULL
2752         ||  field.GetValue()->GetType() != var_type) {
2753         it->second = CQuery::CField(this, new CVariant(var_type), param_type);
2754     } else {
2755         const_cast<CVariant*>(field.GetValue())->SetNull();
2756     }
2757 }
2758 
2759 inline void
x_SetOutParameter(const string & name,const CVariant & value)2760 CQueryImpl::x_SetOutParameter(const string& name, const CVariant& value)
2761 {
2762     TParamsMap::iterator it = m_Params.find(name);
2763     if (it == m_Params.end()) {
2764         CQuery::CField field(this, new CVariant(value), eSP_InOut);
2765         m_Params.insert(make_pair(name, field));
2766     } else {
2767         it->second.x_Detach();
2768         CQueryFieldImpl& field = *it->second.m_Impl;
2769         try {
2770             const_cast<CVariant&>(*field.GetValue()) = value;
2771         } catch (CDB_ClientEx&) {
2772             it->second = CQuery::CField(this, new CVariant(value), eSP_InOut);
2773         }
2774     }
2775 }
2776 
2777 inline const CVariant&
GetFieldValue(unsigned int col_num)2778 CQueryImpl::GetFieldValue(unsigned int col_num)
2779 {
2780     return m_CurRS->GetVariant(col_num);
2781 }
2782 
2783 inline const CQuery::CField&
GetParameter(CTempString name)2784 CQueryImpl::GetParameter(CTempString name)
2785 {
2786     x_CheckCanWork();
2787 
2788     TParamsMap::iterator it = m_Params.find(name);
2789     if (it == m_Params.end()) {
2790         NCBI_THROW(CSDB_Exception,
2791                    eNotExist | Retriable(eRetriable_No),
2792                    "Parameter '" + string(name) + "' doesn't exist.  "
2793                    + x_GetContext());
2794     } else if (static_cast<const CParamQFB&>(*it->second.m_Impl->m_Basis)
2795                .GetParamType() == eSP_InOut
2796                &&  !IsFinished(CQuery::eAllResultSets) ) {
2797         NCBI_THROW(CSDB_Exception,
2798                    eInconsistent | Retriable(eRetriable_No),
2799                    "CQuery::GetParameter called with some results still"
2800                    " unread.  " + x_GetContext());
2801     }
2802 
2803     return it->second;
2804 }
2805 
2806 inline void
ClearParameter(CTempString name)2807 CQueryImpl::ClearParameter(CTempString name)
2808 {
2809     x_CheckCanWork();
2810 
2811     TParamsMap::iterator it = m_Params.find(name);
2812     if (it != m_Params.end()) {
2813         it->second.x_Detach();
2814         m_Params.erase(it);
2815     }
2816 }
2817 
2818 void
x_ClearAllParams(void)2819 CQueryImpl::x_ClearAllParams(void)
2820 {
2821     for (auto& p : m_Params) {
2822         p.second.x_Detach();
2823     }
2824     m_Params.clear();
2825 }
2826 
2827 inline void
ClearParameters(void)2828 CQueryImpl::ClearParameters(void)
2829 {
2830     x_CheckCanWork();
2831     x_ClearAllParams();
2832     m_Context->extra_msg.clear();
2833 }
2834 
2835 inline void
SetSql(CTempString sql)2836 CQueryImpl::SetSql(CTempString sql)
2837 {
2838     x_CheckCanWork();
2839 
2840     m_Sql = sql.empty() ? CTempString(" ") : sql;
2841     m_Executed = false;
2842     m_IsSP = false;
2843 }
2844 
x_DetachAllFields(void)2845 void CQueryImpl::x_DetachAllFields(void)
2846 {
2847     for (auto& f : m_Row.m_Fields) {
2848         f.x_Detach();
2849     }
2850 }
2851 
2852 void
x_Close(void)2853 CQueryImpl::x_Close(void)
2854 {
2855     m_DBImpl->ResetTimeout();
2856     if (m_CurRS != NULL) {
2857         auto orig_row_no = m_CurRowNo;
2858         try {
2859             VerifyDone(CQuery::eAllResultSets);
2860         } catch (CSDB_Exception& e) {
2861             ERR_POST_X(14, Critical << e <<
2862                            "Problem while closing DB query "
2863                            "(result was at row number " << orig_row_no <<
2864                            " and is now at row number " << m_CurRowNo << ").");
2865         }
2866         if (m_CurRSNo != 0) {
2867             _TRACE(m_CurRowNo << " row(s) from query.");
2868         }
2869     }
2870 
2871     delete m_CurRS;
2872     m_CurRS = NULL;
2873     if (m_CallStmt) {
2874         if (m_DBImpl->IsOpen())
2875             m_CallStmt->PurgeResults();
2876         delete m_CallStmt;
2877         m_CallStmt = NULL;
2878     }
2879     if (m_DBImpl->IsOpen()) {
2880         m_Stmt->PurgeResults();
2881         m_Stmt->Close();
2882     }
2883 }
2884 
2885 inline void
x_InitBeforeExec(void)2886 CQueryImpl::x_InitBeforeExec(void)
2887 {
2888     m_IgnoreBounds = true;
2889     m_HasExplicitMode = false;
2890     m_RSBeginned = false;
2891     m_RSFinished = true;
2892     m_ReportedWrongRowCount = false;
2893     m_CurRSNo = 0;
2894     m_CurRowNo = 0;
2895     m_CurRelRowNo = 0;
2896     m_RowCount = 0;
2897     m_MinRowCount = 0;
2898     m_MaxRowCount = kMax_Auto;
2899     m_Status = -1;
2900     m_DBImpl->ResetPrintOutput();
2901 }
2902 
2903 inline void
Execute(const CTimeout & timeout)2904 CQueryImpl::Execute(const CTimeout& timeout)
2905 {
2906     if (m_IsSP  ||  m_Sql.empty()) {
2907         SDBAPI_THROW(eInconsistent | Retriable(eRetriable_No),
2908                      "No statement to execute.");
2909     }
2910 
2911     x_CheckCanWork();
2912 
2913     try {
2914         x_Close();
2915         x_InitBeforeExec();
2916 
2917         m_Stmt->ClearParamList();
2918         ITERATE(TParamsMap, it, m_Params) {
2919             const CQueryFieldImpl& field = *it->second.m_Impl;
2920             m_Stmt->SetParam(*field.GetValue(), it->first);
2921         }
2922         if ( !timeout.IsDefault() ) {
2923             m_DBImpl->SetTimeout(timeout);
2924         }
2925         m_Executed = true;
2926         m_Stmt->SendSql(m_Sql);
2927         HasMoreResultSets();
2928     }
2929     SDBAPI_CATCH_LOWLEVEL()
2930 }
2931 
2932 inline void
ExecuteSP(CTempString sp,const CTimeout & timeout)2933 CQueryImpl::ExecuteSP(CTempString sp, const CTimeout& timeout)
2934 {
2935     x_CheckCanWork();
2936 
2937     m_Sql  = sp;
2938     m_IsSP = true;
2939 
2940     try {
2941         x_Close();
2942         x_InitBeforeExec();
2943 
2944         m_CallStmt = m_DBImpl->GetConnection()->GetCallableStatement(sp);
2945         ITERATE(TParamsMap, it, m_Params) {
2946             const CParamQFB& pqfb
2947                 = static_cast<const CParamQFB&>(*it->second.m_Impl->m_Basis);
2948             if (pqfb.GetParamType() == eSP_InOut) {
2949                 m_CallStmt->SetOutputParam(*pqfb.GetValue(), it->first);
2950             } else {
2951                 m_CallStmt->SetParam(*pqfb.GetValue(), it->first);
2952             }
2953         }
2954         if ( !timeout.IsDefault() ) {
2955             m_DBImpl->SetTimeout(timeout);
2956         }
2957         m_Executed = true;
2958         m_CallStmt->Execute();
2959         HasMoreResultSets();
2960     }
2961     SDBAPI_CATCH_LOWLEVEL()
2962 }
2963 
2964 inline void
Cancel(void)2965 CQueryImpl::Cancel(void)
2966 {
2967     x_DetachAllFields();
2968     x_CheckCanWork();
2969     IStatement* stmt = m_CallStmt ? m_CallStmt : m_Stmt;
2970     stmt->Cancel();
2971 }
2972 
2973 inline void
SetIgnoreBounds(bool is_ignore)2974 CQueryImpl::SetIgnoreBounds(bool is_ignore)
2975 {
2976     x_CheckCanWork();
2977     m_IgnoreBounds = is_ignore;
2978     m_HasExplicitMode = true;
2979     x_CheckRowCount();
2980 }
2981 
2982 inline unsigned int
GetResultSetNo(void) const2983 CQueryImpl::GetResultSetNo(void) const
2984 {
2985     x_CheckCanWork();
2986     return m_CurRSNo;
2987 }
2988 
2989 inline unsigned int
GetRowNo(CQuery::EHowMuch how_much) const2990 CQueryImpl::GetRowNo(CQuery::EHowMuch how_much) const
2991 {
2992     x_CheckCanWork();
2993     if (m_IgnoreBounds  ||  how_much == CQuery::eAllResultSets) {
2994         return m_CurRowNo;
2995     } else {
2996         return m_CurRelRowNo;
2997     }
2998 }
2999 
3000 inline int
GetRowCount(void) const3001 CQueryImpl::GetRowCount(void) const
3002 {
3003     x_CheckCanWork();
3004     if ( !IsFinished(CQuery::eAllResultSets) ) {
3005         NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
3006                    "CQuery::GetRowCount called with some results still"
3007                    " unread.  " + x_GetContext());
3008     } else {
3009         return m_RowCount;
3010     }
3011 }
3012 
3013 inline int
GetStatus(void) const3014 CQueryImpl::GetStatus(void) const
3015 {
3016     x_CheckCanWork();
3017     if ( !IsFinished(CQuery::eAllResultSets) ) {
3018         NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
3019                    "CQuery::GetStatus called with some results still"
3020                    " unread.  " + x_GetContext());
3021     } else {
3022         return m_Status;
3023     }
3024 }
3025 
x_CheckRowCount(void)3026 void CQueryImpl::x_CheckRowCount(void)
3027 {
3028     if (m_ReportedWrongRowCount) {
3029         return;
3030     }
3031 
3032     unsigned int n;
3033     if ( !m_IgnoreBounds ) {
3034         n = m_CurRelRowNo;
3035 #if 0 // Counts rows affected by INSERT and the like.
3036     } else if (m_RowCount > 0) {
3037         n = m_RowCount;
3038 #endif
3039     } else {
3040         n = m_CurRowNo;
3041     }
3042 
3043     if (n > m_MaxRowCount) {
3044         m_ReportedWrongRowCount = true;
3045         NCBI_THROW(CSDB_Exception, eWrongParams | Retriable(eRetriable_No),
3046                    "Too many rows returned (limited to "
3047                    + NStr::NumericToString(m_MaxRowCount) + ").  "
3048                    + x_GetContext());
3049     } else if (m_RSFinished  &&  n < m_MinRowCount) {
3050         m_ReportedWrongRowCount = true;
3051         NCBI_THROW(CSDB_Exception, eWrongParams | Retriable(eRetriable_No),
3052                    "Not enough rows returned ("
3053                    + NStr::NumericToString(m_CurRowNo) + '/'
3054                    + NStr::NumericToString(m_MinRowCount) + ").  "
3055                    + x_GetContext());
3056     }
3057 }
3058 
3059 inline bool
x_Fetch(void)3060 CQueryImpl::x_Fetch(void)
3061 {
3062     try {
3063         x_DetachAllFields();
3064         if (m_CurRS->Next()) {
3065             ++m_CurRowNo;
3066             ++m_CurRelRowNo;
3067             x_CheckRowCount();
3068             return true;
3069         }
3070         else {
3071             m_RSFinished = true;
3072             _TRACE(m_CurRelRowNo << " row(s) in set.");
3073             x_CheckRowCount();
3074             return false;
3075         }
3076     }
3077     SDBAPI_CATCH_LOWLEVEL()
3078 }
3079 
3080 inline void
x_InitRSFields(void)3081 CQueryImpl::x_InitRSFields(void)
3082 {
3083     x_DetachAllFields();
3084     m_RowUnderConstruction = true;
3085     m_Row.x_Reset(*this, *m_CurRS);
3086     m_RowUnderConstruction = false;
3087 }
3088 
3089 bool
HasMoreResultSets(void)3090 CQueryImpl::HasMoreResultSets(void)
3091 {
3092     x_CheckCanWork();
3093     if (m_CurRS  &&  !m_RSBeginned)
3094         return true;
3095     while (m_CurRS  &&  !m_RSFinished) {
3096         x_Fetch();
3097     }
3098     delete m_CurRS;
3099     m_CurRS = NULL;
3100     m_RSBeginned = m_RSFinished = false;
3101 
3102     IStatement* stmt = (m_CallStmt? m_CallStmt: m_Stmt);
3103     try {
3104         while (stmt->HasMoreResults()) {
3105             m_CurRS = stmt->GetResultSet();
3106             if (!m_CurRS)
3107                 continue;
3108             switch (m_CurRS->GetResultType()) {
3109             case eDB_StatusResult:
3110                 delete m_CurRS;
3111                 m_CurRS = NULL;
3112                 break;
3113             case eDB_ParamResult:
3114                 if (m_CallStmt) {
3115                     m_CurRS->Next();
3116                     unsigned int col_cnt = m_CurRS->GetTotalColumns();
3117                     const IResultSetMetaData* meta = m_CurRS->GetMetaData();
3118                     for (unsigned int i = 1; i <= col_cnt; ++i) {
3119                         x_SetOutParameter(meta->GetName(i),
3120                                           m_CurRS->GetVariant(i));
3121                     }
3122                 }
3123                 delete m_CurRS;
3124                 m_CurRS = NULL;
3125                 break;
3126             case eDB_RowResult:
3127                 if (++m_CurRSNo == 2  &&  !m_HasExplicitMode ) {
3128                     ERR_POST_X(16, Info
3129                                << "Multiple SDBAPI result sets found, but"
3130                                " neither SingleSet nor MultiSet explicitly"
3131                                " requested.  Now defaulting to SingleSet.  "
3132                                << x_GetContext());
3133                 }
3134                 if ( !m_IgnoreBounds ) {
3135                     m_ReportedWrongRowCount = false;
3136                 }
3137                 m_CurRelRowNo = 0;
3138                 x_InitRSFields();
3139                 return true;
3140             case eDB_ComputeResult:
3141             case eDB_CursorResult:
3142                 _ASSERT(false);
3143             }
3144         }
3145     }
3146     SDBAPI_CATCH_LOWLEVEL()
3147 
3148     m_DBImpl->ResetTimeout();
3149 
3150     _TRACE(m_CurRowNo << " row(s) from query.");
3151 
3152     if (m_CallStmt) {
3153         try {
3154             m_Status = m_CallStmt->GetReturnStatus();
3155         } catch (CDB_ClientEx&) {
3156         }
3157     }
3158     if (m_RowCount == 0) {
3159         if (m_CurRowNo != 0) {
3160             m_RowCount = m_CurRowNo;
3161             // m_CurRowNo = 0;
3162         }
3163         else {
3164             m_RowCount = stmt->GetRowCount();
3165         }
3166     }
3167     m_CurRSNo = 0;
3168     m_RSFinished = true;
3169     return false;
3170 }
3171 
3172 void
BeginNewRS(void)3173 CQueryImpl::BeginNewRS(void)
3174 {
3175     x_CheckCanWork();
3176     bool has_more_rs = HasMoreResultSets();
3177     if ( !has_more_rs  &&  !m_Executed ) {
3178         // Check has_more_rs in addition to m_Executed because SetSql
3179         // resets the latter.
3180         Execute(CTimeout(CTimeout::eDefault));
3181         has_more_rs = HasMoreResultSets();
3182     }
3183     if ( !has_more_rs ) {
3184         if (m_IgnoreBounds  &&  m_CurRowNo == 0) {
3185             // OK to have no results whatsoever in SingleSet mode.
3186             return;
3187         }
3188         NCBI_THROW(CSDB_Exception, eClosed | Retriable(eRetriable_No),
3189                    "All result sets in CQuery were already iterated through.  "
3190                    + x_GetContext());
3191     }
3192     if (m_RSFinished) {
3193         // Complete recovering from a premature call to GetStatus or
3194         // GetRowCount.
3195         m_RSFinished  = false;
3196         m_CurRelRowNo = 0;
3197         try {
3198             x_InitRSFields();
3199         } SDBAPI_CATCH_LOWLEVEL()
3200     }
3201     while (HasMoreResultSets()  &&  !x_Fetch()  &&  m_IgnoreBounds)
3202         m_RSBeginned = true;
3203     m_RSBeginned = true;
3204 }
3205 
3206 inline void
PurgeResults(void)3207 CQueryImpl::PurgeResults(void)
3208 {
3209     x_CheckCanWork();
3210     m_HasExplicitMode = true; // Doesn't particularly matter at this point!
3211     bool has_more = true;
3212     while (has_more) {
3213         try {
3214             if ((has_more = HasMoreResultSets())) {
3215                 BeginNewRS();
3216             }
3217         } catch (CSDB_Exception& e) {
3218             IResultSet* rs = NULL;
3219             while (has_more  &&  rs != m_CurRS) {
3220                 try {
3221                     rs = m_CurRS;
3222                     if ((has_more = HasMoreResultSets())) {
3223                         BeginNewRS();
3224                     }
3225                 } catch (CSDB_Exception& e2) {
3226                     has_more = true;
3227                     // Not technically the correct relationship, but by far
3228                     // the simplest way to consolidate the exceptions.
3229                     e.AddPrevious(&e2);
3230                 }
3231             }
3232             throw;
3233         }
3234     }
3235 }
3236 
3237 inline
3238 unsigned int
GetTotalColumns(void) const3239 CQueryImpl::GetTotalColumns(void) const
3240 {
3241     x_CheckCanWork(true);
3242     return m_Row.GetTotalColumns();
3243 }
3244 
3245 inline string
GetColumnName(unsigned int col) const3246 CQueryImpl::GetColumnName(unsigned int col) const
3247 {
3248     x_CheckCanWork(true);
3249     return m_Row.GetColumnName(col);
3250 }
3251 
3252 inline ESDB_Type
GetColumnType(unsigned int col) const3253 CQueryImpl::GetColumnType(unsigned int col) const
3254 {
3255     x_CheckCanWork(true);
3256     if (m_RowUnderConstruction) {
3257         try {
3258             return s_ConvertType(m_CurRS->GetMetaData()->GetType(col));
3259         }
3260         SDBAPI_CATCH_LOWLEVEL()
3261     } else {
3262         return m_Row.GetColumnType(col);
3263     }
3264 }
3265 
3266 inline void
Next(void)3267 CQueryImpl::Next(void)
3268 {
3269     while (!x_Fetch()  &&  m_IgnoreBounds  &&  HasMoreResultSets())
3270         m_RSBeginned = true;
3271     m_RSBeginned = true;
3272 }
3273 
3274 inline void
RequireRowCount(unsigned int min_rows,unsigned int max_rows)3275 CQueryImpl::RequireRowCount(unsigned int min_rows, unsigned int max_rows)
3276 {
3277     if ( !m_Executed ) {
3278         SDBAPI_THROW(eInconsistent | Retriable(eRetriable_No),
3279                      "RequireRowCount must follow Execute or ExecuteSP,"
3280                      " which reset any requirements.");
3281     }
3282     if (min_rows > max_rows) {
3283         SDBAPI_THROW(eWrongParams | Retriable(eRetriable_No),
3284                      "Inconsistent row-count constraints: "
3285                      + NStr::NumericToString(min_rows) + " > "
3286                      + NStr::NumericToString(max_rows));
3287     }
3288     x_CheckCanWork();
3289     _TRACE("RequireRowCount(" << min_rows << ", " << max_rows << ')');
3290     m_MinRowCount = min_rows;
3291     m_MaxRowCount = max_rows;
3292     if (m_CurRS != NULL) {
3293         x_CheckRowCount();
3294     }
3295 }
3296 
3297 inline void
VerifyDone(CQuery::EHowMuch how_much)3298 CQueryImpl::VerifyDone(CQuery::EHowMuch how_much)
3299 {
3300     x_CheckCanWork();
3301 
3302     bool missed_results = false;
3303     bool want_all = m_IgnoreBounds  ||  how_much == CQuery::eAllResultSets;
3304 
3305     for (;;) {
3306         try {
3307             if (m_RSFinished) {
3308                 x_CheckRowCount();
3309             } else if (m_CurRS) {
3310                 // Tolerate having read just enough rows, unless that number
3311                 // was zero but not necessarily required to be.
3312                 missed_results
3313                     = x_Fetch()  ||  ( !m_RSBeginned  &&  m_MaxRowCount > 0);
3314             }
3315         } catch (...) {
3316             if (want_all) {
3317                 PurgeResults();
3318             } else {
3319                 HasMoreResultSets();
3320             }
3321             throw;
3322         }
3323 
3324         // We always want the effect of HasMoreResultSets.
3325         if (HasMoreResultSets()  &&  want_all) {
3326             BeginNewRS();
3327         } else {
3328             break;
3329         }
3330     }
3331 
3332     if (missed_results) {
3333         NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
3334                    "Result set had unread rows.  " + x_GetContext());
3335     }
3336 }
3337 
3338 inline bool
IsFinished(CQuery::EHowMuch how_much) const3339 CQueryImpl::IsFinished(CQuery::EHowMuch how_much) const
3340 {
3341     if ( !m_RSFinished ) {
3342         return false;
3343     } else if (how_much == CQuery::eThisResultSet
3344                ||  ( !m_CurRS  &&  !m_Stmt->HasMoreResults())) {
3345         return true;
3346     }
3347 
3348     // Check whether any further result sets actually exist, in which
3349     // case some state (saved here) will need to be rolled back.
3350     // m_CurRS will have to remain advanced, but HasMoreResultSets
3351     // knows not to advance it again until BeginNewRS has run.
3352     CQuery::CRow saved_row        = m_Row;
3353     unsigned int saved_rel_row_no = m_CurRelRowNo;
3354     CQueryImpl&  nc_self          = const_cast<CQueryImpl&>(*this);
3355 
3356     nc_self.m_Row.m_Fields.clear();
3357     nc_self.m_Row.m_MetaData.Reset();
3358     if (nc_self.HasMoreResultSets()) {
3359         nc_self.m_RSFinished  = true; // still match end()
3360         nc_self.m_CurRelRowNo = saved_rel_row_no;
3361         nc_self.m_Row         = saved_row;
3362         return false;
3363     }
3364 
3365     return true;
3366 }
3367 
3368 inline
GetRow(void) const3369 const CQuery::CRow& CQueryImpl::GetRow(void) const
3370 {
3371     x_CheckCanWork(true);
3372     return m_Row;
3373 }
3374 
3375 inline const CQuery::CField&
GetColumn(const CDBParamVariant & c) const3376 CQueryImpl::GetColumn(const CDBParamVariant& c) const
3377 {
3378     x_CheckCanWork(true);
3379     return c.IsPositional() ? m_Row[c.GetPosition()] : m_Row[c.GetName()];
3380 }
3381 
3382 inline CDatabaseImpl*
GetDatabase(void) const3383 CQueryImpl::GetDatabase(void) const
3384 {
3385     return m_DBImpl.GetNCPointer();
3386 }
3387 
3388 inline IConnection*
GetConnection(void)3389 CQueryImpl::GetConnection(void)
3390 {
3391     return m_DBImpl->GetConnection();
3392 }
3393 
3394 inline
GetPrintOutput(void) const3395 const list<string>& CQueryImpl::GetPrintOutput(void) const
3396 {
3397     return m_DBImpl->GetPrintOutput();
3398 }
3399 
3400 
3401 inline
CBlobBookmarkImpl(CDatabaseImpl * db_impl,I_BlobDescriptor * descr)3402 CBlobBookmarkImpl::CBlobBookmarkImpl(CDatabaseImpl* db_impl,
3403                                      I_BlobDescriptor* descr)
3404     : m_DBImpl(db_impl),
3405       m_Descr(descr)
3406 {}
3407 
3408 CNcbiOstream&
GetOStream(size_t blob_size,TBlobOStreamFlags flags)3409 CBlobBookmarkImpl::GetOStream(size_t blob_size, TBlobOStreamFlags flags)
3410 {
3411     try {
3412         CDB_Connection* db_conn = m_DBImpl->GetConnection()->GetCDB_Connection();
3413         m_OStream.reset(new CWStream
3414                         (new CxBlobWriter(db_conn, *m_Descr, blob_size,
3415                                           flags, false),
3416                          0, 0,
3417                          CRWStreambuf::fOwnWriter
3418                          | CRWStreambuf::fLogExceptions));
3419         return *m_OStream;
3420     }
3421     SDBAPI_CATCH_LOWLEVEL()
3422 }
3423 
3424 
CBlobBookmark(void)3425 CBlobBookmark::CBlobBookmark(void)
3426 {}
3427 
CBlobBookmark(CBlobBookmarkImpl * bm_impl)3428 CBlobBookmark::CBlobBookmark(CBlobBookmarkImpl* bm_impl)
3429     : m_Impl(bm_impl)
3430 {}
3431 
CBlobBookmark(const CBlobBookmark & bm)3432 CBlobBookmark::CBlobBookmark(const CBlobBookmark& bm)
3433     : m_Impl(bm.m_Impl)
3434 {}
3435 
3436 CBlobBookmark&
operator =(const CBlobBookmark & bm)3437 CBlobBookmark::operator= (const CBlobBookmark& bm)
3438 {
3439     m_Impl = bm.m_Impl;
3440     return *this;
3441 }
3442 
~CBlobBookmark(void)3443 CBlobBookmark::~CBlobBookmark(void)
3444 {}
3445 
3446 CNcbiOstream&
GetOStream(size_t blob_size,TBlobOStreamFlags flags) const3447 CBlobBookmark::GetOStream(size_t blob_size, TBlobOStreamFlags flags) const
3448 {
3449     return m_Impl.GetNCPointer()->GetOStream(blob_size, flags);
3450 }
3451 
3452 CNcbiOstream&
GetOStream(size_t blob_size,CQuery::EAllowLog log_it) const3453 CBlobBookmark::GetOStream(size_t blob_size, CQuery::EAllowLog log_it) const
3454 {
3455     return GetOStream(blob_size,
3456                       (log_it == CQuery::eDisableLog) ? fBOS_SkipLogging : 0);
3457 }
3458 
DBAPI_MakeTrans(CDatabase & db)3459 CAutoTrans::CSubject DBAPI_MakeTrans(CDatabase& db)
3460 {
3461     return DBAPI_MakeTrans(*db.m_Impl->GetConnection());
3462 }
3463 
DBAPI_MakeTrans(CQuery & query)3464 CAutoTrans::CSubject DBAPI_MakeTrans(CQuery& query)
3465 {
3466     return DBAPI_MakeTrans(*query.m_Impl->GetConnection());
3467 }
3468 
3469 inline
x_GetContext(void) const3470 const CDB_Exception::SContext& CQuery::CRow::x_GetContext(void) const
3471 {
3472     return *m_MetaData->exception_context;
3473 }
3474 
CRowIterator(void)3475 CQuery::CRowIterator::CRowIterator(void)
3476     : m_IsEnd(false)
3477 {}
3478 
3479 inline
CRowIterator(CQueryImpl * q,bool is_end)3480 CQuery::CRowIterator::CRowIterator(CQueryImpl* q, bool is_end)
3481     : m_Query(q),
3482       m_IsEnd(is_end)
3483 {}
3484 
CRowIterator(const CRowIterator & ri)3485 CQuery::CRowIterator::CRowIterator(const CRowIterator& ri)
3486     : m_Query(ri.m_Query),
3487       m_IsEnd(ri.m_IsEnd)
3488 {}
3489 
3490 CQuery::CRowIterator&
operator =(const CRowIterator & ri)3491 CQuery::CRowIterator::operator= (const CRowIterator& ri)
3492 {
3493     m_Query = ri.m_Query;
3494     m_IsEnd = ri.m_IsEnd;
3495     return *this;
3496 }
3497 
~CRowIterator(void)3498 CQuery::CRowIterator::~CRowIterator(void)
3499 {}
3500 
3501 inline
x_GetContext(void) const3502 const CDB_Exception::SContext& CQuery::CRowIterator::x_GetContext(void) const
3503 {
3504     return m_Query->x_GetContext();
3505 }
3506 
3507 unsigned int
GetResultSetNo(void) const3508 CQuery::CRowIterator::GetResultSetNo(void) const
3509 {
3510     return m_Query->GetResultSetNo();
3511 }
3512 
3513 unsigned int
GetRowNo(void) const3514 CQuery::CRowIterator::GetRowNo(void) const
3515 {
3516     return m_Query->GetRowNo();
3517 }
3518 
3519 unsigned int
GetTotalColumns(void) const3520 CQuery::CRowIterator::GetTotalColumns(void) const
3521 {
3522     return m_Query->GetTotalColumns();
3523 }
3524 
3525 string
GetColumnName(unsigned int col) const3526 CQuery::CRowIterator::GetColumnName(unsigned int col) const
3527 {
3528     return m_Query->GetColumnName(col);
3529 }
3530 
3531 ESDB_Type
GetColumnType(unsigned int col) const3532 CQuery::CRowIterator::GetColumnType(unsigned int col) const
3533 {
3534     return m_Query->GetColumnType(col);
3535 }
3536 
3537 bool
operator ==(const CRowIterator & ri) const3538 CQuery::CRowIterator::operator== (const CRowIterator& ri) const
3539 {
3540     if (m_Query != ri.m_Query)
3541         return false;
3542     else if (m_IsEnd ^ ri.m_IsEnd) {
3543         return m_Query->IsFinished();
3544     }
3545     else
3546         return true;
3547 }
3548 
3549 CQuery::CRowIterator&
operator ++(void)3550 CQuery::CRowIterator::operator++ (void)
3551 {
3552     if (m_IsEnd  ||  m_Query->IsFinished()) {
3553         SDBAPI_THROW(eInconsistent | Retriable(eRetriable_No),
3554                      "Cannot increase end() iterator");
3555     }
3556     m_Query->Next();
3557     return *this;
3558 }
3559 
3560 const CQuery::CField&
operator [](unsigned int col) const3561 CQuery::CRowIterator::operator[](unsigned int col) const
3562 {
3563     return m_Query->GetRow()[col];
3564 }
3565 
3566 const CQuery::CField&
operator [](CTempString col) const3567 CQuery::CRowIterator::operator[](CTempString col) const
3568 {
3569     return m_Query->GetRow()[col];
3570 }
3571 
operator *(void) const3572 const CQuery::CRow& CQuery::CRowIterator::operator*(void) const
3573 {
3574     return m_Query->GetRow();
3575 }
3576 
3577 
Detach(void)3578 CRef<CQueryFieldImpl> CQueryFieldImpl::Detach(void)
3579 {
3580     unique_ptr<IQueryFieldBasis> new_basis
3581         (new CLocalQFB(new CVariant(*GetValue()), x_GetContext()));
3582     CRef<CQueryFieldImpl> replacement(new CQueryFieldImpl(*this));
3583     m_Basis.reset(new_basis.release());
3584     return replacement;
3585 }
3586 
Detach(void)3587 CRef<CQueryFieldImpl> CQueryBlobImpl::Detach(void)
3588 {
3589     unique_ptr<IQueryFieldBasis> new_basis
3590         (new CLocalQFB(new CVariant(*GetValue()), x_GetContext()));
3591     CRef<CQueryFieldImpl> replacement(new CQueryBlobImpl(*this));
3592     m_Basis.reset(new_basis.release());
3593     return replacement;
3594 }
3595 
x_Detach(void)3596 void CQuery::CField::x_Detach(void)
3597 {
3598     if ( m_Impl.NotEmpty()  &&  !m_Impl->ReferencedOnlyOnce() ) {
3599         m_Impl.Reset(m_Impl->Detach());
3600     }
3601 }
3602 
3603 inline
x_GetContext(void) const3604 const CDB_Exception::SContext& CQueryFieldImpl::x_GetContext(void) const
3605 {
3606     return m_Basis->x_GetContext();
3607 }
3608 
3609 inline
GetValue(void) const3610 const CVariant* CQueryFieldImpl::GetValue(void) const
3611 {
3612     return m_Basis->GetValue();
3613 }
3614 
3615 string
AsString(void) const3616 CQuery::CField::AsString(void) const
3617 {
3618     string value;
3619     s_ConvertValue(*m_Impl->GetValue(), value);
3620     return value;
3621 }
3622 
3623 unsigned char
AsByte(void) const3624 CQuery::CField::AsByte(void) const
3625 {
3626     unsigned char value = 0;
3627     s_ConvertValue(*m_Impl->GetValue(), value);
3628     return value;
3629 }
3630 
3631 short
AsShort(void) const3632 CQuery::CField::AsShort(void) const
3633 {
3634     short value = 0;
3635     s_ConvertValue(*m_Impl->GetValue(), value);
3636     return value;
3637 }
3638 
3639 Int4
AsInt4(void) const3640 CQuery::CField::AsInt4(void) const
3641 {
3642     Int4 value = 0;
3643     s_ConvertValue(*m_Impl->GetValue(), value);
3644     return value;
3645 }
3646 
3647 Int8
AsInt8(void) const3648 CQuery::CField::AsInt8(void) const
3649 {
3650     Int8 value = 0;
3651     s_ConvertValue(*m_Impl->GetValue(), value);
3652     return value;
3653 }
3654 
3655 float
AsFloat(void) const3656 CQuery::CField::AsFloat(void) const
3657 {
3658     float value = 0;
3659     s_ConvertValue(*m_Impl->GetValue(), value);
3660     return value;
3661 }
3662 
3663 double
AsDouble(void) const3664 CQuery::CField::AsDouble(void) const
3665 {
3666     double value = 0;
3667     s_ConvertValue(*m_Impl->GetValue(), value);
3668     return value;
3669 }
3670 
3671 bool
AsBool(void) const3672 CQuery::CField::AsBool(void) const
3673 {
3674     bool value = false;
3675     s_ConvertValue(*m_Impl->GetValue(), value);
3676     return value;
3677 }
3678 
3679 CTime
AsDateTime(void) const3680 CQuery::CField::AsDateTime(void) const
3681 {
3682     CTime value;
3683     s_ConvertValue(*m_Impl->GetValue(), value);
3684     return value;
3685 }
3686 
3687 const vector<unsigned char>&
AsVector(void) const3688 CQueryFieldImpl::AsVector(void) const
3689 {
3690     SDBAPI_THROW(eUnsupported | Retriable(eRetriable_No),
3691                  string("Method is unsupported for this type of data: ")
3692                  + CDB_Object::GetTypeName(GetValue()->GetType(), false));
3693 }
3694 
3695 const vector<unsigned char>&
AsVector(void) const3696 CQueryBlobImpl::AsVector(void) const
3697 {
3698     const CVariant& var_val = *GetValue();
3699     string value = var_val.GetString();
3700     // WorkShop cannot eat string::iterators in vector<>::insert (although due
3701     // to STL he has to eat any iterator-like type) but is okay with pointers
3702     // to unsigned char here.
3703     const unsigned char* data
3704                        = reinterpret_cast<const unsigned char*>(value.data());
3705     m_Vector.clear();
3706     m_Vector.insert(m_Vector.begin(), data, data + value.size());
3707     return m_Vector;
3708 }
3709 
3710 const vector<unsigned char>&
AsVector(void) const3711 CQuery::CField::AsVector(void) const
3712 {
3713     return m_Impl->AsVector();
3714 }
3715 
3716 CNcbiIstream&
AsIStream(void) const3717 CQueryFieldImpl::AsIStream(void) const
3718 {
3719     SDBAPI_THROW(eUnsupported | Retriable(eRetriable_No),
3720                  string("Method is unsupported for this type of data: ")
3721                  + CDB_Object::GetTypeName(GetValue()->GetType(), false));
3722 }
3723 
3724 CNcbiIstream&
AsIStream(void) const3725 CQueryBlobImpl::AsIStream(void) const
3726 {
3727     const CVariant& var_val = *GetValue();
3728     m_ValueForStream = var_val.GetString();
3729     m_IStream.reset
3730       (new CNcbiIstrstream(m_ValueForStream));
3731     return *m_IStream;
3732 }
3733 
3734 CNcbiIstream&
AsIStream(void) const3735 CQuery::CField::AsIStream(void) const
3736 {
3737     return m_Impl->AsIStream();
3738 }
3739 
3740 bool
IsNull(void) const3741 CQuery::CField::IsNull(void) const
3742 {
3743     return m_Impl->GetValue()->IsNull();
3744 }
3745 
GetOStream(size_t,TBlobOStreamFlags) const3746 CNcbiOstream* IQueryFieldBasis::GetOStream(size_t, TBlobOStreamFlags) const
3747 {
3748     SDBAPI_THROW(eUnsupported | Retriable(eRetriable_No),
3749                  "Method requires a live field");
3750 }
3751 
GetOStream(size_t blob_size,TBlobOStreamFlags flags) const3752 CNcbiOstream* CRemoteQFB::GetOStream(size_t blob_size,
3753                                      TBlobOStreamFlags flags) const
3754 {
3755     const CVariant& var_val = *GetValue();
3756     try {
3757         IConnection* conn = m_Query.GetConnection()->CloneConnection();
3758         CDB_Connection* db_conn = conn->GetCDB_Connection();
3759         return new CWStream
3760                         (new CxBlobWriter(db_conn, var_val.GetBlobDescriptor(),
3761                                           blob_size, flags, false),
3762                          0, 0,
3763                          CRWStreambuf::fOwnWriter
3764                          | CRWStreambuf::fLogExceptions);
3765     }
3766     SDBAPI_CATCH_LOWLEVEL()
3767 }
3768 
GetOStream(size_t,TBlobOStreamFlags) const3769 CNcbiOstream& CQueryFieldImpl::GetOStream(size_t, TBlobOStreamFlags) const
3770 {
3771     SDBAPI_THROW(eUnsupported | Retriable(eRetriable_No),
3772                  string("Method is unsupported for this type of data: ")
3773                  + CDB_Object::GetTypeName(GetValue()->GetType(), false));
3774 }
3775 
GetOStream(size_t blob_size,TBlobOStreamFlags flags) const3776 CNcbiOstream& CQueryBlobImpl::GetOStream(size_t blob_size,
3777                                          TBlobOStreamFlags flags) const
3778 {
3779     m_OStream.reset(m_Basis->GetOStream(blob_size, flags));
3780     return *m_OStream;
3781 }
3782 
3783 CNcbiOstream&
GetOStream(size_t blob_size,TBlobOStreamFlags flags) const3784 CQuery::CField::GetOStream(size_t blob_size, TBlobOStreamFlags flags) const
3785 {
3786     return m_Impl->GetOStream(blob_size, flags);
3787 }
3788 
3789 CNcbiOstream&
GetOStream(size_t blob_size,EAllowLog log_it) const3790 CQuery::CField::GetOStream(size_t blob_size, EAllowLog log_it) const
3791 {
3792     return GetOStream(blob_size,
3793                       (log_it == eDisableLog) ? fBOS_SkipLogging : 0);
3794 }
3795 
GetBookmark(void) const3796 CBlobBookmark IQueryFieldBasis::GetBookmark(void) const
3797 {
3798     SDBAPI_THROW(eUnsupported | Retriable(eRetriable_No),
3799                  "Method requires a live field");
3800 }
3801 
GetBookmark(void) const3802 CBlobBookmark CRemoteQFB::GetBookmark(void) const
3803 {
3804     const CVariant& var_val = *GetValue();
3805     CRef<CBlobBookmarkImpl> bm
3806         (new CBlobBookmarkImpl(m_Query.GetDatabase(),
3807                                var_val.ReleaseBlobDescriptor()));
3808     return CBlobBookmark(bm);
3809 }
3810 
GetBookmark(void) const3811 CBlobBookmark CQueryFieldImpl::GetBookmark(void) const
3812 {
3813     SDBAPI_THROW(eUnsupported | Retriable(eRetriable_No),
3814                  string("Method is unsupported for this type of data: ")
3815                  + CDB_Object::GetTypeName(GetValue()->GetType(), false));
3816 }
3817 
GetBookmark(void) const3818 CBlobBookmark CQueryBlobImpl::GetBookmark(void) const
3819 {
3820     return m_Basis->GetBookmark();
3821 }
3822 
3823 CBlobBookmark
GetBookmark(void) const3824 CQuery::CField::GetBookmark(void) const
3825 {
3826     return m_Impl->GetBookmark();
3827 }
3828 
3829 
CQuery(void)3830 CQuery::CQuery(void)
3831 {}
3832 
CQuery(CDatabaseImpl * db_impl)3833 CQuery::CQuery(CDatabaseImpl* db_impl)
3834 {
3835     m_Impl.Reset(new CQueryImpl(db_impl));
3836 }
3837 
CQuery(const CQuery & q)3838 CQuery::CQuery(const CQuery& q)
3839     : m_Impl(q.m_Impl)
3840 {}
3841 
~CQuery(void)3842 CQuery::~CQuery(void)
3843 {}
3844 
3845 CQuery &
operator =(const CQuery & q)3846 CQuery::operator= (const CQuery& q)
3847 {
3848     m_Impl = q.m_Impl;
3849     return *this;
3850 }
3851 
3852 CQuery&
SetParameter(CTempString name,const string & value,ESDB_Type type,ESP_ParamType param_type)3853 CQuery::SetParameter(CTempString   name,
3854                      const string& value,
3855                      ESDB_Type     type /* = eSDB_String */,
3856                      ESP_ParamType param_type /* = eSP_In */)
3857 {
3858     m_Impl->SetParameter(name, value, type, param_type);
3859     return *this;
3860 }
3861 
3862 CQuery&
SetParameter(CTempString name,const char * value,ESDB_Type type,ESP_ParamType param_type)3863 CQuery::SetParameter(CTempString   name,
3864                      const char*   value,
3865                      ESDB_Type     type /* = eSDB_String */,
3866                      ESP_ParamType param_type /* = eSP_In */)
3867 {
3868     m_Impl->SetParameter(name, value, type, param_type);
3869     return *this;
3870 }
3871 
3872 CQuery&
SetParameter(CTempString name,Int8 value,ESDB_Type type,ESP_ParamType param_type)3873 CQuery::SetParameter(CTempString   name,
3874                      Int8          value,
3875                      ESDB_Type     type /* = eSDB_Int8 */,
3876                      ESP_ParamType param_type /* = eSP_In */)
3877 {
3878     m_Impl->SetParameter(name, value, type, param_type);
3879     return *this;
3880 }
3881 
3882 CQuery&
SetParameter(CTempString name,Int4 value,ESDB_Type type,ESP_ParamType param_type)3883 CQuery::SetParameter(CTempString   name,
3884                      Int4          value,
3885                      ESDB_Type     type /* = eSDB_Int4 */,
3886                      ESP_ParamType param_type /* = eSP_In */)
3887 {
3888     m_Impl->SetParameter(name, value, type, param_type);
3889     return *this;
3890 }
3891 
3892 CQuery&
SetParameter(CTempString name,short value,ESDB_Type type,ESP_ParamType param_type)3893 CQuery::SetParameter(CTempString   name,
3894                      short         value,
3895                      ESDB_Type     type /* = eSDB_Short */,
3896                      ESP_ParamType param_type /* = eSP_In */)
3897 {
3898     m_Impl->SetParameter(name, value, type, param_type);
3899     return *this;
3900 }
3901 
3902 CQuery&
SetParameter(CTempString name,unsigned char value,ESDB_Type type,ESP_ParamType param_type)3903 CQuery::SetParameter(CTempString   name,
3904                      unsigned char value,
3905                      ESDB_Type     type /* = eSDB_Byte */,
3906                      ESP_ParamType param_type /* = eSP_In */)
3907 {
3908     m_Impl->SetParameter(name, value, type, param_type);
3909     return *this;
3910 }
3911 
3912 CQuery&
SetParameter(CTempString name,float value,ESDB_Type type,ESP_ParamType param_type)3913 CQuery::SetParameter(CTempString   name,
3914                      float         value,
3915                      ESDB_Type     type /* = eSDB_Float */,
3916                      ESP_ParamType param_type /* = eSP_In */)
3917 {
3918     m_Impl->SetParameter(name, value, type, param_type);
3919     return *this;
3920 }
3921 
3922 CQuery&
SetParameter(CTempString name,double value,ESDB_Type type,ESP_ParamType param_type)3923 CQuery::SetParameter(CTempString   name,
3924                      double        value,
3925                      ESDB_Type     type /* = eSDB_Double */,
3926                      ESP_ParamType param_type /* = eSP_In */)
3927 {
3928     m_Impl->SetParameter(name, value, type, param_type);
3929     return *this;
3930 }
3931 
3932 CQuery&
SetParameter(CTempString name,const CTime & value,ESDB_Type type,ESP_ParamType param_type)3933 CQuery::SetParameter(CTempString   name,
3934                      const CTime&  value,
3935                      ESDB_Type     type /* = eSDB_DateTime */,
3936                      ESP_ParamType param_type /*= eSP_In */)
3937 {
3938     m_Impl->SetParameter(name, value, type, param_type);
3939     return *this;
3940 }
3941 
3942 CQuery&
SetParameter(CTempString name,bool value,ESDB_Type type,ESP_ParamType param_type)3943 CQuery::SetParameter(CTempString   name,
3944                      bool          value,
3945                      ESDB_Type     type /* = eSDB_Bit */,
3946                      ESP_ParamType param_type /* = eSP_In */)
3947 {
3948     m_Impl->SetParameter(name, value, type, param_type);
3949     return *this;
3950 }
3951 
3952 CQuery&
SetNullParameter(CTempString name,ESDB_Type type,ESP_ParamType param_type)3953 CQuery::SetNullParameter(CTempString   name,
3954                          ESDB_Type     type,
3955                          ESP_ParamType param_type /* = eSP_In */)
3956 {
3957     m_Impl->SetNullParameter(name, type, param_type);
3958     return *this;
3959 }
3960 
3961 const CQuery::CField&
GetParameter(CTempString name)3962 CQuery::GetParameter(CTempString name)
3963 {
3964     return m_Impl->GetParameter(name);
3965 }
3966 
3967 CQuery&
ClearParameter(CTempString name)3968 CQuery::ClearParameter(CTempString name)
3969 {
3970     m_Impl->ClearParameter(name);
3971     return *this;
3972 }
3973 
3974 CQuery&
ClearParameters(void)3975 CQuery::ClearParameters(void)
3976 {
3977     m_Impl->ClearParameters();
3978     return *this;
3979 }
3980 
3981 CQuery&
SetSql(CTempString sql)3982 CQuery::SetSql(CTempString sql)
3983 {
3984     m_Impl->SetSql(sql);
3985     return *this;
3986 }
3987 
3988 CQuery&
Execute(const CTimeout & timeout)3989 CQuery::Execute(const CTimeout& timeout)
3990 {
3991     m_Impl->Execute(timeout);
3992     return *this;
3993 }
3994 
3995 CQuery&
ExecuteSP(CTempString sp,const CTimeout & timeout)3996 CQuery::ExecuteSP(CTempString sp, const CTimeout& timeout)
3997 {
3998     m_Impl->ExecuteSP(sp, timeout);
3999     return *this;
4000 }
4001 
4002 void
Cancel(void)4003 CQuery::Cancel(void)
4004 {
4005     m_Impl->Cancel();
4006 }
4007 
4008 CQuery&
SingleSet(void)4009 CQuery::SingleSet(void)
4010 {
4011     m_Impl->SetIgnoreBounds(true);
4012     return *this;
4013 }
4014 
4015 CQuery&
MultiSet(void)4016 CQuery::MultiSet(void)
4017 {
4018     m_Impl->SetIgnoreBounds(false);
4019     return *this;
4020 }
4021 
4022 CQuery::CRow
GetTheOnlyRow(void)4023 CQuery::GetTheOnlyRow(void)
4024 {
4025     // Check prerequisite that the requested number of rows includes 1
4026     if (m_Impl->GetMinRowCount() > 1)
4027         NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
4028                    "Exactly one row requested while RequireRowCount() set "
4029                    "the minimum to " +
4030                    NStr::NumericToString(m_Impl->GetMinRowCount()));
4031     if (m_Impl->GetMaxRowCount() < 1)
4032         NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
4033                    "Exactly one row requested while RequireRowCount() set "
4034                    "the maximum to " +
4035                    NStr::NumericToString(m_Impl->GetMaxRowCount()));
4036 
4037     CQuery::iterator        q_it = begin();
4038     if (q_it == end())
4039         NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
4040                    "Expected exactly one row, but none are available");
4041 
4042     CQuery::CRow            row(*q_it);
4043 
4044     ++q_it;
4045     if (q_it != end())
4046         NCBI_THROW(CSDB_Exception, eInconsistent | Retriable(eRetriable_No),
4047                    "Expected exactly one row, but more than one are available");
4048 
4049     VerifyDone();
4050     return row;
4051 }
4052 
4053 unsigned int
GetResultSetNo(void) const4054 CQuery::GetResultSetNo(void) const
4055 {
4056     return m_Impl->GetResultSetNo();
4057 }
4058 
4059 unsigned int
GetRowNo(EHowMuch how_much) const4060 CQuery::GetRowNo(EHowMuch how_much) const
4061 {
4062     return m_Impl->GetRowNo(how_much);
4063 }
4064 
4065 int
GetRowCount(void) const4066 CQuery::GetRowCount(void) const
4067 {
4068     return m_Impl->GetRowCount();
4069 }
4070 
4071 int
GetStatus(void) const4072 CQuery::GetStatus(void) const
4073 {
4074     return m_Impl->GetStatus();
4075 }
4076 
4077 const list<string>&
GetPrintOutput(void) const4078 CQuery::GetPrintOutput(void) const
4079 {
4080     return m_Impl->GetPrintOutput();
4081 }
4082 
4083 bool
HasMoreResultSets(void)4084 CQuery::HasMoreResultSets(void)
4085 {
4086     return m_Impl->HasMoreResultSets();
4087 }
4088 
4089 void
PurgeResults(void)4090 CQuery::PurgeResults(void)
4091 {
4092     m_Impl->PurgeResults();
4093 }
4094 
4095 void
RequireRowCount(unsigned int n)4096 CQuery::RequireRowCount(unsigned int n)
4097 {
4098     m_Impl->RequireRowCount(n, n);
4099 }
4100 
4101 void
RequireRowCount(unsigned int min_rows,unsigned int max_rows)4102 CQuery::RequireRowCount(unsigned int min_rows, unsigned int max_rows)
4103 {
4104     m_Impl->RequireRowCount(min_rows, max_rows);
4105 }
4106 
4107 void
VerifyDone(EHowMuch how_much)4108 CQuery::VerifyDone(EHowMuch how_much)
4109 {
4110     m_Impl->VerifyDone(how_much);
4111 }
4112 
4113 unsigned int
GetTotalColumns(void) const4114 CQuery::GetTotalColumns(void) const
4115 {
4116     return m_Impl->GetTotalColumns();
4117 }
4118 
4119 string
GetColumnName(unsigned int col) const4120 CQuery::GetColumnName(unsigned int col) const
4121 {
4122     return m_Impl->GetColumnName(col);
4123 }
4124 
4125 ESDB_Type
GetColumnType(unsigned int col) const4126 CQuery::GetColumnType(unsigned int col) const
4127 {
4128     return m_Impl->GetColumnType(col);
4129 }
4130 
4131 CQuery::CRowIterator
begin(void) const4132 CQuery::begin(void) const
4133 {
4134     m_Impl.GetNCPointer()->BeginNewRS();
4135     return CRowIterator(m_Impl.GetNCPointer(), false);
4136 }
4137 
4138 CQuery::CRowIterator
end(void) const4139 CQuery::end(void) const
4140 {
4141     return CRowIterator(m_Impl.GetNCPointer(), true);
4142 }
4143 
4144 inline
x_Init(const CDiagCompileInfo &,const string &,const CException * prev_exception,EDiagSev)4145 void CSDB_DeadlockException::x_Init(const CDiagCompileInfo&, const string&,
4146                                     const CException* prev_exception, EDiagSev)
4147 {
4148     _ASSERT(dynamic_cast<const CDB_DeadlockEx*>(prev_exception));
4149 }
4150 
4151 inline
x_InitErrCode(CException::EErrCode err_code)4152 void CSDB_DeadlockException::x_InitErrCode(CException::EErrCode err_code)
4153 {
4154     _ASSERT((TErrCode)err_code == (TErrCode)CSDB_Exception::eLowLevel);
4155 }
4156 
4157 
4158 END_NCBI_SCOPE
4159