GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/sql.h
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 104 109 95.4%
Branches: 17 22 77.3%

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 2244 bool IsEqualSchema(const float value, const float compare) const {
132
2/2
✓ Branch 0 taken 2213 times.
✓ Branch 1 taken 31 times.
4457 return (value > compare - kSchemaEpsilon &&
133
2/2
✓ Branch 0 taken 2069 times.
✓ Branch 1 taken 144 times.
4457 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 7020 sqlite3* sqlite_db() const { return database_.database(); }
148 1594 const std::string& filename() const { return database_.filename(); }
149 4211 float schema_version() const { return schema_version_; }
150 3847 unsigned schema_revision() const { return schema_revision_; }
151 946 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 251 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 442 DatabaseRaiiWrapper(const std::string &filename,
263 Database<DerivedT> *delegate)
264 442 : sqlite_db(NULL)
265 442 , lookaside_buffer(NULL)
266 442 , db_file_guard(filename, UnlinkGuard::kDisabled)
267 442 , delegate_(delegate) {}
268 ~DatabaseRaiiWrapper();
269
270 7020 sqlite3* database() const { return sqlite_db; }
271 2143 const std::string& filename() const { return db_file_guard.path(); }
272
273 bool Close();
274
275 106 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 252 bool BindDouble(const int index, const double value) {
372 252 LazyInit();
373 252 last_error_code_ = sqlite3_bind_double(statement_, index, value);
374 252 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 29471 bool BindInt64(const int index, const sqlite3_int64 value) {
382 29471 LazyInit();
383 29471 last_error_code_ = sqlite3_bind_int64(statement_, index, value);
384 29471 return Successful();
385 }
386 911 bool BindNull(const int index) {
387 911 LazyInit();
388 911 last_error_code_ = sqlite3_bind_null(statement_, index);
389 911 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 175167 bool BindText(const int index, const std::string &value) {
400 175167 return BindText(index, value.data(), static_cast<int>(value.length()),
401 175167 SQLITE_STATIC);
402 }
403 331218 bool BindText(const int index,
404 const char* value,
405 const int size,
406 void(*dtor)(void*) = SQLITE_STATIC) {
407 331218 LazyInit();
408 331218 last_error_code_ = sqlite3_bind_text(statement_, index, value, size, dtor);
409 331218 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 32 int RetrieveType(const int idx_column) const {
422 32 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 730 int RetrieveBytes(const int idx_column) const {
434 730 return sqlite3_column_bytes(statement_, idx_column);
435 }
436 731 const void *RetrieveBlob(const int idx_column) const {
437 731 return sqlite3_column_blob(statement_, idx_column);
438 }
439 266 double RetrieveDouble(const int idx_column) const {
440 266 return sqlite3_column_double(statement_, idx_column);
441 }
442 2192 int RetrieveInt(const int idx_column) const {
443 2192 return sqlite3_column_int(statement_, idx_column);
444 }
445 15005 sqlite3_int64 RetrieveInt64(const int idx_column) const {
446 15005 return sqlite3_column_int64(statement_, idx_column);
447 }
448 8680 const unsigned char *RetrieveText(const int idx_column) const {
449 8680 return sqlite3_column_text(statement_, idx_column);
450 }
451 7116 std::string RetrieveString(const int idx_column) const {
452
2/4
✓ Branch 2 taken 7116 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7116 times.
✗ Branch 6 not taken.
7116 return reinterpret_cast<const char*>(RetrieveText(idx_column));
453 }
454 template <typename T>
455 inline T Retrieve(const int index);
456
457 protected:
458 5073 Sql()
459 5073 : database_(NULL)
460 5073 , statement_(NULL)
461 5073 , query_string_(NULL)
462 5073 , last_error_code_(0) { }
463
464 537936 bool IsInitialized() const { return statement_ != NULL; }
465
466 /**
467 * Initializes the prepared statement immediately.
468 *
469 * @param database the sqlite database pointer to be query against
470 * @param statement the query string to be prepared for execution
471 * @return true on successful statement preparation
472 */
473 bool Init(const sqlite3 *database, const std::string &statement);
474
475 /**
476 * Defers the initialization of the prepared statement to the first usage to
477 * safe memory and CPU cycles for statements that are defined but never used.
478 * Typically this method is used in constructors of derived classes.
479 *
480 * @param database the sqlite database pointer to be query against
481 * @param statement the query string to be prepared for execution
482 */
483 void DeferredInit(const sqlite3 *database, const char *statement);
484
485 /**
486 * Checks the last action for success
487 * @return true if last action succeeded otherwise false
488 */
489 871180 inline bool Successful() const {
490 1196296 return SQLITE_OK == last_error_code_ ||
491
4/4
✓ Branch 0 taken 325116 times.
✓ Branch 1 taken 546064 times.
✓ Branch 2 taken 324928 times.
✓ Branch 3 taken 188 times.
1196108 SQLITE_ROW == last_error_code_ ||
492
2/2
✓ Branch 0 taken 324908 times.
✓ Branch 1 taken 20 times.
1196108 SQLITE_DONE == last_error_code_;
493 }
494
495 private:
496 bool Init(const char *statement);
497 537936 void LazyInit() {
498
2/2
✓ Branch 1 taken 1002 times.
✓ Branch 2 taken 536934 times.
537936 if (!IsInitialized()) {
499
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1002 times.
1002 assert(NULL != database_);
500
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1002 times.
1002 assert(NULL != query_string_);
501 1002 const bool success = Init(query_string_);
502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1002 times.
1002 assert(success);
503 }
504 537936 }
505
506 sqlite3 *database_;
507 sqlite3_stmt *statement_;
508 const char *query_string_;
509 int last_error_code_;
510 };
511
512 } // namespace sqlite
513
514 #include "sql_impl.h"
515
516 #endif // CVMFS_SQL_H_
517