GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/sql.h
Date: 2025-02-09 02:34:19
Exec Total Coverage
Lines: 107 113 94.7%
Branches: 18 24 75.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM file system.
3 */
4
5 #ifndef CVMFS_SQL_H_
6 #define CVMFS_SQL_H_
7
8 #include <cassert>
9 #include <string>
10
11 #include "duplex_sqlite3.h"
12 #include "util/file_guard.h"
13 #include "util/pointer.h"
14
15 namespace sqlite {
16
17 struct MemStatistics {
18 1 MemStatistics()
19 1 : lookaside_slots_used(-1)
20 1 , lookaside_slots_max(-1)
21 1 , lookaside_hit(-1)
22 1 , lookaside_miss_size(-1)
23 1 , lookaside_miss_full(-1)
24 1 , page_cache_used(-1)
25 1 , page_cache_hit(-1)
26 1 , page_cache_miss(-1)
27 1 , schema_used(-1)
28 1 , stmt_used(-1)
29 1 { }
30 int lookaside_slots_used;
31 int lookaside_slots_max;
32 int lookaside_hit;
33 int lookaside_miss_size;
34 int lookaside_miss_full;
35 int page_cache_used; ///< Bytes used for caching pages
36 int page_cache_hit;
37 int page_cache_miss;
38 int schema_used; ///< Bytes used to store db schema
39 int stmt_used; ///< Bytes used for prepared statmements (lookaside + heap)
40 };
41
42 class Sql;
43
44 /**
45 * Encapsulates an SQlite connection.
46 *
47 * This is an abstract base class for different SQLite database flavours used
48 * throughout CernVM-FS. It provides a general interface for creating, opening,
49 * compacting and migrating an SQLite database.
50 * Furthermore it manages a `properties` table in each database, to store simple
51 * key-value style information in a common fashion. For that, it offers the
52 * templated methods SetProperty(), GetProperty<>() and HasProperty() that take
53 * all common data types and persist it in the database.
54 *
55 * By default Database<> objects do not take ownership of the underlying SQLite
56 * database file and hence do not unlink it on database closure. If the using
57 * code calls Database<>::TakeFileOwnership() the SQLite file will be unlinked
58 * in the destructor of the Database<> object.
59 *
60 * Note: This implements a Curiously Recurring Template Pattern in order to
61 * implement Database::Create and Database::Open as a static polymorphism
62 *
63 * The following methods need to be implemented by each subclass:
64 * (Database<> assumes 'true' as a return value on success)
65 *
66 * -> bool CreateEmptyDatabase()
67 * creates all necessary SQLite tables for the concrete Database
68 * implementation. Furthermore it can insert default data into the
69 * newly created database tables.
70 * -> bool CheckSchemaCompatibility()
71 * checks a database for compatibility directly after opening it.
72 * Database<> provides schema_version() and schema_revision() to
73 * access the compatibility stored in the `properties` table
74 * -> bool LiveSchemaUpgradeIfNecessary()
75 * this allows for on-the-fly schema updates and is always called
76 * when a database is opened read/write. It assumes 'true' both on
77 * successful migration and if no migration was necessary
78 * -> bool CompactDatabase()
79 * here implementation specific cleanup actions can take place on
80 * databases opened as read/write. It is invoked by the `Vacuum()`
81 * method, that can be used by higher level user code
82 *
83 * Furthermore Database<> expects two static constants to be defined:
84 *
85 * -> kLatestSchema - the newest schema version generated by invoking
86 * DerivedT::CreateEmptyDatabase()
87 * -> kLatestSchemaRevision - same as kLatestSchema, however different schema
88 * revisions are supposed to be backward compatible
89 * or on-the-fly updateable by
90 * DerivedT::LiveSchemaUpgradeIfNecessary()
91 *
92 * @param DerivedT the name of the inheriting Database implementation class
93 * (Curiously Recurring Template Pattern)
94 *
95 * TODO(rmeusel): C++11 Move Constructors to allow for stack allocated databases
96 */
97 template <class DerivedT>
98 class Database : SingleCopy {
99 public:
100 enum OpenMode {
101 kOpenReadOnly,
102 kOpenReadWrite,
103 };
104
105 static const float kSchemaEpsilon; // floats get imprecise in SQlite
106
107 /**
108 * Creates a new database file of the type implemented by DerivedT. During the
109 * invocation of this static method DerivedT::CreateEmptyDatabase() is called.
110 *
111 * @param filename the file location of the newly created database
112 * (file does not need to exist)
113 * @return an empty database of type DerivedT (or NULL on failure)
114 */
115 static DerivedT* Create(const std::string &filename);
116
117 /**
118 * Opens a database file and assumes it to be of type DerivedT. This method
119 * will call DerivedT::CheckSchemaCompatibility() to figure out readability of
120 * the contained schema revision. Furthermore, if the database was opened in
121 * read/write mode, it calls DerivedT::LiveSchemaUpgradeIfNecessary() to allow
122 * for on-the-fly schema upgrades of the underlying database file.
123 *
124 * @param filename path to the SQLite file to be opened as DerivedT
125 * @param open_mode kOpenReadOnly or kOpenReadWrite open modes
126 * @return a database of type DerivedT (or NULL on failure)
127 */
128 static DerivedT* Open(const std::string &filename,
129 const OpenMode open_mode);
130
131 2347 bool IsEqualSchema(const float value, const float compare) const {
132
2/2
✓ Branch 0 taken 2314 times.
✓ Branch 1 taken 33 times.
4661 return (value > compare - kSchemaEpsilon &&
133
2/2
✓ Branch 0 taken 2169 times.
✓ Branch 1 taken 145 times.
4661 value < compare + kSchemaEpsilon);
134 }
135
136 bool BeginTransaction() const;
137 bool CommitTransaction() const;
138
139 template <typename T>
140 T GetProperty(const std::string &key) const;
141 template <typename T>
142 T GetPropertyDefault(const std::string &key, const T default_value) const;
143 template <typename T>
144 bool SetProperty(const std::string &key, const T value);
145 bool HasProperty(const std::string &key) const;
146
147 7057 sqlite3* sqlite_db() const { return database_.database(); }
148 1599 const std::string& filename() const { return database_.filename(); }
149 4323 float schema_version() const { return schema_version_; }
150 4235 unsigned schema_revision() const { return schema_revision_; }
151 947 bool read_write() const { return read_write_; }
152
153 /**
154 * Provides the number of rows modified by INSERT, UPDATE or DELETE statements
155 * that have been run against this database since it was opened.
156 * @return number of rows modified by all executed manipulating statements
157 */
158 unsigned GetModifiedRowCount() const;
159
160 /**
161 * Figures out the ratio of free SQLite memory pages in the SQLite database
162 * file. A high ratio can be an indication of a necessary call to Vacuum().
163 * Note: This is not done automatically and the decision is left to the using
164 * code!
165 * @return the free-page ratio in the opened database file (free pages/pages)
166 */
167 double GetFreePageRatio() const;
168
169 /**
170 * Retrieves the per-connection memory statistics from SQlite
171 */
172 void GetMemStatistics(MemStatistics *stats) const;
173
174 /**
175 * Performs a VACUUM call on the opened database file to compacts the database.
176 * As a first step it runs DerivedT::CompactDatabase() to allow for implement-
177 * ation dependent cleanup actions. Vacuum() assumes that the SQLite database
178 * was opened in read/write mode.
179 * @return true on success
180 */
181 bool Vacuum() const;
182
183 /**
184 * Prints the given error message, together with the last encountered SQLite
185 * error of this database.
186 * @param error_msg an error message to be printed along with the SQL error
187 */
188 void PrintSqlError(const std::string &error_msg);
189
190 /**
191 * Returns the english language error description of the last error
192 * happened in the context of the encapsulated sqlite3 database object.
193 * Note: In a multithreaded context it might be unpredictable which
194 * the actual last error is.
195 * @return english language error description of last error
196 */
197 std::string GetLastErrorMsg() const;
198
199
200 /**
201 * Transfers the ownership of the SQLite database file to the Database<> ob-
202 * ject. Hence, it will automatically unlink the file, once the Database<>
203 * object goes out of scope or is deleted.
204 */
205 void TakeFileOwnership();
206
207 /**
208 * Resigns from the ownership of the SQLite database file underlying this
209 * Database<> object. After calling this the using code is responsible of
210 * managing the database file.
211 */
212 void DropFileOwnership();
213
214 /**
215 * Check if the SQLite database file is managed by the Database<> object
216 * Note: unmanaged means, that the using code needs to take care of the file
217 * management (i.e. delete the file after usage)
218 *
219 * @return false if file is unmanaged
220 */
221 27 bool OwnsFile() const { return database_.OwnsFile(); }
222
223 /**
224 * Used when attaching legacy catalogs to set 0.9 schema where mistakenly 1.0
225 * was used.
226 */
227 1 void EnforceSchema(float version, unsigned revision) {
228 1 schema_version_ = version;
229 1 schema_revision_ = revision;
230 1 }
231
232 protected:
233 /**
234 * Private constructor! Use the factory methods DerivedT::Create() or
235 * DerivedT::Open() to instantiate a database object of type DerivedT.
236 */
237 Database(const std::string &filename,
238 const OpenMode open_mode);
239
240 bool Initialize();
241
242 bool CreatePropertiesTable();
243 bool PrepareCommonQueries();
244
245 bool OpenDatabase(const int sqlite_open_flags);
246 bool Configure();
247 bool FileReadAhead();
248
249 void ReadSchemaRevision();
250 bool StoreSchemaRevision();
251
252 228 void set_schema_version(const float ver) { schema_version_ = ver; }
253 253 void set_schema_revision(const unsigned rev) { schema_revision_ = rev; }
254
255 private:
256 /**
257 * This wraps the opaque SQLite database object along with a file unlink guard
258 * to control the life time of the database connection and the database file
259 * in an RAII fashion.
260 */
261 struct DatabaseRaiiWrapper {
262 443 DatabaseRaiiWrapper(const std::string &filename,
263 Database<DerivedT> *delegate)
264 443 : sqlite_db(NULL)
265 443 , lookaside_buffer(NULL)
266 443 , db_file_guard(filename, UnlinkGuard::kDisabled)
267 443 , delegate_(delegate) {}
268 ~DatabaseRaiiWrapper();
269
270 7057 sqlite3* database() const { return sqlite_db; }
271 2150 const std::string& filename() const { return db_file_guard.path(); }
272
273 bool Close();
274
275 107 void TakeFileOwnership() { db_file_guard.Enable(); }
276 4 void DropFileOwnership() { db_file_guard.Disable(); }
277 27 bool OwnsFile() const { return db_file_guard.IsEnabled(); }
278
279 sqlite3 *sqlite_db;
280 void *lookaside_buffer;
281 UnlinkGuard db_file_guard;
282 Database<DerivedT> *delegate_;
283 };
284
285 static const char *kSchemaVersionKey;
286 static const char *kSchemaRevisionKey;
287
288 DatabaseRaiiWrapper database_;
289
290 const bool read_write_;
291 float schema_version_;
292 unsigned schema_revision_;
293
294 UniquePtr<Sql> begin_transaction_;
295 UniquePtr<Sql> commit_transaction_;
296
297 UniquePtr<Sql> has_property_;
298 UniquePtr<Sql> set_property_;
299 UniquePtr<Sql> get_property_;
300 };
301
302
303 //
304 // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
305 //
306
307
308 /**
309 * Base class for all SQL statement classes. It wraps a single SQL statement
310 * and all necessary calls of the sqlite3 API to deal with this statement.
311 *
312 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
313 * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE *
314 * NOTE This base class implements a lazy initialization of the SQLite *
315 * NOTE prepared statement. Therefore it is strongly discouraged to use *
316 * NOTE any sqlite3_***() functions directly in the subclasses. Instead *
317 * NOTE one must wrap them in this base class and implement the lazy *
318 * NOTE initialization scheme as seen below. *
319 * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE *
320 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
321 *
322 * Derived classes can decide if their statement should be prepared immediately
323 * or on first use (aka lazily). The derived constructor must call Sql::Init()
324 * or Sql::DeferredInit() accordingly.
325 *
326 * Sql objects created via the public constructor rather than by the constructor
327 * of a derived class are prepared immediately by default.
328 */
329 class Sql {
330 public:
331 /**
332 * Basic constructor to use this class for a specific statement.
333 * @param database the database to use the query on
334 * @param statement the statement to prepare
335 */
336 Sql(sqlite3 *sqlite_db, const std::string &statement);
337 virtual ~Sql();
338
339 bool Execute();
340 bool FetchRow();
341 std::string DebugResultTable();
342 bool Reset();
343 6 inline int GetLastError() const { return last_error_code_; }
344
345 /**
346 * Returns the english language error description of the last error
347 * happened in the context of the sqlite3 database object this statement is
348 * registered to.
349 * Note: In a multithreaded context it might be unpredictable which
350 * the actual last error is.
351 * @return english language error description of last error
352 */
353 std::string GetLastErrorMsg() const;
354
355 277 bool BindBlob(const int index, const void* value, const unsigned size) {
356 277 LazyInit();
357 277 last_error_code_ =
358 277 sqlite3_bind_blob(statement_, index, value, static_cast<int>(size),
359 SQLITE_STATIC);
360 277 return Successful();
361 }
362 bool BindBlobTransient(const int index, const void* value,
363 const unsigned size)
364 {
365 LazyInit();
366 last_error_code_ =
367 sqlite3_bind_blob(statement_, index, value, static_cast<int>(size),
368 SQLITE_TRANSIENT); // NOLINT
369 return Successful();
370 }
371 254 bool BindDouble(const int index, const double value) {
372 254 LazyInit();
373 254 last_error_code_ = sqlite3_bind_double(statement_, index, value);
374 254 return Successful();
375 }
376 1422 bool BindInt(const int index, const int value) {
377 1422 LazyInit();
378 1422 last_error_code_ = sqlite3_bind_int(statement_, index, value);
379 1422 return Successful();
380 }
381 29479 bool BindInt64(const int index, const sqlite3_int64 value) {
382 29479 LazyInit();
383 29479 last_error_code_ = sqlite3_bind_int64(statement_, index, value);
384 29479 return Successful();
385 }
386 1624 bool BindNull(const int index) {
387 1624 LazyInit();
388 1624 last_error_code_ = sqlite3_bind_null(statement_, index);
389 1624 return Successful();
390 }
391 154575 bool BindTextTransient(const int index, const std::string &value) {
392 154575 return BindTextTransient(
393 309150 index, value.data(), static_cast<int>(value.length()));
394 }
395 154575 bool BindTextTransient(const int index, const char *value, const int size) {
396 // NOLINTNEXTLINE(performance-no-int-to-ptr)
397 154575 return BindText(index, value, size, SQLITE_TRANSIENT);
398 }
399 175206 bool BindText(const int index, const std::string &value) {
400 175206 return BindText(index, value.data(), static_cast<int>(value.length()),
401 175206 SQLITE_STATIC);
402 }
403 331257 bool BindText(const int index,
404 const char* value,
405 const int size,
406 void(*dtor)(void*) = SQLITE_STATIC) {
407 331257 LazyInit();
408 331257 last_error_code_ = sqlite3_bind_text(statement_, index, value, size, dtor);
409 331257 return Successful();
410 }
411
412 /**
413 * Figures out the type to be bound by template parameter deduction
414 * NOTE: For strings this is suboptimal, since it needs to assume that the
415 * provided buffer is transient and copy it to be sure.
416 */
417 template <typename T>
418 inline bool Bind(const int index, const T &value);
419
420
421 36 int RetrieveType(const int idx_column) const {
422 36 return sqlite3_column_type(statement_, idx_column);
423 }
424
425 /**
426 * Determines the number of bytes necessary to store the column's data as a
427 * string. This might involve type conversions and depends on which other
428 * RetrieveXXX methods were called on the same column index before!
429 *
430 * See SQLite documentation for sqlite_column_bytes() for details:
431 * https://www.sqlite.org/c3ref/column_blob.html
432 */
433 733 int RetrieveBytes(const int idx_column) const {
434 733 return sqlite3_column_bytes(statement_, idx_column);
435 }
436 734 const void *RetrieveBlob(const int idx_column) const {
437 734 return sqlite3_column_blob(statement_, idx_column);
438 }
439 267 double RetrieveDouble(const int idx_column) const {
440 267 return sqlite3_column_double(statement_, idx_column);
441 }
442 710 int RetrieveNullableInt(const int idx_column, const int val_null) const {
443
1/2
✓ Branch 1 taken 710 times.
✗ Branch 2 not taken.
710 if (sqlite3_column_type(statement_, idx_column) == SQLITE_NULL)
444 710 return val_null;
445 return sqlite3_column_int(statement_, idx_column);
446 }
447 2203 int RetrieveInt(const int idx_column) const {
448 2203 return sqlite3_column_int(statement_, idx_column);
449 }
450 15058 sqlite3_int64 RetrieveInt64(const int idx_column) const {
451 15058 return sqlite3_column_int64(statement_, idx_column);
452 }
453 8690 const unsigned char *RetrieveText(const int idx_column) const {
454 8690 return sqlite3_column_text(statement_, idx_column);
455 }
456 7120 std::string RetrieveString(const int idx_column) const {
457
2/4
✓ Branch 2 taken 7120 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7120 times.
✗ Branch 6 not taken.
7120 return reinterpret_cast<const char*>(RetrieveText(idx_column));
458 }
459 template <typename T>
460 inline T Retrieve(const int index);
461
462 protected:
463 5095 Sql()
464 5095 : database_(NULL)
465 5095 , statement_(NULL)
466 5095 , query_string_(NULL)
467 5095 , last_error_code_(0) { }
468
469 538768 bool IsInitialized() const { return statement_ != NULL; }
470
471 /**
472 * Initializes the prepared statement immediately.
473 *
474 * @param database the sqlite database pointer to be query against
475 * @param statement the query string to be prepared for execution
476 * @return true on successful statement preparation
477 */
478 bool Init(const sqlite3 *database, const std::string &statement);
479
480 /**
481 * Defers the initialization of the prepared statement to the first usage to
482 * safe memory and CPU cycles for statements that are defined but never used.
483 * Typically this method is used in constructors of derived classes.
484 *
485 * @param database the sqlite database pointer to be query against
486 * @param statement the query string to be prepared for execution
487 */
488 void DeferredInit(const sqlite3 *database, const char *statement);
489
490 /**
491 * Checks the last action for success
492 * @return true if last action succeeded otherwise false
493 */
494 872087 inline bool Successful() const {
495 1197237 return SQLITE_OK == last_error_code_ ||
496
4/4
✓ Branch 0 taken 325150 times.
✓ Branch 1 taken 546937 times.
✓ Branch 2 taken 324962 times.
✓ Branch 3 taken 188 times.
1197049 SQLITE_ROW == last_error_code_ ||
497
2/2
✓ Branch 0 taken 324942 times.
✓ Branch 1 taken 20 times.
1197049 SQLITE_DONE == last_error_code_;
498 }
499
500 private:
501 bool Init(const char *statement);
502 538768 void LazyInit() {
503
2/2
✓ Branch 1 taken 1005 times.
✓ Branch 2 taken 537763 times.
538768 if (!IsInitialized()) {
504
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1005 times.
1005 assert(NULL != database_);
505
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1005 times.
1005 assert(NULL != query_string_);
506 1005 const bool success = Init(query_string_);
507
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1005 times.
1005 assert(success);
508 }
509 538768 }
510
511 sqlite3 *database_;
512 sqlite3_stmt *statement_;
513 const char *query_string_;
514 int last_error_code_;
515 };
516
517 } // namespace sqlite
518
519 #include "sql_impl.h"
520
521 #endif // CVMFS_SQL_H_
522