1 #ifndef CPPCORE__DBAPI__SIMPLE__SDBAPI__HPP
2 #define CPPCORE__DBAPI__SIMPLE__SDBAPI__HPP
3 
4 /* $Id: sdbapi.hpp 620233 2020-11-18 14:47:29Z ucko $
5  * ===========================================================================
6  *
7  *                            PUBLIC DOMAIN NOTICE
8  *               National Center for Biotechnology Information
9  *
10  *  This software/database is a "United States Government Work" under the
11  *  terms of the United States Copyright Act.  It was written as part of
12  *  the author's official duties as a United States Government employee and
13  *  thus cannot be copyrighted.  This software/database is freely available
14  *  to the public for use. The National Library of Medicine and the U.S.
15  *  Government have not placed any restriction on its use or reproduction.
16  *
17  *  Although all reasonable efforts have been taken to ensure the accuracy
18  *  and reliability of the software and data, the NLM and the U.S.
19  *  Government do not and cannot warrant the performance or results that
20  *  may be obtained by using this software or data. The NLM and the U.S.
21  *  Government disclaim all warranties, express or implied, including
22  *  warranties of performance, merchantability or fitness for any particular
23  *  purpose.
24  *
25  *  Please cite the author in any work or product based on this material.
26  *
27  * ===========================================================================
28  *
29  * Author:  Michael Kholodov
30  *
31  * File Description:  Simple database API interface
32  *
33  */
34 
35 #include <string>
36 #include <map>
37 #include <vector>
38 
39 #include <corelib/ncbitime.hpp>
40 #include <corelib/rwstream.hpp>
41 #include <util/ncbi_url.hpp>
42 #include <dbapi/dbapi.hpp>
43 #include <dbapi/error_codes.hpp>
44 
45 
46 BEGIN_NCBI_SCOPE
47 
48 
49 class CSDBAPI;
50 class CDatabase;
51 class CQuery;
52 class CBlobBookmark;
53 class CVariant;
54 class CDatabaseImpl;
55 class CBulkInsertImpl;
56 class CQueryImpl;
57 class CQueryFieldImpl;
58 class CRemoteQFB;
59 class CBlobBookmarkImpl;
60 class IResultSet;
61 struct SQueryRSMetaData;
62 class CBlobStoreDynamic;
63 class CBlobStoreStatic;
64 
65 
66 /// Exception class used throughout the API
67 class CSDB_Exception : public CException
68 {
69 public:
70     enum EErrCode {
71         eURLFormat,     ///< Incorrectly formated URL is used to create
72                         ///< CSDB_ConnectionParam
73         eClosed,        ///< CDatabase/CQuery/CBulkInsert is tried to be used
74                         ///< when no connection is opened
75         eStarted,       ///< CBulkInsert has already started to send data, no
76                         ///< changes in meta-information can be made
77         eNotInOrder,    ///< Columns cannot be bound to CBulkInsert randomly
78         eInconsistent,  ///< Operation logically incorrect is attempted to be
79                         ///< made (increase past end() iterator, incorrect
80                         ///< number of values in CBulkInsert etc)
81         eUnsupported,   ///< Unsupported data type conversion is requested
82         eOutOfBounds,   ///< Conversion of string to integer type exceeded
83                         ///< limits of requested type
84         eNotExist,      ///< Field/parameter with given name/position does not
85                         ///< exist
86         eLowLevel,      ///< Exception from low level DBAPI was re-thrown with
87                         ///< this exception class
88         eWrongParams    ///< Wrong parameters provided to the method
89     };
90 
91     /// Translate from the error code value to its string representation.
92     virtual const char* GetErrCodeString(void) const override;
93 
94     /// Returns any underlying DBAPI exception, or else NULL.
95     const CDB_Exception* GetDBException(void) const;
96 
97     /// Returns any underlying DBAPI error code, or else CException::eInvalid.
98     CDB_Exception::TErrCode GetDBErrCode(void) const;
99     /// Returns any underlying DBAPI error code string, or else "eInvalid".
100     const char* GetDBErrCodeString(void) const;
101 
102     const string& GetServerName(void) const;
103     const string& GetUserName(void) const;
104     const string& GetDatabaseName(void) const;
105 
106     /// Returns any additional context (typically, the relevant SQL
107     /// statement or database operation).
108     const string& GetExtraMsg(void) const;
109 
110     void ReportExtra(ostream& os) const override;
111 
112     /// Retrieve info about ability to retry an action caused the exception
113     virtual ERetriable GetRetriable(void) const override;
114 
115     // Standard exception boilerplate code.
116     CSDB_Exception(const CDiagCompileInfo& info,
117                    const CException* prev_exception,
118                    EErrCode err_code,
119                    const CDB_Exception::SMessageInContext& message,
120                    EDiagSev severity = eDiag_Error)
121         : CException(info, prev_exception, (CException::EErrCode)err_code,
122                      message.message, severity)
123         , m_Context(message.context)
124         NCBI_EXCEPTION_DEFAULT_IMPLEMENTATION(CSDB_Exception, CException);
125 
126 public:
CSDB_Exception(const CDiagCompileInfo & info,const CException * prev_exception,const CExceptionArgs<EErrCode> & args,const CDB_Exception::SMessageInContext & message)127     CSDB_Exception(const CDiagCompileInfo& info,
128                    const CException* prev_exception,
129                    const CExceptionArgs<EErrCode>& args,
130                    const CDB_Exception::SMessageInContext& message)
131         : CException(info, prev_exception, message.message,
132                      args.GetSeverity(), 0)
133         , m_Context(message.context)
134     {
135         x_Init(info, message.message, prev_exception, args.GetSeverity());
136         x_InitArgs(args);
137         x_InitErrCode((CException::EErrCode) args.GetErrCode());
138     }
139 
140 protected:
141     CSDB_Exception(const CDiagCompileInfo& info,
142                    const CException* prev_exception,
143                    const CDB_Exception::SMessageInContext& message,
144                    EDiagSev severity = eDiag_Error,
145                    CException::TFlags flags = 0);
146 
147     void x_Init(const CDiagCompileInfo& info, const string& message,
148                 const CException* prev_exception, EDiagSev severity) override;
149 
150     void x_Assign(const CException& src) override;
151 
152 private:
153     CConstRef<CDB_Exception::SContext> m_Context;
154 };
155 
156 
157 class CSDB_DeadlockException : public CSDB_Exception
158 {
159 public:
160     void x_Init(const CDiagCompileInfo& info, const string& message,
161                 const CException* prev_exception, EDiagSev severity) override;
162 
163     void x_InitErrCode(CException::EErrCode err_code) override;
164 
165     NCBI_EXCEPTION_DEFAULT(CSDB_DeadlockException, CSDB_Exception);
166 };
167 
168 
169 /// Database types used throughout API.  Each corresponds most naturally to
170 /// a common Transact-SQL type, but implicit conversion is also possible in
171 /// many cases, including to and from types with no exact SDBAPI equivalent.
172 enum ESDB_Type {
173     eSDB_Byte,       ///< Like Transact-SQL BYTE (unsigned 8-bit integer)
174     eSDB_Short,      ///< Like Transact-SQL SMALLINT (signed 16-bit integer)
175     eSDB_Int4,       ///< Like Transact-SQL INT (signed 32-bit integer)
176     eSDB_Int8,       ///< Like Transact-SQL BIGINT (signed 64-bit integer)
177     eSDB_Float,      ///< Like Transact-SQL REAL (32-bit floating point value)
178     eSDB_Double,     ///< Like Transact-SQL DOUBLE (64-bit floating point value)
179     eSDB_String,     ///< Like Transact-SQL VARCHAR(N)
180     eSDB_StringUCS2, ///< Like Transact-SQL NVARCHAR(N)
181     eSDB_Binary,     ///< Like Transact-SQL VARBINARY(N)
182     eSDB_DateTime,   ///< Like Transact-SQL DATETIME
183     eSDB_BigDateTime, ///< Like Transact-SQL DATETIME2 (MS) or BIGDATETIME (Syb)
184     eSDB_Text,       ///< Like Transact-SQL TEXT
185     eSDB_TextUCS2,   ///< Like Transact-SQL NTEXT
186     eSDB_Image,      ///< Like Transact-SQL IMAGE
187     eSDB_StringMax,  ///< Like Transact-SQL VARCHAR(MAX)
188     eSDB_StringMaxUCS2, ///< Like Transact-SQL NVARCHAR(MAX)
189     eSDB_BinaryMax,  ///< Like Transact-SQL VARBINARY(MAX)
190     eSDB_Bit         ///< Like Transact-SQL BIT
191 };
192 
193 /// Stored procedure and statement parameter types
194 enum ESP_ParamType {
195     eSP_In,     ///< Parameter is only passed to server, no return is expected
196     eSP_InOut   ///< Parameter can be returned from stored procedure
197 };
198 
199 /// Flags for NewBlobStore methods
200 enum ENewBlobStoreFlags {
201     fNBS_ZLib         = 1 << 0, ///< Use zlib (gzip) compression
202     fNBS_BZLib        = 1 << 1, ///< Use bzlib (bzip2) compression
203     fNBS_LogIt        = 1 << 2, ///< Enable transaction logs
204     fNBS_IsText       = 1 << 3, ///< Blobs have type TEXT or [N]VARCHAR(MAX)
205     fNBS_Preallocated = 1 << 4  ///< Don't create rows or clean up any excess
206 };
207 DECLARE_SAFE_FLAGS_TYPE(ENewBlobStoreFlags, TNewBlobStoreFlags);
208 DECLARE_SAFE_FLAGS(ENewBlobStoreFlags);
209 
210 /// (S)DBAPI_TRANSACTION glue for CDatabase.
211 CAutoTrans::CSubject DBAPI_MakeTrans(CDatabase& db);
212 /// (S)DBAPI_TRANSACTION glue for CQuery.
213 CAutoTrans::CSubject DBAPI_MakeTrans(CQuery& query);
214 
215 /// Establish an automatically managed anonymous SQL transaction on
216 /// the specified object's connection for the duration of the
217 /// immediately following code block: SDBAPI_TRANSACTION(obj) { ... }
218 ///
219 /// Automatically COMMIT upon reaching the end of the block normally.
220 /// Automatically ROLLBACK upon leaving the block early, via a break
221 /// or return statement or uncaught exception.  A client crash or
222 /// severed connection will trigger an implicit server-side ROLLBACK.
223 ///
224 /// Nested transactions are possible, and will use savepoints for
225 /// inner transactions so they can fail cleanly (without also
226 /// canceling outer transactions).
227 #define SDBAPI_TRANSACTION(obj) DBAPI_TRANSACTION(obj)
228 
229 /// Object used to execute queries and stored procedures on the database
230 /// server and retrieve result sets.
231 class CQuery
232 {
233 public:
234     /// Empty constructor of query object.
235     /// Object created this way cannot be used for anything except assigning
236     /// from the other query object.
237     CQuery(void);
238     ~CQuery(void);
239 
240     /// Copying of query object from other one.
241     /// The copy of query object behaves with the same internal result set as
242     /// the original object. So that if you increment iterator created from
243     /// one object it will move to the next row in another query object too.
244     CQuery(const CQuery& q);
245     CQuery& operator= (const CQuery& q);
246 
247     ///  Allow transaction log (general, to avoid using bools).
248     enum EAllowLog {
249         eDisableLog,     ///< Disables log.
250         eEnableLog       ///< Enables log.
251     };
252 
253     class CRow;
254 
255     /// Class representing value in result set or output parameter of stored
256     /// procedure.
257     class CField
258     {
259     public:
260         CField(const CField& f);
261         ~CField();
262 
263         /// Get value as UTF-8 string.
264         /// Any underlying database type will be converted to a string
265         /// using the variable-width UTF-8 encoding.
266         /// @sa CUtf8::AsBasicString, CUtf8::AsSingleByteString
267         string AsString(void) const;
268         /// Get value as single byte.
269         /// If underlying database type is string or text then attempt to
270         /// convert it to byte will be made. If the value cannot be converted
271         /// to integer or resulting integer cannot fit into byte exception
272         /// will be thrown.
273         unsigned char AsByte(void) const;
274         /// Get value as short integer.
275         /// If underlying database type is string or text then attempt to
276         /// convert it to byte will be made. If the value cannot be converted
277         /// to integer or resulting integer cannot fit into short data type
278         /// exception will be thrown.
279         short AsShort(void) const;
280         /// Get value as 4-byte integer.
281         /// If underlying database type is string or text then attempt to
282         /// convert it to integer will be made. If the value cannot be
283         /// converted to integer or resulting integer cannot fit into
284         /// 4 bytes exception will be thrown.
285         Int4 AsInt4(void) const;
286         /// Get value as 8-byte integer.
287         /// If underlying database type is string or text then attempt to
288         /// convert it to integer will be made. If the value cannot be
289         /// converted to integer exception will be thrown.
290         Int8 AsInt8(void) const;
291         /// Get value as float.
292         /// If underlying database type is string or text then attempt to
293         /// convert it to double will be made. If the value cannot be
294         /// converted to double exception will be thrown.
295         float AsFloat(void) const;
296         /// Get value as double.
297         /// If underlying database type is string or text then attempt to
298         /// convert it to double will be made. If the value cannot be
299         /// converted to double exception will be thrown.
300         double AsDouble(void) const;
301         /// Get value as bool.
302         /// If underlying database type is string or text then attempt to
303         /// convert it to integer will be made. If the value cannot be
304         /// converted to integer or resulting integer is not equal to 0 or 1
305         /// (also if underlying type is integer and it's not equal to 0 or 1)
306         /// exception will be thrown.
307         bool AsBool(void) const;
308         /// Get value as CTime.
309         /// If underlying database type is string or text then attempt to
310         /// convert it to CTime will be made. If the value cannot be
311         /// converted to CTime exception will be thrown.
312         CTime AsDateTime(void) const;
313         /// Get value as input stream.
314         /// Only Text and Image data can be read via stream. Returned stream
315         /// should be read completely before any other field is attempted
316         /// to be read.
317         CNcbiIstream& AsIStream(void) const;
318         /// Get value as vector of bytes.
319         /// Only Text and Image data can be read as vector. Returned vector
320         /// should be read completely before any other field is attempted
321         /// to be read.
322         const vector<unsigned char>& AsVector(void) const;
323 
324         /// Check if value is NULL.
325         bool IsNull(void) const;
326 
327         /// Get a blob output stream, on top of a cloned connection.
328         /// The original connection should NOT have an active transaction.
329         ///
330         /// NOTE: You won't be able to write a blob using this method if its
331         /// value is set to NULL in the database. To use this method you should
332         /// pre-set blob value to empty string or anything else of your choice.
333         ///
334         /// @param blob_size
335         ///   blob_size is the size of the BLOB to be written.
336         /// @param flags
337         ///   @see EBlobOStreamFlags.
338         /// @param log_it
339         ///    Enables transaction log for BLOB (enabled by default).
340         ///    Make sure you have enough log segment space, or disable it.
341         ///
342         /// @deprecated Please use CBlobBookmark::GetOStream instead.
343         ///
344         /// @sa CBlobBookmark::GetOStream
345         NCBI_DEPRECATED
346         CNcbiOstream& GetOStream(size_t blob_size, TBlobOStreamFlags flags = 0)
347             const;
348         NCBI_DEPRECATED
349         CNcbiOstream& GetOStream(size_t blob_size, EAllowLog log_it) const;
350 
351         /// Get bookmark for the blob. This bookmark can be used to change
352         /// blob data later when all results from this query are processed.
353         ///
354         /// NOTE: You won't be able to write a blob using this method if its
355         /// value is set to NULL in the database. To use this method you should
356         /// pre-set blob value to empty string or anything else of your choice.
357         /// Blob value should be set before the query execution, i.e. query
358         /// should return non-NULL value. Setting non-NULL value after query
359         /// execution won't work.
360         ///
361         /// @deprecated Please use CDatabase::NewBookmark instead.
362         ///
363         /// @sa CDatabase::NewBookmark
364         NCBI_DEPRECATED
365         CBlobBookmark GetBookmark(void) const;
366 
367     private:
368         friend class CQueryImpl;
369         friend class CQueryFieldImpl;
370         friend class CRow;
371 
372         void x_Detach(void);
373 
374         /// Create field for particular column number in result set
375         CField(CQueryImpl* q, unsigned int col_num);
376         /// Create field for particular parameter in the query
377         CField(CQueryImpl* q, CVariant* v, ESP_ParamType param_type);
378 
379         CRef<CQueryFieldImpl> m_Impl;
380     };
381 
382     /// A full row of result data.
383     class CRow
384     {
385     public:
386         CRow(const CRow& r);
387         ~CRow();
388 
389         /// Get column value by its number.
390         /// All columns are numbered starting with 1.
391         const CField& operator[](unsigned int col) const;
392         /// Get column value by its name.
393         const CField& operator[](CTempString col) const;
394 
395         /// Get number of columns in the row.
396         unsigned int GetTotalColumns(void) const;
397         /// Get name of the column with given number in the row.
398         /// All columns are numbered starting with 1.
399         const string& GetColumnName(unsigned int col) const;
400         /// Get type of the column with given number in the row.
401         /// All columns are numbered starting with 1.
402         ESDB_Type GetColumnType(unsigned int col) const;
403 
404     private:
405         friend class CQueryImpl;
406 
407         CRow(void);
408         void x_Reset(CQueryImpl& q, IResultSet& rs);
409 
410         const CDB_Exception::SContext& x_GetContext(void) const;
411         void x_CheckColumnNumber(unsigned int col) const;
412 
413         vector<CField>         m_Fields;
414         CRef<SQueryRSMetaData> m_MetaData;
415     };
416 
417     /// Iterator class doing main navigation through result sets.
418     class CRowIterator
419     {
420     public:
421         /// Empty constructor of iterator.
422         /// Object constructed this way cannot be used for anything except
423         /// assigning from another iterator object.
424         CRowIterator(void);
425         ~CRowIterator(void);
426 
427         /// Copying of iterator object.
428         /// Different copies of iterator won't point to different rows in the
429         /// result set, i.e. incrementing one iterator will change values
430         /// returned from the other.
431         CRowIterator(const CRowIterator& ri);
432         CRowIterator& operator= (const CRowIterator& ri);
433 
434         /// Get row number currently active.
435         /// Row numbers are assigned consecutively to each row in all result
436         /// sets returned starting with 1. Row number is not reset after
437         /// passing result set boundary.
438         unsigned int GetRowNo(void) const;
439         /// Get number of currently active result set.
440         /// All result sets are numbered starting with 1.
441         unsigned int GetResultSetNo(void) const;
442 
443         /// Get column value by its number.
444         /// All columns are numbered starting with 1.
445         const CField& operator[](unsigned int col) const;
446         /// Get column value by its name.
447         const CField& operator[](CTempString col) const;
448 
449         /// Get number of columns in the current result set.
450         unsigned int GetTotalColumns(void) const;
451         /// Get name of the column with given number in the current result set.
452         /// All columns are numbered starting with 1.
453         string GetColumnName(unsigned int col) const;
454         /// Get type of the column with given number in the current result set.
455         /// All columns are numbered starting with 1.
456         ESDB_Type GetColumnType(unsigned int col) const;
457 
458         /// Comparison of iterators.
459         /// Only comparison with end() iterator makes sense. Otherwise all
460         /// iterators created from the same CQuery object will be equal.
461         bool operator==(const CRowIterator& ri) const;
462         bool operator!=(const CRowIterator& ri) const;
463 
464         /// Advance iterator to the next row in the result set.
465         CRowIterator& operator++(void);
466         CRowIterator  operator++(int);
467 
468         /// Get the full row.  (The caller is welcome to keep a copy.)
469         const CRow& operator*(void) const;
470 
471     private:
472         friend class CQuery;
473 
474         /// Create iterator for the query.
475         /// Two types of iterators are possible - pointer to end and pointer
476         /// to anything else.
477         CRowIterator(CQueryImpl* q, bool is_end);
478 
479         const CDB_Exception::SContext& x_GetContext(void) const;
480 
481         /// Query iterator was created for
482         CRef<CQueryImpl> m_Query;
483         /// Flag showing whether this is constant pointer to the end or
484         /// pointer to some particular row.
485         bool             m_IsEnd;
486     };
487 
488     typedef CRowIterator iterator;
489     typedef CRowIterator const_iterator;
490 
491 
492     /// Assign string value to the parameter.
493     /// If data type requested is not string then attempt to do conversion
494     /// will be made. If conversion is impossible exception will be thrown.
495     CQuery& SetParameter(CTempString   name,
496                          const string& value,
497                          ESDB_Type     type = eSDB_String,
498                          ESP_ParamType param_type = eSP_In);
499     /// Assign string value to the parameter.
500     /// If data type requested is not string then attempt to do conversion
501     /// will be made. If conversion is impossible exception will be thrown.
502     CQuery& SetParameter(CTempString   name,
503                          const char*   value,
504                          ESDB_Type     type = eSDB_String,
505                          ESP_ParamType param_type = eSP_In);
506     /// Assign 8-byte integer value to the parameter.
507     /// If data type requested is not 8-byte integer then attempt to do
508     /// conversion will be made. If conversion is impossible exception
509     /// will be thrown.
510     CQuery& SetParameter(CTempString   name,
511                          Int8          value,
512                          ESDB_Type     type = eSDB_Int8,
513                          ESP_ParamType param_type = eSP_In);
514     /// Assign 4-byte integer value to the parameter.
515     /// If data type requested is not 4-byte integer then attempt to do
516     /// conversion will be made. If conversion is impossible exception
517     /// will be thrown.
518     CQuery& SetParameter(CTempString   name,
519                          Int4          value,
520                          ESDB_Type     type = eSDB_Int4,
521                          ESP_ParamType param_type = eSP_In);
522 #if !NCBI_INT8_IS_LONG
523     CQuery& SetParameter(CTempString   name,
524                          long          value,
525                          ESDB_Type     type = eSDB_Int4,
526                          ESP_ParamType param_type = eSP_In);
527 #endif
528     /// Assign short integer value to the parameter.
529     /// If data type requested is not short integer then attempt to do
530     /// conversion will be made. If conversion is impossible exception
531     /// will be thrown.
532     CQuery& SetParameter(CTempString   name,
533                          short         value,
534                          ESDB_Type     type = eSDB_Short,
535                          ESP_ParamType param_type = eSP_In);
536     /// Assign byte value to the parameter.
537     /// If data type requested is not byte then attempt to do
538     /// conversion will be made. If conversion is impossible exception
539     /// will be thrown.
540     CQuery& SetParameter(CTempString   name,
541                          unsigned char value,
542                          ESDB_Type     type = eSDB_Byte,
543                          ESP_ParamType param_type = eSP_In);
544     /// Assign float value to the parameter.
545     /// If data type requested is not float then attempt to do
546     /// conversion will be made. If conversion is impossible exception
547     /// will be thrown.
548     CQuery& SetParameter(CTempString   name,
549                          float         value,
550                          ESDB_Type     type = eSDB_Float,
551                          ESP_ParamType param_type = eSP_In);
552     /// Assign double value to the parameter.
553     /// If data type requested is not double then attempt to do
554     /// conversion will be made. If conversion is impossible exception
555     /// will be thrown.
556     CQuery& SetParameter(CTempString   name,
557                          double        value,
558                          ESDB_Type     type = eSDB_Double,
559                          ESP_ParamType param_type = eSP_In);
560     /// Assign CTime value to the parameter.
561     /// If data type requested is not datetime then attempt to do
562     /// conversion will be made. If conversion is impossible exception
563     /// will be thrown.
564     CQuery& SetParameter(CTempString   name,
565                          const CTime&  value,
566                          ESDB_Type     type = eSDB_DateTime,
567                          ESP_ParamType param_type = eSP_In);
568     /// Assign bool value to the parameter.
569     /// If data type requested is not byte then attempt to do
570     /// conversion will be made. If conversion is impossible exception
571     /// will be thrown.
572     CQuery& SetParameter(CTempString   name,
573                          bool          value,
574                          ESDB_Type     type = eSDB_Bit,
575                          ESP_ParamType param_type = eSP_In);
576     /// Assign null value to the parameter.
577     /// Data type should be given explicitly to show which type of data should
578     /// be sent to server.
579     CQuery& SetNullParameter(CTempString   name,
580                              ESDB_Type     type,
581                              ESP_ParamType param_type = eSP_In);
582     /// Declare an output-only parameter.
583     /// Equivalent for now to calling SetNullParameter with a
584     /// param_type value of eSP_InOut because MSSQL and Sybase (and
585     /// hence FreeTDS) don't support true output-only parameters.
586     /// However, if SDBAPI ever gains support for database engines
587     /// with this feature, this method will arrange to make use of it
588     /// as appropriate.
589     CQuery& SetOutputParameter(CTempString name, ESDB_Type type);
590 
591     /// Get value of the parameter.
592     /// For eSP_In parameter value set to it will always be returned. For
593     /// eSP_InOut parameter value set to it will be returned before stored
594     /// procedure execution and value returned from procedure after executing
595     /// it and reading its row results (or confirming that it produced none).
596     /// Throw an exception if no such parameter exists, or if it's an output
597     /// parameter but not yet available because the caller hasn't finished
598     /// processing results.
599     const CField& GetParameter(CTempString name);
600 
601     /// Remove parameter with given name from parameter list.
602     CQuery& ClearParameter(CTempString name);
603     /// Remove all parameters from parameter list.
604     CQuery& ClearParameters(void);
605 
606     /// Set current sql statement.
607     /// Method does not clear parameter list and doesn't purge result sets
608     /// left from previous query execution.
609     CQuery& SetSql(CTempString sql);
610     /// Explicitly execute sql statement.
611     /// All result sets left from previous statement or stored procedure
612     /// execution are purged.  The query reverts to SingleSet mode, with
613     /// no row count requirements.
614     CQuery& Execute(const CTimeout& timeout = CTimeout(CTimeout::eDefault));
615     /// Execute stored procedure with given name.
616     /// All result sets left from previous statement or stored procedure
617     /// execution are purged.  The query reverts to SingleSet mode, with
618     /// no row count requirements.
619     CQuery& ExecuteSP(CTempString sp,
620                       const CTimeout& timeout = CTimeout(CTimeout::eDefault));
621 
622     /// Cancel the current statement or procedure call.  May be called
623     /// asynchronously to force a "timeout" within one second.
624     void Cancel(void);
625 
626     /// Get number of rows read after statement execution.
627     /// This number is available only when all result sets are read or purged
628     /// by call to PurgeResults().  At all other times, the method throws an
629     /// exception.
630     int GetRowCount(void) const;
631     /// Get return status of stored procedure.
632     /// Status is available only if stored procedure is executed via
633     /// ExecuteSP() method and all result sets returned from the procedure are
634     /// read or purged by call to PurgeResults().
635     int GetStatus(void) const;
636     /// Get any PRINT output from the latest procedure call (or statement).
637     /// This output may be incomplete until the caller has explicitly read
638     /// or purged all result sets.
639     ///
640     /// @note Many actions can invalidate this output, even working with
641     /// other CQuery objects associated with the same CDatabase object or a
642     /// normal (non-clone) copy thereof.
643     const list<string>& GetPrintOutput(void) const;
644 
645     /// Check if any more result sets are available for reading.
646     /// Advances to the next result set purging all remaining rows in current
647     /// one if reading of it was already started (begin() method was called).
648     bool HasMoreResultSets(void);
649 
650     /// Purge all remaining result sets; fill in all remaining parameter
651     /// results.
652     void PurgeResults(void);
653 
654     /// Whether to consider just the current result set or all result
655     /// sets, in MultiSet mode.  (In SingleSet mode, always consider all.)
656     enum EHowMuch {
657         eThisResultSet,
658         eAllResultSets
659     };
660 
661     /// Indicate precisely how many rows the active query should return.
662     /// In MultiSet mode, the requirement applies to individual result
663     /// sets.  (Callers may specify the requirement for each set as it
664     /// comes up, or let it carry over unchanged.)  Any call to this
665     /// method must follow Execute or ExecuteSP, which reset any such
666     /// requirements.
667     void RequireRowCount(unsigned int n);
668     /// Indicate the minimum and maximum number of rows the active query
669     /// should return.  In MultiSet mode, the requirement applies to
670     /// individual result sets.  (Callers may specify the requirement
671     /// for each set as it comes up, or let it carry over unchanged.)
672     /// Any call to this method must follow Execute or ExecuteSP, which
673     /// reset any such requirements.
674     /// @param min_rows
675     ///  Minimum valid row count.
676     /// @param max_rows
677     ///  Maximum valid row count.  (kMax_Auto for no limit.)
678     void RequireRowCount(unsigned int min_rows, unsigned int max_rows);
679     /// Ensure that no unread rows or parameter results remain, and that
680     /// the total number of rows satisfies any constraints specified by
681     /// RequireRowCount.  Throw an exception (after purging any unread
682     /// rows) if not.
683     void VerifyDone(EHowMuch how_much = eThisResultSet);
684 
685     /// Get total number of columns in the current result set
686     unsigned int GetTotalColumns(void) const;
687     /// Get name of the column with given number in the current result set.
688     /// All columns are numbered starting with 1.
689     string GetColumnName(unsigned int col) const;
690     /// Get type of the column with given number in the current result set
691     /// All columns are numbered starting with 1.
692     ESDB_Type GetColumnType(unsigned int col) const;
693     /// Get number of currently active result set.
694     /// All result sets are numbered starting with 1.
695     unsigned int GetResultSetNo(void) const;
696     /// Get row number currently active.
697     /// Row numbers are assigned consecutively to each row in all result
698     /// sets returned starting with 1.  With eAllResultSets (default) or
699     /// in MultiSet mode, row number is not reset after passing result set
700     /// boundary.
701     unsigned int GetRowNo(EHowMuch how_much = eAllResultSets) const;
702 
703     /// Convert this query to work like only one result set was returned
704     /// effectively merging all result sets together. If some result sets
705     /// were already read then all the remaining result sets will be merged.
706     /// Method impacts not only this CQuery object and all iterators created
707     /// from it but all copies of this CQuery object too. Result sets only
708     /// from recently executed statement are affected. After re-execution of a
709     /// statement default behavior is used - to not merge different result
710     /// sets.
711     CQuery& SingleSet(void);
712     /// Convert this query to not merge different result sets, i.e. iterator
713     /// will be equal to end() at the end of each result set and to switch to
714     /// the next one you'll have to call begin() again.
715     /// Method impacts not only this CQuery object and all iterators created
716     /// from it but all copies of this CQuery object too.
717     CQuery& MultiSet(void);
718 
719     /// Start iterating through next result set.
720     /// If a query was supplied but not explicitly executed, automatically
721     /// execute it before proceeding.  If iteration was already in
722     /// progress, purge the remainder of the current result set and advance
723     /// to the next, if there is one.
724     CRowIterator begin(void) const;
725     /// Get iterator pointing to the end of the current result set or to the
726     /// end of all result sets (depending on the setting changed with
727     /// SingleSet() and MultiSet()). Method cannot be used to take two
728     /// different iterators - one for the end of result set and one for the
729     /// end of all result sets. Which end is pointed to depends on the last
730     /// call to SingleSet() or MultiSet() even if it was made after call to
731     /// this method.
732     CRowIterator end(void) const;
733 
734     /// Provides the only row for the executed query.
735     /// It makes sure that there is exactly one row available and that the
736     /// required row count range includes 1.
737     /// VerifyDone() is called in the method so the user does not need to call
738     /// it.
739     /// In case of any problems a CSDB_Exception exception is generated.
740     CRow GetTheOnlyRow(void);
741 
742 private:
743     friend class CDatabase;
744 
745     friend CAutoTrans::CSubject DBAPI_MakeTrans(CQuery& query);
746 
747     /// Create query object for given database object
748     CQuery(CDatabaseImpl* db_impl);
749 
750 
751     /// Query implementation object
752     CRef<CQueryImpl> m_Impl;
753 };
754 
755 
756 /// Object used to store bookmarks to blobs to be changed later.
757 ///
758 /// @sa CQuery::CField::GetBookmark, CDatabase::NewBookmark
759 class CBlobBookmark
760 {
761 public:
762     /// Blob type (if known).
763     enum EBlobType {
764         eUnknown,
765         eText,
766         eBinary
767     };
768 
769     /// Get Blob output stream. Blob will be updated using the same
770     /// database connection which this bookmark was create on.
771     /// Thus it shouldn't have any active queries or bulk inserts by the time
772     /// this method is called and until the stream is not used anymore.
773     ///
774     /// @param blob_size
775     ///   blob_size is the size of the BLOB to be written.
776     /// @param flags
777     ///   @see EBlobOStreamFlags.
778     /// @param log_it
779     ///    Enables transaction log for BLOB (enabled by default).
780     ///    Make sure you have enough log segment space, or disable it.
781     CNcbiOstream& GetOStream(size_t blob_size, TBlobOStreamFlags flags = 0)
782         const;
783     CNcbiOstream& GetOStream(size_t blob_size, CQuery::EAllowLog log_it) const;
784 
785     /// Empty constructor of bookmark object.
786     /// Object created this way cannot be used for anything except assigning
787     /// from the other bookmark object.
788     CBlobBookmark(void);
789     ~CBlobBookmark(void);
790 
791     /// Copy of bookmark object
792     CBlobBookmark(const CBlobBookmark& bm);
793     CBlobBookmark& operator= (const CBlobBookmark& bm);
794 
795 private:
796     friend class CDatabase;
797     friend class CRemoteQFB;
798 
799     /// Create bookmark with the given implementation
800     CBlobBookmark(CBlobBookmarkImpl* bm_impl);
801 
802 
803     /// Bookmark implementation object
804     CRef<CBlobBookmarkImpl> m_Impl;
805 };
806 
807 
808 /// Object used to perform bulk-inserting operations to database.
809 class CBulkInsert
810 {
811 public:
812     /// Empty constructor of bulk-insert object.
813     /// Object created this way cannot be used for anything except assigning
814     /// from other bulk-insert object.
815     CBulkInsert(void);
816     ~CBulkInsert(void);
817 
818     /// Copying of bulk-insert object.
819     /// Any copy will work with the same bulk-insert session on the server.
820     /// So that Complete() call on any of the copies will end up this session
821     /// and no insertion will be possible to make from other copies.
822     CBulkInsert(const CBulkInsert& bi);
823     CBulkInsert& operator= (const CBulkInsert& bi);
824 
825     /// Type of hint that can be set for bulk insert
826     enum EHints {
827         eTabLock,
828         eCheckConstraints,
829         eFireTriggers
830     };
831     /// Add hint to the bulk insert.
832     /// Resets everything that was set by SetHints().
833     void AddHint(EHints hint);
834 
835     /// Type of hint that requires some value to be provided with it
836     enum EHintsWithValue {
837         eRowsPerBatch,
838         eKilobytesPerBatch
839     };
840     /// Add hint with value to the bulk insert.
841     /// Value should not be equal to zero.
842     /// Resets everything that was set by SetHints().
843     void AddHint(EHintsWithValue hint, unsigned int value);
844     /// Add "ORDER" hint.
845     /// Resets everything that was set by SetHints().
846     void AddOrderHint(CTempString columns);
847     /// Set hints by one call. Resets everything that was set by Add*Hint().
848     void SetHints(CTempString hints);
849 
850     /// Bind column for bulk insert.
851     /// All columns are numbered starting with 1. Columns should be bound in
852     /// strict order (1st column first, then 2nd, then 3rd etc) and no columns
853     /// can be bound several times. Type given is data type which all values
854     /// will be converted to and how data will be sent to the server.
855     void Bind(int col, ESDB_Type type);
856 
857     /// Put values of different type into bulk-insert row.
858     /// Automatic conversion is made to the bound type if possible. If
859     /// conversion is not possible then exception is thrown. Number of values
860     /// put into the row should be exactly the same as number of columns
861     /// bound with calls to Bind().
862     CBulkInsert& operator<<(const string& val);
863     CBulkInsert& operator<<(const char* val);
864     CBulkInsert& operator<<(unsigned char val);
865     CBulkInsert& operator<<(short val);
866     CBulkInsert& operator<<(Int4 val);
867     CBulkInsert& operator<<(Int8 val);
868     CBulkInsert& operator<<(float val);
869     CBulkInsert& operator<<(double val);
870     CBulkInsert& operator<<(bool val);
871     CBulkInsert& operator<<(const CTime& val);
872     /// Special case to support inserting into NVARCHAR and NTEXT fields
873     CBulkInsert& operator<<(const TStringUCS2& val);
874     /// Special case of putting NullValue or EndRow into the row.
875     CBulkInsert& operator<<(CBulkInsert& (*f)(CBulkInsert&));
876 
877     /// Complete bulk insert.
878     /// Nothing can be inserted after call to this method.
879     void Complete();
880 
881 private:
882     friend class CDatabase;
883     friend CBulkInsert& NullValue(CBulkInsert& bi);
884     friend CBulkInsert& EndRow(CBulkInsert& bi);
885 
886     /// Create bulk-insert object for given database, table name and number of
887     /// rows to write before auto-flush occurs.
888     CBulkInsert(CDatabaseImpl* db_impl,
889                 const string&  table_name,
890                 int            autoflush);
891 
892 
893     /// Bulk-insert implementation object
894     CRef<CBulkInsertImpl> m_Impl;
895 };
896 
897 /// Manipulator putting null value into the bulk-insert row.
898 CBulkInsert& NullValue(CBulkInsert& bi);
899 /// Manipulator ending row in the bulk-insert object
900 CBulkInsert& EndRow(CBulkInsert& bi);
901 
902 
903 class CDBConnParamsBase;
904 
905 
906 /// Database password decryptor.
907 ///
908 /// The general structure is public, but the full default
909 /// implementation is only available within NCBI.
910 class CSDB_Decryptor : public CObject
911 {
912 public:
913     string Decrypt(const string& ciphertext, const CTempString& key_id);
914 
915 protected:
916     virtual string x_Decrypt(const string& ciphertext, const string& key)
917 #ifndef HAVE_LIBCONNEXT
918         = 0
919 #endif
920         ;
921 
922     virtual string x_GetKey(const CTempString& key_id);
923 };
924 
925 
926 /// Convenience class to initialize database connection parameters from
927 /// URL-like strings and/or application configuration and/or hard-code.
928 class CSDB_ConnectionParam
929 {
930 public:
931     /// Get database connection parameters from a string and from
932     /// the application configuration.
933     ///
934     /// @param url_string
935     ///   Get connection parameters from this URL-like string, formatted as:
936     ///   dbapi://[username[:password]@][server[:port]][/database][?k1=v1;...]
937     ///   or
938     ///   dbapi://[username[:password]@][service][/database][?k1=v1;...]
939     ///   or
940     ///   service
941     ///
942     ///   Each token must be URL-encoded.
943     ///
944     ///   Also application configuration is looked for section with name
945     ///   "service.dbservice" ("service" can be passed to the ctor or to Set()
946     ///   method). If such section exists then the following parameters are
947     ///   read from it, and if present will override corresponding parameters
948     ///   extracted from the URL or specified later via Set.
949     ///   - username
950     ///   - password
951     ///   - service
952     ///   - port
953     ///   - database
954     ///   - args
955     ///   - login_timeout
956     ///   - io_timeout
957     ///   - exclusive_server
958     ///   - use_conn_pool
959     ///   - conn_pool_name
960     ///   - conn_pool_minsize
961     ///   - conn_pool_maxsize
962     ///   - conn_pool_idle_time
963     ///   - conn_pool_wait_time
964     ///   - conn_pool_allow_temp_overflow
965     ///   - continue_after_raiserror
966     ///   - conn_pool_max_conn_use
967     ///   - password_file
968     ///   - password_key
969     ///
970     ///   Most of these parameters can also come as named URL parameters;
971     ///   "args" is a catch-all for any other parameters, which can appear
972     ///   directly as URL parameters.  (Settings from the configuration file's
973     ///   "args" string override URL parameters on an individual basis.)
974     CSDB_ConnectionParam(const string& url_string = kEmptyStr);
975 
976     /// Flags affecting URL composition.
977     ///
978     /// @sa ComposeUrl
979     enum EComposeUrlFlags {
980         /// Throw an exception if missing any "essential" parameters.
981         /// (It's normally necessary to have the server name, the database
982         /// name, the username, and either a password or a password file.)
983         fThrowIfIncomplete = 0x1,
984         fHidePassword      = 0x2, /// Obscure passwords
985         // For compatibility with older code
986         eThrowIfIncomplete = fThrowIfIncomplete,
987         eAllowIncomplete   = 0
988     };
989     typedef int TComposeUrlFlags; // Bitwise OR of EComposeUrlFlags
990 
991     /// Compose database connection URL string out of this class.
992     ///
993     /// @param flags
994     ///   Any desired flags from EComposeUrlFlags.
995     string ComposeUrl(TComposeUrlFlags flags = 0) const;
996 
997     bool IsEmpty(void) const;
998 
999     /// "Essential" (e.g. those located before '?' in the URL) database
1000     /// connection parameters
1001     enum EParam {
1002         eUsername,
1003         ePassword,
1004         ePasswordFile,
1005         ePasswordKeyID,
1006         /// Named service, interfaces-file alias, or raw server name, per
1007         /// http://ncbi.github.io/cxx-toolkit/pages/ch_dbapi#ch_dbapi.Getting_started
1008         /// @note In the interfaces file, if there are multiple choices for
1009         /// a given alias, FreeTDS (used by SDBAPI) will always attempt to
1010         /// connect to the last one, even if it's out of commission.
1011         eService,
1012         ePort, ///< DB server's port (when not using an alias or named service)
1013         eDatabase,
1014         eLoginTimeout,
1015         eIOTimeout,
1016         eExclusiveServer,
1017         eUseConnPool,
1018         eConnPoolName,
1019         eConnPoolMinSize,
1020         eConnPoolMaxSize,
1021         eConnPoolIdleTime,
1022         eConnPoolWaitTime,
1023         eConnPoolAllowTempOverflow,
1024         eContinueAfterRaiserror,
1025         eConnPoolMaxConnUse,
1026         eArgsString
1027     };
1028 
1029     /// Whether to report values from configuration files, or just
1030     /// those set in code (which have a lower priority).
1031     enum EWithOverrides {
1032         eWithoutOverrides,
1033         eWithOverrides
1034     };
1035 
1036     /// Get one of the "essential" database connection parameters.
1037     /// Empty string means that it is not set.
1038     string Get(EParam param,
1039                EWithOverrides with_overrides = eWithoutOverrides) const;
1040 
1041     /// Flags affecting parameter setting.
1042     ///
1043     /// @sa Set
1044     enum ESetFlags {
1045         /// The specified value is merely a default, which Set should ignore
1046         /// if a non-empty setting for that parameter is already present.
1047         fAsDefault = 0x1
1048     };
1049     typedef int TSetFlags; // Bitwise OR of ESetFlags
1050 
1051     /// Set one of the "essential" database connection parameters,
1052     /// unless overridden in a configuration file.
1053     ///
1054     /// Settings from [*.dbservice] sections always take precedence
1055     /// because they're more visible and easier to adjust.
1056     ///
1057     /// @param param
1058     ///   Parameter to set
1059     /// @param value
1060     ///   The value to set the parameter to. Empty string un-sets it.
1061     /// @param flags
1062     ///   Any desired flags from ESetFlags.
1063     CSDB_ConnectionParam& Set(EParam param, const string& value,
1064                               TSetFlags flags = 0);
1065     /// Merge existing settings with those from url_string.  The flags
1066     /// indicate how to resolve conflicts.
1067     CSDB_ConnectionParam& Set(const string& url_string, TSetFlags flags = 0);
1068     /// Merge existing settings with those from param.  The flags
1069     /// indicate how to resolve conflicts.
1070     CSDB_ConnectionParam& Set(const CSDB_ConnectionParam& param,
1071                               TSetFlags flags = 0);
1072 
1073     /// Access to additional (e.g. those located after '?' in the URL)
1074     /// database connection parameters
1075     const CUrlArgs& GetArgs(void) const;
1076     /// Access to additional (e.g. those located after '?' in the URL)
1077     /// database connection parameters
1078     CUrlArgs& GetArgs(void);
1079 
1080     /// Use the specified password decryptor.
1081     ///
1082     /// @sa GetGlobalDecryptor
1083     static void SetGlobalDecryptor(CRef<CSDB_Decryptor> decryptor);
1084     /// Get the current password decryptor, if any.
1085     ///
1086     /// @sa SetGlobalDecryptor
1087     static CRef<CSDB_Decryptor> GetGlobalDecryptor(void);
1088 
1089     /// Copy ctor (explicit, to avoid accidental copying)
1090     explicit CSDB_ConnectionParam(const CSDB_ConnectionParam& param);
1091     /// Assignment
1092     CSDB_ConnectionParam& operator= (const CSDB_ConnectionParam& param);
1093 
1094 private:
1095     friend class CSDBAPI;
1096     friend class CDatabaseImpl;
1097 
1098     /// Populate m_ParamMap according to the current server or service name.
1099     void x_FillParamMap(void);
1100 
1101     /// Fill parameters for low-level DBAPI from what is set here and in the
1102     /// configuration file.
1103     void x_FillLowerParams(CDBConnParamsBase* params) const;
1104 
1105     void x_FillBoolParam(CDBConnParamsBase* params, const string& name,
1106                          EParam id) const;
1107 
1108     /// Determine what password to use, accounting for possible
1109     /// encryption or indirection.
1110     string x_GetPassword() const;
1111 
1112     static const char* x_GetName(EParam param);
1113 
1114     static bool x_IsKnownArg(const string& name);
1115 
1116     void x_ReportOverride(const CTempString& name, CTempString code_value,
1117                           CTempString reg_value) const;
1118 
1119     /// URL storing all parameters set in code.
1120     mutable CUrl m_Url;
1121 
1122     /// Map of any parameters set in the configuration file, which override
1123     /// those set in code.
1124     typedef map<EParam, string> TParamMap;
1125     TParamMap m_ParamMap;
1126 };
1127 
1128 
1129 /// Database connection object.
1130 class CDatabase
1131 {
1132 public:
1133     /// How thoroughly IsConnected should actually check the connection.
1134     enum EConnectionCheckMethod {
1135         eNoCheck,   //< Confirm that Connect() was called and Close() wasn't.
1136         eFastCheck, //< Also verify that the driver still sees a connection.
1137         eFullCheck, //< Further confirm that a simple query succeeds.
1138     };
1139 
1140     /// Empty constructor of database object.
1141     /// Object created this way cannot be used for anything except assigning
1142     /// from another database object.
1143     CDatabase(void);
1144     /// Create database object for particular database parameters
1145     CDatabase(const CSDB_ConnectionParam& param);
1146     /// Create database object and take database parameters from given URL and
1147     /// application configuration. See CSDB_ConnectionParam ctor
1148     /// for URL format and rules of overriding.
1149     CDatabase(const string& url_string);
1150     ~CDatabase(void);
1151 
1152     /// Copying of database object.
1153     /// Objects copied this way will work over the same physical connection to
1154     /// the server unless they're reconnected again.
1155     CDatabase(const CDatabase& db);
1156     CDatabase& operator= (const CDatabase& db);
1157 
1158     /// Get connection parameters
1159     CSDB_ConnectionParam& GetConnectionParam(void);
1160     const CSDB_ConnectionParam& GetConnectionParam(void) const;
1161 
1162     /// Explicitly (re)connect to the database server.
1163     /// NB: NewQuery et al. automatically establish initial connections,
1164     /// but do not automatically reconnect, because the original connection
1165     /// may have had important state.
1166     void Connect(void);
1167     /// Close database object.
1168     /// You cannot do anything with CQuery and CBulkInsert objects created
1169     /// from this database object after call to this method. Although you can
1170     /// reconnect to the database server CQuery and CBulkInsert objects will
1171     /// not work anyway.
1172     void Close(void);
1173     /// Clone database object.
1174     /// While cloning new physical connection to database server is created,
1175     /// so that resulting database object can be used completely independently
1176     /// from original one.
1177     CDatabase Clone(void);
1178     /// Check if database object was already connected to database server.
1179     /// By default, this checks only that Connect() method was called
1180     /// and Close() wasn't called, but more thorough checks are possible.
1181     /// NB: eFullCheck mode cannot coexist with active queries.
1182     bool IsConnected(EConnectionCheckMethod check_method = eNoCheck);
1183 
1184     /// Get new CQuery object for this database.
1185     /// Automatically establish an initial connection if necessary,
1186     /// but do not automatically reconnect.
1187     CQuery NewQuery(void);
1188     /// Get new CQuery object with particular sql statement for this database.
1189     /// Automatically establish an initial connection if necessary,
1190     /// but do not automatically reconnect.
1191     CQuery NewQuery(const string& sql);
1192     /// Get new CBulkInsert object.
1193     /// Automatically establish an initial connection if necessary,
1194     /// but do not automatically reconnect.
1195     ///
1196     /// @param table_name
1197     ///   Name of the table to insert to
1198     /// @param autoflush
1199     ///   Number of rows to insert before the batch is committed to database
1200     CBulkInsert NewBulkInsert(const string& table_name, int autoflush);
1201     /// Get new CBlobBookmark object.
1202     /// Automatically establish an initial connection if necessary,
1203     /// but do not automatically reconnect.
1204     ///
1205     /// @param table_name
1206     ///   Name of the table to update.
1207     /// @param column_name
1208     ///   Name of the column to update.
1209     /// @param search_conditions
1210     ///   SQL expression identifying the relevant row.  (Failure to
1211     ///   match exactly one row yields undefined behavior, and may
1212     ///   result in updating multiple rows in some cases.)
1213     /// @param column_type
1214     ///   General column type (text or binary), if known.  (Optional.)
1215     /// @param has_legacy_type
1216     ///   Whether the column has a legacy type (IMAGE or [N]TEXT) or
1217     ///   a modern type like [N]VARCHAR(MAX).  (Optional.)
1218     /// @sa CQuery::CField::GetBookmark
1219     CBlobBookmark NewBookmark(const string&            table_name,
1220                               const string&            column_name,
1221                               const string&            search_conditions,
1222                               CBlobBookmark::EBlobType column_type
1223                               = CBlobBookmark::eUnknown,
1224                               ETriState has_legacy_type = eTriState_Unknown);
1225 
1226     /// Get new CBlobStoreStatic object (to be owned by caller).
1227     /// Automatically establish an initial connection if necessary,
1228     /// but do not automatically reconnect.
1229     ///
1230     /// @param table_name
1231     ///   Name of the table holding the blobs (structure to be deduced
1232     ///   by inspection).
1233     /// @param flags
1234     ///   Flags governing compression and transaction logging.
1235     /// @param image_limit
1236     ///   Maximum size of a single blob (to be split across columns as
1237     ///   needed).
1238     CBlobStoreStatic* NewBlobStore(const string&      table_name,
1239                                    TNewBlobStoreFlags flags = 0,
1240                                    size_t             image_limit = 1 << 24);
1241 
1242     /// Get new CBlobStoreStatic object (to be owned by caller).
1243     /// Automatically establish an initial connection if necessary,
1244     /// but do not automatically reconnect.
1245     ///
1246     /// @param table_name
1247     ///   Name of the table holding the blobs (structure given explicitly
1248     ///   via key_col_name, num_col_name, and blob_col_names).
1249     /// @param key_col_name
1250     ///   Name of the column holding keys with which blobs are associated.
1251     /// @param num_col_name
1252     ///   Name of the column holding the number of blob columns used.
1253     /// @param blob_col_names
1254     ///   Names of the actual blob columns.
1255     /// @param flags
1256     ///   Flags governing compression, transaction logging, and blob
1257     ///   column textuality.
1258     /// @param image_limit
1259     ///   Maximum size of a single blob (to be split across columns as
1260     ///   needed).
1261     CBlobStoreStatic* NewBlobStore(const string&        table_name,
1262                                    const string&        key_col_name,
1263                                    const string&        num_col_name,
1264                                    const vector<string> blob_col_names,
1265                                    TNewBlobStoreFlags   flags = 0,
1266                                    size_t               image_limit = 1 << 24);
1267 
1268 private:
1269     friend CAutoTrans::CSubject DBAPI_MakeTrans(CDatabase& db);
1270 
1271     void x_ConnectAsNeeded(const char* operation);
1272 
1273     /// Database parameters
1274     CSDB_ConnectionParam m_Params;
1275     /// Database implementation object
1276     CRef<CDatabaseImpl>  m_Impl;
1277 };
1278 
1279 
1280 class CSDBAPI
1281 {
1282 public:
1283     /// Initialize SDBAPI.
1284     /// Creates minimum number of connections required for each pool configured
1285     /// in application's configuration file. If openning of some of those
1286     /// connections failed then method will return FALSE, otherwise TRUE.
1287     static bool Init(void);
1288 
1289     /// Report the specified application name to servers.
1290     ///
1291     /// By default, this will be the same name reported to AppLog
1292     /// (typically autodetected).  Any changes will take effect for
1293     /// subsequent connections, but won't affect preexisting ones.
1294     ///
1295     /// @sa GetApplicationName()
1296     static void SetApplicationName(const CTempString& name);
1297 
1298     /// Check SDBAPI's application name setting.
1299     ///
1300     /// @sa SetApplicationName()
1301     static string GetApplicationName(void);
1302 
1303     /// @sa UseDriver
1304     enum EDriver {
1305         eDriver_FTDS95,
1306         eDriver_FTDS100
1307     };
1308     /// Use the specified driver for all connections.
1309     ///
1310     /// Any call to this method must be the application's very first
1311     /// use of SDBAPI; calling it later is an error, and will result
1312     /// in throwing an exception.
1313     static void UseDriver(EDriver driver);
1314 
1315     /// @sa UpdateMirror
1316     enum EMirrorStatus {
1317         eMirror_Steady,      ///< Mirror is working on the same server as before
1318         eMirror_NewMaster,   ///< Switched to a new master
1319         eMirror_Unavailable  ///< All databases in the mirror are unavailable
1320     };
1321 
1322     /// Check for master/mirror switch. If switch is detected or if all databases
1323     /// in the mirror become unavailable, then all connections
1324     /// to the "old" master server will be immediately invalidated, so that any
1325     /// subsequent database operation on them (via objects CQuery and
1326     /// CBulkInsert) would cause an error. The affected CDatabase objects will
1327     /// be automatically invalidated too. User code will have to explicitly re-connect
1328     /// (which will open connection to the new master, if any).
1329     /// @note
1330     ///   If the database resource is in any way misconfigured, then an exception
1331     ///   will be thrown.
1332     /// @param dbservice
1333     ///   Database resource name
1334     /// @param servers
1335     ///   List of database servers, with the master one first.
1336     /// @param error_message
1337     ///   Detailed error message (if any).
1338     /// @return
1339     ///   Result code
1340     static EMirrorStatus UpdateMirror(const string& dbservice,
1341                                       list<string>* servers = NULL,
1342                                       string*       error_message = NULL);
1343 
1344     /// Get new CBlobStoreDynamic object (to be owned by caller).
1345     ///
1346     /// @param param
1347     ///   Connection parameters; only Service, Username, and Password apply
1348     ///   here, though.
1349     /// @param table_name
1350     ///   Name of the table holding the blobs (structure to be deduced
1351     ///   by inspection).
1352     /// @param flags
1353     ///   Flags governing compression and transaction logging.
1354     /// @param image_limit
1355     ///   Maximum size of a single blob (to be split across columns as
1356     ///   needed).
1357     static CBlobStoreDynamic* NewBlobStore(const CSDB_ConnectionParam& param,
1358                                            const string& table_name,
1359                                            TNewBlobStoreFlags flags = 0,
1360                                            size_t image_limit = 1 << 24);
1361 
1362 private:
1363     CSDBAPI(void);
1364 };
1365 
1366 
1367 
1368 //////////////////////////////////////////////////////////////////////////
1369 // Inline methods
1370 //////////////////////////////////////////////////////////////////////////
1371 
1372 inline
CSDB_Exception(const CDiagCompileInfo & info,const CException * prev_exception,const CDB_Exception::SMessageInContext & message,EDiagSev severity,CException::TFlags flags)1373 CSDB_Exception::CSDB_Exception(const CDiagCompileInfo& info,
1374                                const CException* prev_exception,
1375                                const CDB_Exception::SMessageInContext& message,
1376                                EDiagSev severity, CException::TFlags flags)
1377     : CException(info, prev_exception, message.message, severity, flags),
1378       m_Context(message.context)
1379 {
1380     x_Init(info, message, prev_exception, severity);
1381 }
1382 
1383 inline
GetDBException(void) const1384 const CDB_Exception* CSDB_Exception::GetDBException(void) const
1385 {
1386     return dynamic_cast<const CDB_Exception*>(GetPredecessor());
1387 }
1388 
1389 inline
GetDBErrCode(void) const1390 CException::TErrCode CSDB_Exception::GetDBErrCode(void) const
1391 {
1392     const CDB_Exception* dbex = GetDBException();
1393     return dbex ? static_cast<CException::TErrCode>(dbex->Type()) : eInvalid;
1394 }
1395 
1396 inline
GetDBErrCodeString(void) const1397 const char* CSDB_Exception::GetDBErrCodeString(void) const
1398 {
1399     const CDB_Exception* dbex = GetDBException();
1400     return dbex ? dbex->GetErrCodeString() : "eInvalid";
1401 }
1402 
1403 inline
GetServerName(void) const1404 const string& CSDB_Exception::GetServerName(void) const
1405 {
1406     return m_Context->server_name;
1407 }
1408 
1409 inline
GetUserName(void) const1410 const string& CSDB_Exception::GetUserName(void) const
1411 {
1412     return m_Context->username;
1413 }
1414 
1415 inline
GetDatabaseName(void) const1416 const string& CSDB_Exception::GetDatabaseName(void) const
1417 {
1418     return m_Context->database_name;
1419 }
1420 
1421 inline
GetExtraMsg(void) const1422 const string& CSDB_Exception::GetExtraMsg(void) const
1423 {
1424     return m_Context->extra_msg;
1425 }
1426 
1427 
1428 #if !NCBI_INT8_IS_LONG
1429 inline
SetParameter(CTempString name,long value,ESDB_Type type,ESP_ParamType param_type)1430 CQuery& CQuery::SetParameter(CTempString name, long value, ESDB_Type type,
1431                           ESP_ParamType param_type)
1432 {
1433     return SetParameter(name, static_cast<int>(value), type, param_type);
1434 }
1435 #endif
1436 
1437 inline
SetOutputParameter(CTempString name,ESDB_Type type)1438 CQuery& CQuery::SetOutputParameter(CTempString name, ESDB_Type type)
1439 {
1440     SetNullParameter(name, type, eSP_InOut);
1441     return *this;
1442 }
1443 
1444 
1445 inline
CSDB_ConnectionParam(const string & url_string)1446 CSDB_ConnectionParam::CSDB_ConnectionParam(const string& url_string /* = kEmptyStr */)
1447 {
1448     if (url_string.empty()) {
1449         m_Url.SetScheme("dbapi");
1450         m_Url.SetIsGeneric(true);
1451         m_Url.GetArgs();
1452         return;
1453     }
1454 
1455     if (NStr::StartsWith(url_string, "dbapi://"))
1456         m_Url.SetUrl(url_string);
1457     else
1458         m_Url.SetUrl("dbapi://" + url_string);
1459     // Force arguments to exist
1460     m_Url.GetArgs();
1461     x_FillParamMap();
1462 }
1463 
1464 inline
CSDB_ConnectionParam(const CSDB_ConnectionParam & param)1465 CSDB_ConnectionParam::CSDB_ConnectionParam(const CSDB_ConnectionParam& param)
1466     : m_Url(param.m_Url), m_ParamMap(param.m_ParamMap)
1467 {}
1468 
1469 inline CSDB_ConnectionParam&
operator =(const CSDB_ConnectionParam & param)1470 CSDB_ConnectionParam::operator= (const CSDB_ConnectionParam& param)
1471 {
1472     m_Url = param.m_Url;
1473     m_ParamMap = param.m_ParamMap;
1474     return *this;
1475 }
1476 
1477 inline CUrlArgs&
GetArgs(void)1478 CSDB_ConnectionParam::GetArgs(void)
1479 {
1480     return m_Url.GetArgs();
1481 }
1482 
1483 inline const CUrlArgs&
GetArgs(void) const1484 CSDB_ConnectionParam::GetArgs(void) const
1485 {
1486     return m_Url.GetArgs();
1487 }
1488 
1489 inline const char*
x_GetName(EParam param)1490 CSDB_ConnectionParam::x_GetName(EParam param)
1491 {
1492     switch (param) {
1493     case eUsername:         return "username";
1494     case ePassword:         return "password";
1495     case ePasswordFile:     return "password_file";
1496     case ePasswordKeyID:    return "password_key";
1497     case eService:          return "service";
1498     case ePort:             return "port";
1499     case eDatabase:         return "database";
1500     case eLoginTimeout:     return "login_timeout";
1501     case eIOTimeout:        return "io_timeout";
1502     case eExclusiveServer:  return "exclusive_server";
1503     case eUseConnPool:      return "use_conn_pool";
1504     case eConnPoolName:     return "conn_pool_name";
1505     case eConnPoolMinSize:  return "conn_pool_minsize";
1506     case eConnPoolMaxSize:  return "conn_pool_maxsize";
1507     case eConnPoolIdleTime: return "conn_pool_idle_time";
1508     case eConnPoolWaitTime: return "conn_pool_wait_time";
1509     case eConnPoolAllowTempOverflow:
1510                             return "allow_temp_overflow";
1511     case eContinueAfterRaiserror:
1512                             return "continue_after_raiserror";
1513     case eConnPoolMaxConnUse:
1514                             return "conn_pool_max_conn_use";
1515     case eArgsString:       return "args_string";
1516     }
1517     _TROUBLE;
1518     return kEmptyCStr;
1519 }
1520 
1521 inline bool
x_IsKnownArg(const string & name)1522 CSDB_ConnectionParam::x_IsKnownArg(const string& name)
1523 {
1524     for (int p = eLoginTimeout;  p < eArgsString;  ++p) {
1525         if (name == x_GetName(static_cast<EParam>(p))) {
1526             return true;
1527         }
1528     }
1529     return false;
1530 }
1531 
1532 inline string
Get(EParam param,EWithOverrides with_overrides) const1533 CSDB_ConnectionParam::Get(EParam param, EWithOverrides with_overrides) const
1534 {
1535     if (with_overrides == eWithOverrides) {
1536         TParamMap::const_iterator it = m_ParamMap.find(param);
1537         if (it != m_ParamMap.end()) {
1538             return it->second;
1539         }
1540     }
1541 
1542     switch (param) {
1543     case eUsername:
1544         return m_Url.GetUser();
1545     case ePassword:
1546         return m_Url.GetPassword();
1547     case eService:
1548         return m_Url.GetHost();
1549     case ePort:
1550         return m_Url.GetPort();
1551     case eDatabase:
1552         return m_Url.GetPath().empty()? kEmptyStr: m_Url.GetPath().substr(1);
1553     case ePasswordFile:
1554     {
1555         bool   found  = false;
1556         string pwfile = m_Url.GetArgs().GetValue("password_file", &found);
1557         if (found  &&  !pwfile.empty()  &&  !m_Url.GetPassword().empty()) {
1558             NCBI_THROW(CSDB_Exception, eURLFormat | Retriable(eRetriable_No),
1559                        "Password and password_file URL parameters"
1560                        " are mutually exclusive.");
1561             return kEmptyStr;
1562         }
1563         return pwfile;
1564     }
1565     case ePasswordKeyID:
1566     case eLoginTimeout:
1567     case eIOTimeout:
1568     case eExclusiveServer:
1569     case eUseConnPool:
1570     case eConnPoolName:
1571     case eConnPoolMinSize:
1572     case eConnPoolMaxSize:
1573     case eConnPoolIdleTime:
1574     case eConnPoolWaitTime:
1575     case eConnPoolAllowTempOverflow:
1576     case eContinueAfterRaiserror:
1577     case eConnPoolMaxConnUse:
1578     {
1579         bool found_dummy = false;
1580         return m_Url.GetArgs().GetValue(x_GetName(param), &found_dummy);
1581     }
1582     case eArgsString:
1583         return m_Url.GetOriginalArgsString();
1584     }
1585     _ASSERT(false);
1586     return string();
1587 }
1588 
1589 inline CSDB_ConnectionParam&
Set(EParam param,const string & value,TSetFlags flags)1590 CSDB_ConnectionParam::Set(EParam param, const string& value, TSetFlags flags)
1591 {
1592     TParamMap::const_iterator it = m_ParamMap.find(param);
1593     if (it != m_ParamMap.end()) {
1594         x_ReportOverride(x_GetName(it->first), value, it->second);
1595     }
1596 
1597     if ((flags & fAsDefault) != 0
1598         &&  ( !Get(param).empty()
1599               ||  (param == ePasswordFile  &&  !Get(ePassword).empty())
1600               ||  (param == ePassword      &&  !Get(ePasswordFile).empty()) ))
1601     {
1602         return *this;
1603     }
1604 
1605     switch (param) {
1606     case eUsername:
1607         m_Url.SetUser(value);
1608         return *this;
1609     case ePassword:
1610         if ( !value.empty()  &&  !Get(ePasswordFile).empty() ) {
1611             // XXX - issue diagnostics?
1612             Set(ePasswordFile, kEmptyStr);
1613         }
1614         m_Url.SetPassword(value);
1615         return *this;
1616     case eService:
1617         m_Url.SetHost(value);
1618         x_FillParamMap();
1619         return *this;
1620     case ePort:
1621         m_Url.SetPort(value);
1622         return *this;
1623     case eDatabase:
1624         m_Url.SetPath("/" + value);
1625         return *this;
1626     case ePasswordFile:
1627         if ( !value.empty()  &&  !Get(ePassword).empty() ) {
1628             // XXX - issue diagnostics?
1629             Set(ePassword, kEmptyStr);
1630         }
1631         if ( !value.empty()  ||  m_Url.GetArgs().IsSetValue("password_file")) {
1632             m_Url.GetArgs().SetValue("password_file", value);
1633         }
1634         return *this;
1635     case ePasswordKeyID:
1636     case eLoginTimeout:
1637     case eIOTimeout:
1638     case eExclusiveServer:
1639     case eUseConnPool:
1640     case eConnPoolName:
1641     case eConnPoolMinSize:
1642     case eConnPoolMaxSize:
1643     case eConnPoolIdleTime:
1644     case eConnPoolWaitTime:
1645     case eConnPoolAllowTempOverflow:
1646     case eContinueAfterRaiserror:
1647     case eConnPoolMaxConnUse:
1648     {
1649         string name = x_GetName(param);
1650         if ( !value.empty()  ||  m_Url.GetArgs().IsSetValue(name)) {
1651             m_Url.GetArgs().SetValue(name, value);
1652         }
1653         return *this;
1654     }
1655     case eArgsString:
1656         m_Url.GetArgs().GetArgs().clear();
1657         m_Url.GetArgs().SetQueryString(value);
1658         return *this;
1659     }
1660     _ASSERT(false);
1661     return *this;
1662 }
1663 
1664 
1665 inline CSDB_ConnectionParam&
Set(const string & url_string,TSetFlags flags)1666 CSDB_ConnectionParam::Set(const string& url_string, TSetFlags flags)
1667 {
1668     CSDB_ConnectionParam param(url_string);
1669     return Set(param, flags);
1670 }
1671 
1672 
1673 inline CSDB_ConnectionParam&
GetConnectionParam(void)1674 CDatabase::GetConnectionParam(void)
1675 {
1676     return m_Params;
1677 }
1678 
1679 inline const CSDB_ConnectionParam&
GetConnectionParam(void) const1680 CDatabase::GetConnectionParam(void) const
1681 {
1682     return m_Params;
1683 }
1684 
1685 inline CQuery
NewQuery(const string & sql)1686 CDatabase::NewQuery(const string& sql)
1687 {
1688     CQuery q = NewQuery();
1689     q.SetSql(sql);
1690     return q;
1691 }
1692 
1693 
1694 inline
Decrypt(const string & ciphertext,const CTempString & key)1695 string CSDB_Decryptor::Decrypt(const string& ciphertext,
1696                                const CTempString& key)
1697 {
1698     return x_Decrypt(ciphertext, x_GetKey(key));
1699 }
1700 
1701 
1702 inline bool
operator !=(const CRowIterator & ri) const1703 CQuery::CRowIterator::operator!= (const CRowIterator& ri) const
1704 {
1705     return !operator== (ri);
1706 }
1707 
1708 inline CQuery::CRowIterator
operator ++(int)1709 CQuery::CRowIterator::operator++ (int)
1710 {
1711     return operator++();
1712 }
1713 
1714 END_NCBI_SCOPE
1715 
1716 #endif  // CPPCORE__DBAPI__SIMPLE__SDBAPI__HPP
1717