1 #ifndef DB_SQLITE_SQLITEWRAPP__HPP
2 #define DB_SQLITE_SQLITEWRAPP__HPP
3 /* $Id: sqlitewrapp.hpp 576166 2018-12-11 15:51:54Z grichenk $
4 * ===========================================================================
5 *
6 * PUBLIC DOMAIN NOTICE
7 * National Center for Biotechnology Information
8 *
9 * This software/database is a "United States Government Work" under the
10 * terms of the United States Copyright Act. It was written as part of
11 * the author's official duties as a United States Government employee and
12 * thus cannot be copyrighted. This software/database is freely available
13 * to the public for use. The National Library of Medicine and the U.S.
14 * Government have not placed any restriction on its use or reproduction.
15 *
16 * Although all reasonable efforts have been taken to ensure the accuracy
17 * and reliability of the software and data, the NLM and the U.S.
18 * Government do not and cannot warrant the performance or results that
19 * may be obtained by using this software or data. The NLM and the U.S.
20 * Government disclaim all warranties, express or implied, including
21 * warranties of performance, merchantability or fitness for any particular
22 * purpose.
23 *
24 * Please cite the author in any work or product based on this material.
25 *
26 * ===========================================================================
27 *
28 * Authors: Pavel Ivanov
29 *
30 * File Description:
31 * Convenient wrappers for SQLite-related objects and most commonly used
32 * functions. Wrappers require SQLite 3.6.14 or higher with async vfs
33 * extension. All wrappers are written in assumption that no one else calls
34 * any sqlite3* functions except the wrapper itself.
35 */
36
37 #include <corelib/ncbiexpt.hpp>
38 #include <corelib/ncbimisc.hpp>
39 #include <corelib/ncbistr.hpp>
40 #include <corelib/ncbimtx.hpp>
41 #include <corelib/ncbi_system.hpp>
42 #include <corelib/obj_pool.hpp>
43
44 #include <sqlite3.h>
45 #ifdef HAVE_SQLITE3ASYNC_H
46 # include <sqlite3async.h>
47 #endif
48
49 #include <deque>
50
51
52 BEGIN_NCBI_SCOPE
53
54
55 class CSQLITE_Exception;
56 class CSQLITE_Connection;
57 class CSQLITE_Statement;
58 class CSQLITE_Blob;
59
60
61 /// Utility class for some global-purpose functions tuning SQLite as a whole
62 /// as opposed to tuning connection-by-connection.
63 class CSQLITE_Global
64 {
65 public:
66 /// Install non-default cache for all SQLite databases. Can be useful
67 /// if default policy of working with cache is inappropriate for your
68 /// application. For more details on sqlite3_pcache_methods look into
69 /// comments before it in sqlite3.h.
70 /// Method can be called only before SQLite is initialized with
71 /// Initialize() method.
72 static void SetCustomPageCache(sqlite3_pcache_methods* methods);
73
74 /// Initialization of SQLite and tuning some default parameters.
75 /// For these SQLite wrappers to work properly this method should be
76 /// called before any work with other classes.
77 static void Initialize(void);
78 /// Finish all SQLite operations. Method should be called at the end of
79 /// application execution. After it has been called any further attempts
80 /// to use wrapper classes will likely cause application crash.
81 static void Finalize(void);
82
83 /// Enable use of the same cache by different connections to the same
84 /// database file. Setting can be changed at any time and will affect all
85 /// connections opened after changing.
86 static void EnableSharedCache(bool enable = true);
87 /// Get default virtual file system installed in SQLite.
88 /// Method can be called only after SQLite is initialized with
89 /// Initialize() method.
90 static sqlite3_vfs* GetDefaultVFS(void);
91 /// Register new virtual file system in SQLite.
92 /// If set_default is TRUE then all connections that will be opened after
93 /// call to this method will use this virtual file system. When
94 /// set_default is FALSE then connections will have to state explicitly
95 /// if they want to use this new vfs (although for now this functionality
96 /// is not yet implemented in CSQLITE_Connection).
97 /// Method can be called only after SQLite is initialized with
98 /// Initialize() method.
99 static void RegisterCustomVFS(sqlite3_vfs* vfs, bool set_default = true);
100
101 #ifdef HAVE_SQLITE3ASYNC_H
102 /// Setup the option for asynchronous file system to do the actual locking
103 /// of database files on disk. If lock_files is TRUE then files will be
104 /// actually locked on hard disk, if it is FALSE files will not be locked
105 /// and so consistency of access to databases opened with asynchronous
106 /// writing flag set will be maintained only inside current process.
107 /// Method should be called only when there's no connections opened.
108 static void SetAsyncWritesFileLocking(bool lock_files);
109 /// Launch background thread doing all asynchronous writes to databases.
110 /// Method is no-op if background thread has already launched.
111 static void RunAsyncWritesThread(void);
112 #endif // HAVE_SQLITE3ASYNC_H
113
114 private:
115 CSQLITE_Global(void);
116 CSQLITE_Global(CSQLITE_Global&);
117 CSQLITE_Global& operator= (CSQLITE_Global&);
118 };
119
120
121 /// Object factory creating sqlite3* handles.
122 /// For internal use in CSQLITE_Connection's internal pool.
123 class CSQLITE_HandleFactory
124 {
125 public:
126 CSQLITE_HandleFactory(CSQLITE_Connection* conn);
127
128 /// Create new database handle
129 sqlite3* CreateObject(void);
130
131 /// Destroy database handle
132 void DeleteObject(sqlite3* handle);
133
134 private:
135 /// Connection object this factory is bound to
136 CSQLITE_Connection* m_Conn;
137 };
138
139
140 /// Connection to SQLite database.
141 class CSQLITE_Connection
142 {
143 public:
144 /// Flags controlling specifics of database connection operation.
145 /// Only one flag can be used in each group of flags (separated by
146 /// comments).
147 enum EOperationFlags {
148 // Mode of operation in multi-threaded environment
149 fInternalMT = 0x00, ///< Object and all statements and blobs created
150 ///< on top of it should support internal
151 ///< synchronization between different threads
152 fExternalMT = 0x01, ///< Object and all statements and blobs created
153 ///< on top of it will not be used from different
154 ///< threads simultaneously, thus performance is
155 ///< optimized, object creates only one low-level
156 ///< SQLite connection
157 /// Default value for multi-threading group of flags
158 fDefaultMT = fInternalMT,
159 eAllMT = fInternalMT + fExternalMT,
160
161 // Mode of vacuuming and shrinking file size
162 fVacuumOn = 0x00, ///< Vacuuming is on, database file has always
163 ///< the minimum size possible
164 fVacuumOff = 0x02, ///< Vacuuming is off, database file can only
165 ///< grow
166 fVacuumManual = 0x04, ///< Vacuuming is only by request
167 /// Default value for vacuuming group of flags
168 fDefaultVacuum = fVacuumOn,
169 eAllVacuum = fVacuumOn + fVacuumOff + fVacuumManual,
170
171 // Mode of journaling of all transactions
172 fJournalDelete = 0x00, ///< Journal on disk, journal file is
173 ///< deleted after each transaction
174 fJournalTruncate = 0x08, ///< Journal on disk, size of journal file
175 ///< is nullified after each transaction
176 fJournalPersist = 0x10, ///< Journal on disk, journal file can only
177 ///< grow, never shrinks
178 fJournalMemory = 0x20, ///< Journaling is entirely in-memory
179 fJournalOff = 0x40, ///< Journaling is completely off (not
180 ///< recommended - transactions cannot be
181 ///< rollbacked unless they consist of just
182 ///< one simple operation)
183 /// Default value for journaling group of flags
184 fDefaultJournal = fJournalDelete,
185 eAllJournal = fJournalDelete + fJournalTruncate + fJournalPersist
186 + fJournalMemory + fJournalOff,
187
188 // Mode of reliable synchronization with disk database file
189 fSyncFull = 0x000, ///< Full synchronization, database cannot be
190 ///< corrupted
191 fSyncOn = 0x080, ///< Synchronization is on, there is a slight
192 ///< chance of database corruption when
193 ///< OS crashes
194 fSyncOff = 0x100, ///< Synchronization is off, database can be
195 ///< corrupted on OS crash or power outage
196 /// Default value for synchronization group of flags
197 fDefaultSync = fSyncFull,
198 eAllSync = fSyncOff + fSyncOn + fSyncFull,
199
200 /// Mode of storing temporary data
201 fTempToMemory = 0x000, ///< Store all temporary tables in memory
202 fTempToFile = 0x200, ///< Use actual disk file for temporary
203 ///< storage
204 /// Default value for "temporary" group of flags
205 fDefaultTemp = fTempToMemory,
206 eAllTemp = fTempToMemory + fTempToFile,
207
208 /// Mode of doing all writes to the database
209 fWritesSync = 0x000, ///< All writes are synchronous - when update
210 ///< has executed data is already in file
211 ///< (though @sa fSyncOff)
212 #ifdef HAVE_SQLITE3ASYNC_H
213 fWritesAsync = 0x400, ///< All writes are asynchronous - updates
214 ///< return as quick as they can, actual
215 ///< writes to database happen in background
216 ///< thread (@sa
217 ///< CSQLITE_Global::RunAsyncWritesThread)
218 #endif
219 /// Default value for writes mode group of flags
220 fDefaultWrites = fWritesSync,
221 eAllWrites = fWritesSync
222 #ifdef HAVE_SQLITE3ASYNC_H
223 + fWritesAsync
224 #endif
225 ,
226
227 fReadOnly = 0x8000, ///< The DB is read-only. Also forces fVacuumOff flag.
228
229 /// Default value of all flags
230 eDefaultFlags = fDefaultMT + fDefaultVacuum + fDefaultJournal
231 + fDefaultSync + fDefaultTemp + fDefaultWrites
232 };
233 /// Bit mask of EOperationFlags
234 typedef int TOperationFlags;
235
236 /// Connect to SQLite database.
237 /// NOTE: Vacuuming flags have any value here only when new database is
238 /// created. When old database is opened with some data in it
239 /// then vacuuming flags are no-op, flags used at creation are in
240 /// effect.
241 /// NOTE: Connection should be destroyed after all statements or blobs
242 /// created on top of it. destroying statements after connection
243 /// was destroyed will result in segmentation fault.
244 ///
245 /// @param file_name
246 /// File name of SQLite database. If it doesn't exist it will be
247 /// created. If directory for file doesn't exist exception will be
248 /// thrown. If file name is empty connection to temporary file will be
249 /// open. This file will be deleted after connection is closed. If file
250 /// name is ":memory:" then connection to in-memory database will be
251 /// open. In both cases (temporary file and in-memory database) flag
252 /// fExternalMT should be set (it is not checked but you will get
253 /// malfunctioning otherwise).
254 /// @param flags
255 /// Flags for object operations
256 CSQLITE_Connection(CTempString file_name,
257 TOperationFlags flags = eDefaultFlags);
258 ~CSQLITE_Connection(void);
259
260 /// Get database file name for the connection
261 const string& GetFileName(void) const;
262 /// Get flags controlling database connection operation
263 TOperationFlags GetFlags (void) const;
264 /// Change flags controlling database connection operation.
265 /// NOTE: Vacuuming flags cannot be changed here. New flags will be
266 /// applied only to newly created low-level SQLite connections.
267 /// Thus in fInternalMT mode it is recommended to destroy all
268 /// CSQLITE_Statement and CSQLITE_Blob objects before changing
269 /// flags. In fExternalMT mode this cleaning of statements is
270 /// mandatory - you will get memory and other resources leak
271 /// otherwise.
272 void SetFlags(TOperationFlags flags);
273 /// Set page size for the database (in bytes). Setting has any value only
274 /// if database is empty (has no tables) and no statements are created
275 /// yet. Page size should be power of 2 and be less than or equal to
276 /// 32768. If page size is not set default value is used.
277 void SetPageSize(unsigned int size);
278 /// Get page size used in the database (in bytes)
279 unsigned int GetPageSize(void);
280 /// Set recommended size of the database cache (number of pages). If value
281 /// is not set or set to 0 then default SQLite value is used.
282 void SetCacheSize(unsigned int size);
283 /// Get recommended size of the database cache. If cache size was not set
284 /// by SetCacheSize() method then this method returns 0, though actually
285 /// SQLite uses some default value.
286 unsigned int GetCacheSize(void);
287
288 /// Try to shrink database and free up to max_free_size bytes of disk
289 /// space. Method is no-op when vacuum mode is other than fVacuumManual.
290 ///
291 /// @sa EOperationFlags
292 void Vacuum(size_t max_free_size);
293 /// Create statement for executing manual vacuuming.
294 /// Caller is responsible for deleting returned statement object.
295 ///
296 /// @sa Vacuum
297 CSQLITE_Statement* CreateVacuumStmt(size_t max_free_size);
298
299 /// Execute sql statement without returning any results.
300 void ExecuteSql(CTempString sql);
301 /// Delete database under this connection. Method makes sure that journal
302 /// file is deleted along with database file itself. All CSQLITE_Statement
303 /// and CSQLITE_Blob objects on this connection should be deleted before
304 /// call to this method. Any use of connection after call to this method
305 /// will cause the database file to be created again.
306 void DeleteDatabase(void);
307
308 /// Create a read-only copy of the database in memory.
309 /// The in-memory database is never synchronized with the original file.
310 /// If 'shared' is true, a single copy of in-memory database is used.
311 /// Otherwise each call to the function creates a new database and
312 /// the returned connection is the only way to access it.
313 /// On error return NULL.
314 static CSQLITE_Connection* CreateInMemoryDatabase(CTempString file_name,
315 bool shared = false);
316
317 public:
318 // For internal use only
319
320 /// Lock and return low-level connection handle
321 sqlite3* LockHandle(void);
322 /// Unlock unused low-level connection handle. This handle will be used
323 /// later by some other statement. Essentially no-op in eExternalMT mode.
324 ///
325 /// @sa EMultiThreadMode
326 void UnlockHandle(sqlite3* handle);
327 /// Setup newly created low-level connection handle.
328 /// Executes all necessary pragmas according to operation flags set.
329 void SetupNewConnection(sqlite3* handle);
330
331 private:
332 CSQLITE_Connection(const CSQLITE_Connection&);
333 CSQLITE_Connection& operator=(const CSQLITE_Connection&);
334
335 /// Check that only one flag in specific group of flags is given
336 void x_CheckFlagsValidity(TOperationFlags flags, EOperationFlags mask);
337 /// Execute sql statement on given low-level connection handle
338 void x_ExecuteSql(sqlite3* handle, CTempString sql);
339
340
341 typedef CObjPool<sqlite3, CSQLITE_HandleFactory> THandlePool;
342
343 /// File name of SQLite database
344 string m_FileName;
345 /// Flags for object operation
346 TOperationFlags m_Flags;
347 /// Page size inside the database
348 unsigned int m_PageSize;
349 /// Recommended size of cache for the database
350 unsigned int m_CacheSize;
351 /// Pool of low-level database connections
352 THandlePool m_HandlePool;
353 // In-memory database can not use handle pool. The only handle
354 // is locked for the whole database life.
355 sqlite3* m_InMemory;
356 };
357
358
359 /// SQL statement executing on SQLite database
360 class CSQLITE_Statement
361 {
362 public:
363 /// Create and prepare statement for given database connection.
364 /// If sql is empty nothing is prepared in the constructor.
365 CSQLITE_Statement(CSQLITE_Connection* conn, CTempString sql = kEmptyStr);
366 /// Create and prepare statement for particular low-level connection.
367 /// If sql is empty nothing is prepared in the constructor.
368 CSQLITE_Statement(sqlite3* conn_handle, CTempString sql = kEmptyStr);
369 ~CSQLITE_Statement();
370
371 /// Change sql text for the object and prepare new statement
372 void SetSql(CTempString sql);
373
374 /// Bind integer value to parameter index.
375 /// Parameters' numbers start from 1.
376 void Bind (int index, int val);
377 /// Bind unsigned integer value to parameter index.
378 /// Parameters' numbers start from 1.
379 void Bind (int index, unsigned int val);
380 /// Bind integer value to parameter index.
381 /// Parameters' numbers start from 1.
382 void Bind (int index, long val);
383 /// Bind unsigned integer value to parameter index.
384 /// Parameters' numbers start from 1.
385 void Bind (int index, unsigned long val);
386 #if !NCBI_INT8_IS_LONG
387 /// Bind 64-bit integer value to parameter index.
388 /// Parameters' numbers start from 1.
389 void Bind (int index, Int8 val);
390 /// Bind unsigned 64-bit integer value to parameter index.
391 /// Parameters' numbers start from 1.
392 void Bind (int index, Uint8 val);
393 #endif
394 /// Bind double value to parameter index.
395 /// Parameters' numbers start from 1.
396 void Bind (int index, double val);
397 /// Bind text value to parameter index.
398 /// Parameters' numbers start from 1. Value of parameter is copied inside
399 /// the method so it may disappear after call.
400 void Bind (int index, CTempString val);
401 /// Bind text value to parameter index.
402 /// Parameters' numbers start from 1. Value of parameter is not copied,
403 /// pointer is remembered instead. So given value should exist until
404 /// statement is executed.
405 void Bind (int index, const char* data, size_t size);
406 /// Bind blob value to parameter index.
407 /// Parameters' numbers start from 1. Value of parameter is not copied,
408 /// pointer is remembered instead. So given value should exist until
409 /// statement is executed.
410 void Bind (int index, const void* data, size_t size);
411 /// Bind blob value of given size containing only zeros to parameter
412 /// index. Parameters' numbers start from 1.
413 void BindZeroedBlob(int index, size_t size);
414 /// Bind null value to parameter index.
415 void BindNull (int index);
416
417 /// Execute statement without returning any result
418 void Execute(void);
419 /// Step through results of the statement.
420 /// If statement wasn't executed yet it starts executing. If statement
421 /// already returned some rows then it moves to the next row. Be aware
422 /// that when statement begins executing until it returns all rows or
423 /// is reseted the database is locked so that nobody else can write to it.
424 /// So it's recommended to step through all results as quick as possible.
425 ///
426 /// @return
427 /// TRUE if new row is available in the result.
428 /// FALSE if no more rows are available and thus statement finished
429 /// executing and released all locks held.
430 bool Step(void);
431 /// Reset the statement to release all locks and to be ready to execute
432 /// again.
433 void Reset(void);
434 /// Reset all bindings to the statement to their initial NULL values
435 void ClearBindings(void);
436
437 /// Get number of columns in result set. Method should be executed only
438 /// after Step() returned TRUE, otherwise returned value is undefined.
439 int GetColumnsCount(void) const;
440 /// Get name of column at index col_ind in result set. The leftmost column
441 /// of result set has the index 0. Method should be executed only after
442 /// Step() returned TRUE, otherwise returned value is undefined.
443 CStringUTF8 GetColumnName(int col_ind) const;
444
445 /// Get integer value from column col_ind in current row. The leftmost
446 /// column of result set has index 0. Method should be executed only
447 /// after Step() returned TRUE, otherwise returned value is undefined.
448 int GetInt (int col_ind) const;
449 /// Get 64-bit integer value from column col_ind in current row.
450 /// The leftmost column of result set has the index 0. Method should be
451 /// executed only after Step() returned TRUE, otherwise returned value is
452 /// undefined.
453 Int8 GetInt8 (int col_ind) const;
454 /// Get double value from column col_ind in current row. The leftmost
455 /// column of result set has the index 0. Method should be executed only
456 /// after Step() returned TRUE, otherwise returned value is undefined.
457 double GetDouble(int col_ind) const;
458 /// Get text value from column col_ind in current row. The leftmost
459 /// column of result set has the index 0. Method should be executed only
460 /// after Step() returned TRUE, otherwise returned value is undefined.
461 string GetString(int col_ind) const;
462 /// Get size of blob value from column col_ind in current row. The leftmost
463 /// column of result set has the index 0. Method should be executed only
464 /// after Step() returned TRUE, otherwise returned value is undefined.
465 size_t GetBlobSize(int col_ind) const;
466 /// Read blob value from column col_ind in current row. The leftmost
467 /// column of result set has the index 0. Method should be executed only
468 /// after Step() returned TRUE, otherwise returned value is undefined.
469 ///
470 /// @param col_ind
471 /// Index of column to read (left-most is 0)
472 /// @param buffer
473 /// Pointer to buffer where to write the data
474 /// @param size
475 /// Size of buffer available for writing
476 /// @return
477 /// Number of bytes read from result set and put into buffer
478 size_t GetBlob (int col_ind, void* buffer, size_t size) const;
479
480 /// Get rowid of the row inserted in last statement execution.
481 /// If connection is working in eExternalMT mode then method should be
482 /// called after statement execution and before execution of any other
483 /// statement in the same connection. If last executed statement was not
484 /// insert then 0 is returned.
485 Int8 GetLastInsertedRowid(void) const;
486 /// Get number of rows changed during last insert, delete or update
487 /// statement. Number does not include rows changed by triggers or by
488 /// any other indirect means. If called when no insert, update or delete
489 /// statement was executed result is undefined.
490 int GetChangedRowsCount (void) const;
491
492 private:
493 CSQLITE_Statement(const CSQLITE_Statement&);
494 CSQLITE_Statement& operator=(const CSQLITE_Statement&);
495
496 /// Prepare new statement if it's not empty
497 void x_Prepare(CTempString sql);
498 /// Finalize current statement
499 void x_Finalize(void);
500
501
502 /// Connection this statement belongs to
503 CSQLITE_Connection* m_Conn;
504 /// Low-level connection handle provided for this statement
505 sqlite3* m_ConnHandle;
506 /// Low-level statement handle
507 sqlite3_stmt* m_StmtHandle;
508 };
509
510
511 /// "Scoped" statement activity object.
512 /// Object binds to some statement and ensures that by the end of the scope
513 /// statement will be reseted and all database locks will be released. Besides
514 /// that object acts as smart pointer to the statement.
515 class CSQLITE_StatementLock
516 {
517 public:
518 /// Bind activity control to the given statement
519 CSQLITE_StatementLock(CSQLITE_Statement* stmt);
520
521 ~CSQLITE_StatementLock(void);
522
523 /// Smart pointer's transformation
524 CSQLITE_Statement& operator* (void);
525
526 /// Smart pointer's transformation
527 CSQLITE_Statement* operator-> (void);
528
529 /// Smart pointer's transformation
530 operator CSQLITE_Statement* (void);
531
532 private:
533 CSQLITE_StatementLock(const CSQLITE_StatementLock&);
534 CSQLITE_StatementLock& operator= (const CSQLITE_StatementLock&);
535
536 /// Statement this object is bound to
537 CSQLITE_Statement* m_Stmt;
538 };
539
540
541 // Structures for internal use only
542 template <bool readwrite> struct SSQLITE_BlobOpen;
543 struct SSQLITE_BlobClose;
544
545 /// Object reading and writing blob directly (mostly without executing select
546 /// or update statements), can read and write blob incrementally.
547 /// Class assumes that from the moment of its creation till deletion nobody
548 /// else writes to the blob and/or changes its size.
549 class CSQLITE_Blob
550 {
551 public:
552 /// Create object reading and writing blob
553 /// Identified row with blob should exist in database, exception is
554 /// thrown otherwise.
555 ///
556 /// @param conn
557 /// Connection which object will work over
558 /// @param table
559 /// Table name where blob is located
560 /// @param column
561 /// Column name where blob is located
562 /// @param rowid
563 /// Rowid of the row where blob is located
564 CSQLITE_Blob(CSQLITE_Connection* conn,
565 CTempString table,
566 CTempString column,
567 Int8 rowid);
568 /// Create object reading and writing blob
569 /// Identified row with blob should exist in database, exception is
570 /// thrown otherwise.
571 ///
572 /// @param conn
573 /// Connection which object will work over
574 /// @param db_name
575 /// Database name where blob is located
576 /// @param table
577 /// Table name where blob is located
578 /// @param column
579 /// Column name where blob is located
580 /// @param rowid
581 /// Rowid of the row where blob is located
582 CSQLITE_Blob(CSQLITE_Connection* conn,
583 CTempString db_name,
584 CTempString table,
585 CTempString column,
586 Int8 rowid);
587 ~CSQLITE_Blob(void);
588
589 /// Set current position inside the blob to desired value
590 void ResetPosition(size_t position = 0);
591 /// Get current position inside the blob
592 size_t GetPosition (void);
593 /// Get size of the blob
594 size_t GetSize(void);
595
596 /// Read from blob at current position
597 ///
598 /// @param buffer
599 /// Pointer to buffer where to write read data
600 /// @param size
601 /// Size of buffer available for writing
602 /// @return
603 /// Number of bytes read from blob. If current position is beyond end
604 /// of the blob then 0 is returned and buffer is unchanged.
605 size_t Read (void* buffer, size_t size);
606 /// Write to blob at current position
607 ///
608 /// @param data
609 /// Pointer to data to write
610 /// @param size
611 /// Number of bytes to write
612 void Write (const void* data, size_t size);
613 /// Set statement to use when appending to existing blob if necessary.
614 /// Statement should be of the form
615 /// "update ... set ... = ...||?2 where rowid=?1"
616 /// If not set object creates its own statement on the first append.
617 /// Created statement is destroyed together with this blob object.
618 /// Statement set by this method is not destroyed with blob object.
619 /// This method can be useful if one wants to cache statement between
620 /// different blob objects creation and deletion to not prepare it with
621 /// each blob object.
622 void SetAppendStatement(CSQLITE_Statement* stmt);
623
624 private:
625 CSQLITE_Blob(const CSQLITE_Blob&);
626 CSQLITE_Blob& operator= (const CSQLITE_Blob&);
627
628 /// Initialize the object
629 void x_Init (void);
630 /// Open low-level blob object
631 ///
632 /// @param readwrite
633 /// TRUE if there's need for write access, FALSE if read-only access is
634 /// sufficient
635 void x_OpenBlob (bool readwrite = false);
636 /// Close low-level blob object
637 void x_CloseBlob(void);
638
639 template <bool readwrite> friend struct SSQLITE_BlobOpen;
640 friend struct SSQLITE_BlobClose;
641
642
643 /// Connection this blob object belongs to
644 CSQLITE_Connection* m_Conn;
645 /// Low-level connection handle provided for this blob object
646 sqlite3* m_ConnHandle;
647 /// Statement used for appending to existing blob value
648 AutoPtr<CSQLITE_Statement> m_AppendStmt;
649 /// Database name for blob
650 string m_Database;
651 /// Table name for blob
652 string m_Table;
653 /// Column name for blob
654 string m_Column;
655 /// Rowid for the row where blob is located
656 Int8 m_Rowid;
657 /// Low-level handle of the blob
658 sqlite3_blob* m_BlobHandle;
659 /// Size of the blob
660 size_t m_Size;
661 /// Current position inside the blob
662 size_t m_Position;
663 };
664
665
666 /// Exception thrown from all SQLite-related objects and functions
667 class CSQLITE_Exception : public CException
668 {
669 public:
670 enum EErrCode {
671 eUnknown, ///< Unknown error
672 eWrongFlags, ///< Incorrect set of flags in connection constructor
673 eDBOpen, ///< Error during database opening
674 eStmtPrepare, ///< Error preparing statement
675 eStmtFinalize, ///< Error finalizing statement
676 eStmtBind, ///< Error binding statement parameters
677 eStmtStep, ///< Error stepping through the statement
678 eStmtReset, ///< Error reseting statement
679 eBlobOpen, ///< Error opening blob object
680 eBlobClose, ///< Error closing blob object
681 eBlobRead, ///< Error reading directly from blob
682 eBlobWrite, ///< Error writing directly to blob
683 eConstraint, ///< Constraint violation during statement execution
684 eDeadLock, ///< SQLite detected possible deadlock between
685 ///< different threads
686 eBadCall ///< Method called when there's no enough capabilities
687 ///< to finish it successfully
688 };
689 virtual const char* GetErrCodeString(void) const override;
690 NCBI_EXCEPTION_DEFAULT(CSQLITE_Exception, CException);
691 };
692
693
694
695 //////////////////////////////////////////////////////////////////////////
696 // Inline functions
697 //////////////////////////////////////////////////////////////////////////
698
699 inline
CSQLITE_Statement(CSQLITE_Connection * conn,CTempString sql)700 CSQLITE_Statement::CSQLITE_Statement(CSQLITE_Connection* conn,
701 CTempString sql)
702 : m_Conn(conn),
703 m_ConnHandle(NULL),
704 m_StmtHandle(NULL)
705 {
706 x_Prepare(sql);
707 }
708
709 inline
CSQLITE_Statement(sqlite3 * conn_handle,CTempString sql)710 CSQLITE_Statement::CSQLITE_Statement(sqlite3* conn_handle,
711 CTempString sql)
712 : m_Conn(NULL),
713 m_ConnHandle(conn_handle),
714 m_StmtHandle(NULL)
715 {
716 x_Prepare(sql);
717 }
718
719 inline void
SetSql(CTempString sql)720 CSQLITE_Statement::SetSql(CTempString sql)
721 {
722 x_Finalize();
723 x_Prepare(sql);
724 }
725
726 inline void
Execute(void)727 CSQLITE_Statement::Execute(void)
728 {
729 while (Step())
730 {}
731 }
732
733 inline void
Bind(int index,Uint8 val)734 CSQLITE_Statement::Bind(int index, Uint8 val)
735 {
736 // SQLite doesn't understand unsigned types anyway
737 Bind(index, static_cast<Int8>(val));
738 }
739
740 #if !NCBI_INT8_IS_LONG
741
742 inline void
Bind(int index,long val)743 CSQLITE_Statement::Bind(int index, long val)
744 {
745 Bind(index, static_cast<Int8>(val));
746 }
747
748 inline void
Bind(int index,unsigned long val)749 CSQLITE_Statement::Bind(int index, unsigned long val)
750 {
751 Bind(index, static_cast<Uint8>(val));
752 }
753
754 #endif
755
756 inline void
Bind(int index,int val)757 CSQLITE_Statement::Bind(int index, int val)
758 {
759 Bind(index, static_cast<long>(val));
760 }
761
762 inline void
Bind(int index,unsigned int val)763 CSQLITE_Statement::Bind(int index, unsigned int val)
764 {
765 Bind(index, static_cast<unsigned long>(val));
766 }
767
768 inline int
GetColumnsCount(void) const769 CSQLITE_Statement::GetColumnsCount(void) const
770 {
771 _ASSERT(m_StmtHandle);
772 return sqlite3_column_count(m_StmtHandle);
773 }
774
775 inline int
GetInt(int col_ind) const776 CSQLITE_Statement::GetInt(int col_ind) const
777 {
778 _ASSERT(m_StmtHandle);
779 return sqlite3_column_int(m_StmtHandle, col_ind);
780 }
781
782 inline Int8
GetInt8(int col_ind) const783 CSQLITE_Statement::GetInt8(int col_ind) const
784 {
785 _ASSERT(m_StmtHandle);
786 return sqlite3_column_int64(m_StmtHandle, col_ind);
787 }
788
789 inline double
GetDouble(int col_ind) const790 CSQLITE_Statement::GetDouble(int col_ind) const
791 {
792 _ASSERT(m_StmtHandle);
793 return sqlite3_column_double(m_StmtHandle, col_ind);
794 }
795
796 inline Int8
GetLastInsertedRowid(void) const797 CSQLITE_Statement::GetLastInsertedRowid(void) const
798 {
799 _ASSERT(m_ConnHandle);
800 return sqlite3_last_insert_rowid(m_ConnHandle);
801 }
802
803 inline int
GetChangedRowsCount(void) const804 CSQLITE_Statement::GetChangedRowsCount(void) const
805 {
806 _ASSERT(m_ConnHandle);
807 return sqlite3_changes(m_ConnHandle);
808 }
809
810
811 inline const string&
GetFileName(void) const812 CSQLITE_Connection::GetFileName(void) const
813 {
814 return m_FileName;
815 }
816
817 inline CSQLITE_Connection::TOperationFlags
GetFlags(void) const818 CSQLITE_Connection::GetFlags(void) const
819 {
820 return m_Flags;
821 }
822
823 inline unsigned int
GetPageSize(void)824 CSQLITE_Connection::GetPageSize(void)
825 {
826 return m_PageSize;
827 }
828
829 inline void
SetPageSize(unsigned int size)830 CSQLITE_Connection::SetPageSize(unsigned int size)
831 {
832 m_PageSize = size;
833 m_HandlePool.Clear();
834 }
835
836 inline unsigned int
GetCacheSize(void)837 CSQLITE_Connection::GetCacheSize(void)
838 {
839 return m_CacheSize == (unsigned int)(-1) ? 0 : m_CacheSize;
840 }
841
842 inline void
SetCacheSize(unsigned int size)843 CSQLITE_Connection::SetCacheSize(unsigned int size)
844 {
845 m_CacheSize = size;
846 m_HandlePool.Clear();
847 }
848
849 inline sqlite3*
LockHandle(void)850 CSQLITE_Connection::LockHandle(void)
851 {
852 if ( m_InMemory ) {
853 return m_InMemory;
854 }
855 sqlite3* result = m_HandlePool.Get();
856 if ((m_Flags & eAllMT) == fExternalMT) {
857 m_HandlePool.Return(result);
858 }
859 return result;
860 }
861
862 inline void
UnlockHandle(sqlite3 * handle)863 CSQLITE_Connection::UnlockHandle(sqlite3* handle)
864 {
865 if (m_InMemory && handle == m_InMemory) {
866 return;
867 }
868 if ((m_Flags & eAllMT) == fInternalMT) {
869 m_HandlePool.Return(handle);
870 }
871 }
872
873 inline void
ExecuteSql(CTempString sql)874 CSQLITE_Connection::ExecuteSql(CTempString sql)
875 {
876 CSQLITE_Statement stmt(this, sql);
877 stmt.Execute();
878 }
879
880 inline void
Vacuum(size_t max_free_size)881 CSQLITE_Connection::Vacuum(size_t max_free_size)
882 {
883 AutoPtr<CSQLITE_Statement> stmt(CreateVacuumStmt(max_free_size));
884 stmt->Execute();
885 }
886
887
888 inline
CSQLITE_StatementLock(CSQLITE_Statement * stmt)889 CSQLITE_StatementLock::CSQLITE_StatementLock(CSQLITE_Statement* stmt)
890 : m_Stmt(stmt)
891 {
892 _ASSERT(stmt);
893 }
894
895 inline CSQLITE_Statement&
operator *(void)896 CSQLITE_StatementLock::operator* (void)
897 {
898 return *m_Stmt;
899 }
900
901 inline CSQLITE_Statement*
operator ->(void)902 CSQLITE_StatementLock::operator-> (void)
903 {
904 return m_Stmt;
905 }
906
907 inline
operator CSQLITE_Statement*(void)908 CSQLITE_StatementLock::operator CSQLITE_Statement* (void)
909 {
910 return m_Stmt;
911 }
912
913
914 inline
~CSQLITE_Blob(void)915 CSQLITE_Blob::~CSQLITE_Blob(void)
916 {
917 x_CloseBlob();
918 if (m_ConnHandle) {
919 m_Conn->UnlockHandle(m_ConnHandle);
920 }
921 }
922
923 inline size_t
GetPosition(void)924 CSQLITE_Blob::GetPosition(void)
925 {
926 return m_Position;
927 }
928
929 inline void
ResetPosition(size_t position)930 CSQLITE_Blob::ResetPosition(size_t position /* = 0 */)
931 {
932 m_Position = position;
933 }
934
935 inline void
SetAppendStatement(CSQLITE_Statement * stmt)936 CSQLITE_Blob::SetAppendStatement(CSQLITE_Statement* stmt)
937 {
938 m_AppendStmt.reset(stmt, eNoOwnership);
939 }
940
941 END_NCBI_SCOPE
942
943 #endif /* DB_SQLITE_SQLITEWRAPP__HPP */
944