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(§ions);
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