GCC Code Coverage Report


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