| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/swissknife_ingest_gc.h |
| Date: | 2026-05-17 02:35:27 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 37 | 72 | 51.4% |
| Branches: | 34 | 133 | 25.6% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * This file is part of the CernVM File System. | ||
| 3 | * | ||
| 4 | * GC database helper functions for cvmfs_swissknife ingest. | ||
| 5 | * These read/update the SQLite database produced by cvmfs_ducc gc. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef CVMFS_SWISSKNIFE_INGEST_GC_H_ | ||
| 9 | #define CVMFS_SWISSKNIFE_INGEST_GC_H_ | ||
| 10 | |||
| 11 | #include <stdint.h> | ||
| 12 | |||
| 13 | #include <string> | ||
| 14 | #include <vector> | ||
| 15 | |||
| 16 | #include "duplex_sqlite3.h" | ||
| 17 | #include "util/logging.h" | ||
| 18 | #include "util/string.h" | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Read pending (not yet deleted) paths from a GC SQLite database. | ||
| 22 | * | ||
| 23 | * If batch_size > 0, at most batch_size rows are returned (in id order). | ||
| 24 | * If ids is non-NULL, the ids of the returned rows are written to it in the | ||
| 25 | * same order as paths. | ||
| 26 | * Returns true on success. | ||
| 27 | */ | ||
| 28 | 126 | inline bool ReadGCDatabase(const std::string &db_path, | |
| 29 | std::vector<std::string> *paths, | ||
| 30 | std::vector<int64_t> *ids = NULL, | ||
| 31 | int batch_size = 0) { | ||
| 32 | 126 | sqlite3 *db = NULL; | |
| 33 |
1/2✓ Branch 2 taken 126 times.
✗ Branch 3 not taken.
|
126 | int rc = sqlite3_open_v2(db_path.c_str(), &db, SQLITE_OPEN_READONLY, NULL); |
| 34 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 105 times.
|
126 | if (rc != SQLITE_OK) { |
| 35 |
2/4✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
|
21 | LogCvmfs(kLogCvmfs, kLogStderr, "Cannot open GC database %s: %s", |
| 36 | db_path.c_str(), sqlite3_errmsg(db)); | ||
| 37 |
2/4✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
|
21 | if (db) sqlite3_close(db); |
| 38 | 21 | return false; | |
| 39 | } | ||
| 40 | |||
| 41 | std::string sql = "SELECT id, path FROM gc_paths WHERE deleted = 0 " | ||
| 42 |
1/2✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
|
105 | "ORDER BY id"; |
| 43 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
|
105 | if (batch_size > 0) { |
| 44 | ✗ | sql += " LIMIT " + StringifyInt(batch_size); | |
| 45 | } | ||
| 46 | 105 | sqlite3_stmt *stmt = NULL; | |
| 47 |
1/2✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
|
105 | rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, NULL); |
| 48 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
|
105 | if (rc != SQLITE_OK) { |
| 49 | ✗ | LogCvmfs(kLogCvmfs, kLogStderr, | |
| 50 | "Cannot prepare GC query on %s: %s", | ||
| 51 | db_path.c_str(), sqlite3_errmsg(db)); | ||
| 52 | ✗ | sqlite3_close(db); | |
| 53 | ✗ | return false; | |
| 54 | } | ||
| 55 | |||
| 56 |
3/4✓ Branch 1 taken 105252 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 105147 times.
✓ Branch 4 taken 105 times.
|
105252 | while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { |
| 57 |
1/2✓ Branch 1 taken 105147 times.
✗ Branch 2 not taken.
|
105147 | const int64_t id = sqlite3_column_int64(stmt, 0); |
| 58 | const char *path = | ||
| 59 |
1/2✓ Branch 1 taken 105147 times.
✗ Branch 2 not taken.
|
105147 | reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1)); |
| 60 |
1/2✓ Branch 0 taken 105147 times.
✗ Branch 1 not taken.
|
105147 | if (path) { |
| 61 |
2/4✓ Branch 2 taken 105147 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 105147 times.
✗ Branch 6 not taken.
|
105147 | paths->push_back(std::string(path)); |
| 62 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 105147 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
105147 | if (ids != NULL) ids->push_back(id); |
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 |
1/2✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
|
105 | sqlite3_finalize(stmt); |
| 67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
|
105 | if (rc != SQLITE_DONE) { |
| 68 | ✗ | LogCvmfs(kLogCvmfs, kLogStderr, | |
| 69 | "Error reading GC database %s: %s", | ||
| 70 | db_path.c_str(), sqlite3_errmsg(db)); | ||
| 71 | ✗ | sqlite3_close(db); | |
| 72 | ✗ | return false; | |
| 73 | } | ||
| 74 | |||
| 75 |
1/2✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
|
105 | sqlite3_close(db); |
| 76 | 105 | return true; | |
| 77 | 105 | } | |
| 78 | |||
| 79 | |||
| 80 | /** | ||
| 81 | * Mark paths as deleted in the GC SQLite database. | ||
| 82 | * | ||
| 83 | * If ids is non-empty, only the rows with the given ids are marked. | ||
| 84 | * If ids is empty, all currently-pending rows are marked (legacy behaviour). | ||
| 85 | * Returns true on success. | ||
| 86 | */ | ||
| 87 | 105 | inline bool MarkGCPathsDeleted( | |
| 88 | const std::string &db_path, | ||
| 89 | const std::vector<int64_t> &ids = std::vector<int64_t>()) { | ||
| 90 | 105 | sqlite3 *db = NULL; | |
| 91 |
1/2✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
|
105 | int rc = sqlite3_open_v2(db_path.c_str(), &db, SQLITE_OPEN_READWRITE, NULL); |
| 92 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 84 times.
|
105 | if (rc != SQLITE_OK) { |
| 93 |
2/4✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
|
21 | LogCvmfs(kLogCvmfs, kLogStderr, |
| 94 | "Cannot open GC database for update %s: %s", | ||
| 95 | db_path.c_str(), sqlite3_errmsg(db)); | ||
| 96 |
2/4✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
|
21 | if (db) sqlite3_close(db); |
| 97 | 21 | return false; | |
| 98 | } | ||
| 99 | |||
| 100 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | if (ids.empty()) { |
| 101 | 84 | const char *sql = "UPDATE gc_paths SET deleted = 1 WHERE deleted = 0"; | |
| 102 | 84 | char *err_msg = NULL; | |
| 103 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | rc = sqlite3_exec(db, sql, NULL, NULL, &err_msg); |
| 104 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
|
84 | if (rc != SQLITE_OK) { |
| 105 | ✗ | LogCvmfs(kLogCvmfs, kLogStderr, | |
| 106 | "Cannot mark GC paths as deleted in %s: %s", | ||
| 107 | ✗ | db_path.c_str(), err_msg ? err_msg : "unknown error"); | |
| 108 | ✗ | sqlite3_free(err_msg); | |
| 109 | ✗ | sqlite3_close(db); | |
| 110 | ✗ | return false; | |
| 111 | } | ||
| 112 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | sqlite3_close(db); |
| 113 | 84 | return true; | |
| 114 | } | ||
| 115 | |||
| 116 | ✗ | const char *sql = "UPDATE gc_paths SET deleted = 1 WHERE id = ?"; | |
| 117 | ✗ | sqlite3_stmt *stmt = NULL; | |
| 118 | ✗ | rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); | |
| 119 | ✗ | if (rc != SQLITE_OK) { | |
| 120 | ✗ | LogCvmfs(kLogCvmfs, kLogStderr, | |
| 121 | "Cannot prepare GC update on %s: %s", | ||
| 122 | db_path.c_str(), sqlite3_errmsg(db)); | ||
| 123 | ✗ | sqlite3_close(db); | |
| 124 | ✗ | return false; | |
| 125 | } | ||
| 126 | |||
| 127 | ✗ | sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL); | |
| 128 | ✗ | for (size_t i = 0; i < ids.size(); ++i) { | |
| 129 | ✗ | sqlite3_bind_int64(stmt, 1, ids[i]); | |
| 130 | ✗ | rc = sqlite3_step(stmt); | |
| 131 | ✗ | if (rc != SQLITE_DONE) { | |
| 132 | ✗ | LogCvmfs(kLogCvmfs, kLogStderr, | |
| 133 | "Cannot mark GC path id %ld as deleted in %s: %s", | ||
| 134 | ✗ | static_cast<long>(ids[i]), db_path.c_str(), // NOLINT | |
| 135 | sqlite3_errmsg(db)); | ||
| 136 | ✗ | sqlite3_finalize(stmt); | |
| 137 | ✗ | sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL); | |
| 138 | ✗ | sqlite3_close(db); | |
| 139 | ✗ | return false; | |
| 140 | } | ||
| 141 | ✗ | sqlite3_reset(stmt); | |
| 142 | } | ||
| 143 | ✗ | sqlite3_exec(db, "COMMIT", NULL, NULL, NULL); | |
| 144 | ✗ | sqlite3_finalize(stmt); | |
| 145 | ✗ | sqlite3_close(db); | |
| 146 | ✗ | return true; | |
| 147 | } | ||
| 148 | |||
| 149 | #endif // CVMFS_SWISSKNIFE_INGEST_GC_H_ | ||
| 150 |