| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/history_sqlite.cc |
| Date: | 2026-09-20 02:39:58 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 244 | 267 | 91.4% |
| Branches: | 187 | 346 | 54.0% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * This file is part of the CernVM File System. | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include "history_sqlite.h" | ||
| 6 | |||
| 7 | using namespace std; // NOLINT | ||
| 8 | |||
| 9 | namespace history { | ||
| 10 | |||
| 11 | const std::string SqliteHistory::kPreviousRevisionKey = "previous_revision"; | ||
| 12 | |||
| 13 | |||
| 14 | 915 | SqliteHistory *SqliteHistory::Open(const std::string &file_name) { | |
| 15 | 915 | const bool read_write = false; | |
| 16 | 915 | return Open(file_name, read_write); | |
| 17 | } | ||
| 18 | |||
| 19 | |||
| 20 | 328 | SqliteHistory *SqliteHistory::OpenWritable(const std::string &file_name) { | |
| 21 | 328 | const bool read_write = true; | |
| 22 | 328 | return Open(file_name, read_write); | |
| 23 | } | ||
| 24 | |||
| 25 | |||
| 26 | 1243 | SqliteHistory *SqliteHistory::Open(const std::string &file_name, | |
| 27 | const bool read_write) { | ||
| 28 | 1243 | SqliteHistory *history = new SqliteHistory(); | |
| 29 |
3/6✓ Branch 0 taken 1243 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1243 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1243 times.
|
1243 | if (NULL == history || !history->OpenDatabase(file_name, read_write)) { |
| 30 | ✗ | delete history; | |
| 31 | ✗ | return NULL; | |
| 32 | } | ||
| 33 | |||
| 34 | 2486 | LogCvmfs(kLogHistory, kLogDebug, | |
| 35 | "opened history database '%s' for repository '%s' %s", | ||
| 36 | 1243 | file_name.c_str(), history->fqrn().c_str(), | |
| 37 |
2/2✓ Branch 1 taken 328 times.
✓ Branch 2 taken 915 times.
|
1243 | ((history->IsWritable()) ? "(writable)" : "")); |
| 38 | |||
| 39 | 1243 | return history; | |
| 40 | } | ||
| 41 | |||
| 42 | |||
| 43 | 3659 | SqliteHistory *SqliteHistory::Create(const std::string &file_name, | |
| 44 | const std::string &fqrn) { | ||
| 45 | 3659 | SqliteHistory *history = new SqliteHistory(); | |
| 46 |
3/6✓ Branch 0 taken 3659 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3659 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3659 times.
|
3659 | if (NULL == history || !history->CreateDatabase(file_name, fqrn)) { |
| 47 | ✗ | delete history; | |
| 48 | ✗ | return NULL; | |
| 49 | } | ||
| 50 | |||
| 51 | 3659 | LogCvmfs(kLogHistory, kLogDebug, | |
| 52 | "created empty history database '%s' for" | ||
| 53 | "repository '%s'", | ||
| 54 | file_name.c_str(), fqrn.c_str()); | ||
| 55 | 3659 | return history; | |
| 56 | } | ||
| 57 | |||
| 58 | |||
| 59 | 1243 | bool SqliteHistory::OpenDatabase(const std::string &file_name, | |
| 60 | const bool read_write) { | ||
| 61 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1243 times.
|
1243 | assert(database_.get() == nullptr); |
| 62 | 1243 | const HistoryDatabase::OpenMode mode = (read_write) | |
| 63 |
2/2✓ Branch 0 taken 328 times.
✓ Branch 1 taken 915 times.
|
1243 | ? HistoryDatabase::kOpenReadWrite |
| 64 | : HistoryDatabase::kOpenReadOnly; | ||
| 65 | 2486 | database_ = std::unique_ptr<HistoryDatabase>( | |
| 66 | 1243 | HistoryDatabase::Open(file_name, mode)); | |
| 67 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1243 times.
|
1243 | if (database_.get() == nullptr) { |
| 68 | ✗ | return false; | |
| 69 | } | ||
| 70 | |||
| 71 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 1243 times.
|
1243 | if (!database_->HasProperty(HistoryDatabase::kFqrnKey)) { |
| 72 | ✗ | LogCvmfs(kLogHistory, kLogDebug, | |
| 73 | "opened history database does not provide " | ||
| 74 | "an FQRN under '%s'", | ||
| 75 | HistoryDatabase::kFqrnKey.c_str()); | ||
| 76 | ✗ | return false; | |
| 77 | } | ||
| 78 | |||
| 79 |
1/2✓ Branch 3 taken 1243 times.
✗ Branch 4 not taken.
|
1243 | set_fqrn(database_->GetProperty<std::string>(HistoryDatabase::kFqrnKey)); |
| 80 | 1243 | PrepareQueries(); | |
| 81 | 1243 | return true; | |
| 82 | } | ||
| 83 | |||
| 84 | |||
| 85 | 3659 | bool SqliteHistory::CreateDatabase(const std::string &file_name, | |
| 86 | const std::string &repo_name) { | ||
| 87 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3659 times.
|
3659 | assert(database_.get() == nullptr); |
| 88 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 3659 times.
|
3659 | assert(fqrn().empty()); |
| 89 | 3659 | set_fqrn(repo_name); | |
| 90 | 7318 | database_ = std::unique_ptr<HistoryDatabase>( | |
| 91 | 3659 | HistoryDatabase::Create(file_name)); | |
| 92 | 3659 | if (database_.get() == nullptr | |
| 93 |
3/6✓ Branch 0 taken 3659 times.
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3659 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3659 times.
|
3659 | || !database_->InsertInitialValues(repo_name)) { |
| 94 | ✗ | LogCvmfs(kLogHistory, kLogDebug, | |
| 95 | "failed to initialize empty database '%s', for repository '%s'", | ||
| 96 | file_name.c_str(), repo_name.c_str()); | ||
| 97 | ✗ | return false; | |
| 98 | } | ||
| 99 | |||
| 100 | 3659 | PrepareQueries(); | |
| 101 | 3659 | return true; | |
| 102 | } | ||
| 103 | |||
| 104 | |||
| 105 | 4902 | void SqliteHistory::PrepareQueries() { | |
| 106 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4902 times.
|
4902 | assert(database_.get() != nullptr); |
| 107 | |||
| 108 |
1/2✓ Branch 3 taken 4902 times.
✗ Branch 4 not taken.
|
4902 | find_tag_ = std::unique_ptr<SqlFindTag>(new SqlFindTag(database_.get())); |
| 109 | 9804 | find_tag_by_date_ = std::unique_ptr<SqlFindTagByDate>( | |
| 110 |
1/2✓ Branch 3 taken 4902 times.
✗ Branch 4 not taken.
|
9804 | new SqlFindTagByDate(database_.get())); |
| 111 | 9804 | count_tags_ = std::unique_ptr<SqlCountTags>( | |
| 112 |
1/2✓ Branch 3 taken 4902 times.
✗ Branch 4 not taken.
|
9804 | new SqlCountTags(database_.get())); |
| 113 |
1/2✓ Branch 3 taken 4902 times.
✗ Branch 4 not taken.
|
4902 | list_tags_ = std::unique_ptr<SqlListTags>(new SqlListTags(database_.get())); |
| 114 | 9804 | get_hashes_ = std::unique_ptr<SqlGetHashes>( | |
| 115 |
1/2✓ Branch 3 taken 4902 times.
✗ Branch 4 not taken.
|
9804 | new SqlGetHashes(database_.get())); |
| 116 | 9804 | list_rollback_tags_ = std::unique_ptr<SqlListRollbackTags>( | |
| 117 |
1/2✓ Branch 3 taken 4902 times.
✗ Branch 4 not taken.
|
9804 | new SqlListRollbackTags(database_.get())); |
| 118 | 9804 | list_branches_ = std::unique_ptr<SqlListBranches>( | |
| 119 |
1/2✓ Branch 3 taken 4902 times.
✗ Branch 4 not taken.
|
9804 | new SqlListBranches(database_.get())); |
| 120 | |||
| 121 |
2/2✓ Branch 2 taken 4820 times.
✓ Branch 3 taken 82 times.
|
4902 | if (database_->ContainsRecycleBin()) { |
| 122 | 9640 | recycle_list_ = std::unique_ptr<SqlRecycleBinList>( | |
| 123 |
1/2✓ Branch 3 taken 4820 times.
✗ Branch 4 not taken.
|
9640 | new SqlRecycleBinList(database_.get())); |
| 124 | } | ||
| 125 | |||
| 126 |
2/2✓ Branch 1 taken 3987 times.
✓ Branch 2 taken 915 times.
|
4902 | if (IsWritable()) { |
| 127 | 7974 | insert_tag_ = std::unique_ptr<SqlInsertTag>( | |
| 128 |
1/2✓ Branch 3 taken 3987 times.
✗ Branch 4 not taken.
|
7974 | new SqlInsertTag(database_.get())); |
| 129 | 7974 | remove_tag_ = std::unique_ptr<SqlRemoveTag>( | |
| 130 |
1/2✓ Branch 3 taken 3987 times.
✗ Branch 4 not taken.
|
7974 | new SqlRemoveTag(database_.get())); |
| 131 | 7974 | rollback_tag_ = std::unique_ptr<SqlRollbackTag>( | |
| 132 |
1/2✓ Branch 3 taken 3987 times.
✗ Branch 4 not taken.
|
7974 | new SqlRollbackTag(database_.get())); |
| 133 | 7974 | recycle_empty_ = std::unique_ptr<SqlRecycleBinFlush>( | |
| 134 |
1/2✓ Branch 3 taken 3987 times.
✗ Branch 4 not taken.
|
7974 | new SqlRecycleBinFlush(database_.get())); |
| 135 | 7974 | insert_branch_ = std::unique_ptr<SqlInsertBranch>( | |
| 136 |
1/2✓ Branch 3 taken 3987 times.
✗ Branch 4 not taken.
|
7974 | new SqlInsertBranch(database_.get())); |
| 137 | 7974 | find_branch_head_ = std::unique_ptr<SqlFindBranchHead>( | |
| 138 |
1/2✓ Branch 3 taken 3987 times.
✗ Branch 4 not taken.
|
7974 | new SqlFindBranchHead(database_.get())); |
| 139 | } | ||
| 140 | 4902 | } | |
| 141 | |||
| 142 | |||
| 143 | 902 | bool SqliteHistory::BeginTransaction() const { | |
| 144 | 902 | return database_->BeginTransaction(); | |
| 145 | } | ||
| 146 | |||
| 147 | |||
| 148 | 738 | bool SqliteHistory::CommitTransaction() const { | |
| 149 | 738 | return database_->CommitTransaction(); | |
| 150 | } | ||
| 151 | |||
| 152 | |||
| 153 | 1760 | bool SqliteHistory::SetPreviousRevision(const shash::Any &history_hash) { | |
| 154 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1760 times.
|
1760 | assert(database_.get() != nullptr); |
| 155 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1760 times.
|
1760 | assert(IsWritable()); |
| 156 |
1/2✓ Branch 3 taken 1760 times.
✗ Branch 4 not taken.
|
1760 | return database_->SetProperty(kPreviousRevisionKey, history_hash.ToString()); |
| 157 | } | ||
| 158 | |||
| 159 | |||
| 160 | 264 | shash::Any SqliteHistory::previous_revision() const { | |
| 161 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 264 times.
|
264 | assert(database_.get() != nullptr); |
| 162 | 264 | const std::string hash_str = database_->GetProperty<std::string>( | |
| 163 |
1/2✓ Branch 1 taken 264 times.
✗ Branch 2 not taken.
|
264 | kPreviousRevisionKey); |
| 164 |
1/2✓ Branch 2 taken 264 times.
✗ Branch 3 not taken.
|
528 | return shash::MkFromHexPtr(shash::HexPtr(hash_str), shash::kSuffixHistory); |
| 165 | 264 | } | |
| 166 | |||
| 167 | |||
| 168 | 8192 | bool SqliteHistory::IsWritable() const { | |
| 169 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 8192 times.
|
8192 | assert(database_.get() != nullptr); |
| 170 | 8192 | return database_->read_write(); | |
| 171 | } | ||
| 172 | |||
| 173 | 1189 | unsigned SqliteHistory::GetNumberOfTags() const { | |
| 174 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1189 times.
|
1189 | assert(database_.get() != nullptr); |
| 175 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1189 times.
|
1189 | assert(count_tags_.get() != nullptr); |
| 176 | 1189 | bool retval = count_tags_->FetchRow(); | |
| 177 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1189 times.
|
1189 | assert(retval); |
| 178 | 1189 | const unsigned count = count_tags_->RetrieveCount(); | |
| 179 | 1189 | retval = count_tags_->Reset(); | |
| 180 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1189 times.
|
1189 | assert(retval); |
| 181 | 1189 | return count; | |
| 182 | } | ||
| 183 | |||
| 184 | |||
| 185 | 174386 | bool SqliteHistory::Insert(const History::Tag &tag) { | |
| 186 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 174386 times.
|
174386 | assert(database_.get() != nullptr); |
| 187 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 174386 times.
|
174386 | assert(insert_tag_.get() != nullptr); |
| 188 | |||
| 189 |
2/2✓ Branch 4 taken 174304 times.
✓ Branch 5 taken 82 times.
|
348772 | return insert_tag_->BindTag(tag) && insert_tag_->Execute() |
| 190 |
2/4✓ Branch 0 taken 174386 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 174304 times.
✗ Branch 5 not taken.
|
348772 | && insert_tag_->Reset(); |
| 191 | } | ||
| 192 | |||
| 193 | |||
| 194 | 533 | bool SqliteHistory::Remove(const std::string &name) { | |
| 195 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 533 times.
|
533 | assert(database_.get() != nullptr); |
| 196 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 533 times.
|
533 | assert(remove_tag_.get() != nullptr); |
| 197 | |||
| 198 |
1/2✓ Branch 1 taken 533 times.
✗ Branch 2 not taken.
|
533 | Tag condemned_tag; |
| 199 |
3/4✓ Branch 1 taken 533 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 41 times.
✓ Branch 4 taken 492 times.
|
533 | if (!GetByName(name, &condemned_tag)) { |
| 200 | 41 | return true; | |
| 201 | } | ||
| 202 | |||
| 203 |
3/8✓ Branch 2 taken 492 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 492 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 492 times.
✗ Branch 9 not taken.
|
984 | return remove_tag_->BindName(name) && remove_tag_->Execute() |
| 204 |
3/6✓ Branch 0 taken 492 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 492 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 492 times.
✗ Branch 7 not taken.
|
984 | && remove_tag_->Reset(); |
| 205 | 533 | } | |
| 206 | |||
| 207 | |||
| 208 | 943 | bool SqliteHistory::Exists(const std::string &name) const { | |
| 209 |
1/2✓ Branch 1 taken 943 times.
✗ Branch 2 not taken.
|
943 | Tag existing_tag; |
| 210 |
1/2✓ Branch 1 taken 943 times.
✗ Branch 2 not taken.
|
1886 | return GetByName(name, &existing_tag); |
| 211 | 943 | } | |
| 212 | |||
| 213 | |||
| 214 | 2472 | bool SqliteHistory::GetByName(const std::string &name, Tag *tag) const { | |
| 215 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2472 times.
|
2472 | assert(database_.get() != nullptr); |
| 216 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2472 times.
|
2472 | assert(find_tag_.get() != nullptr); |
| 217 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2472 times.
|
2472 | assert(NULL != tag); |
| 218 | |||
| 219 |
5/6✓ Branch 2 taken 2472 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 539 times.
✓ Branch 7 taken 1933 times.
✓ Branch 8 taken 539 times.
✓ Branch 9 taken 1933 times.
|
2472 | if (!find_tag_->BindName(name) || !find_tag_->FetchRow()) { |
| 220 | 539 | find_tag_->Reset(); | |
| 221 | 539 | return false; | |
| 222 | } | ||
| 223 | |||
| 224 | 1933 | *tag = find_tag_->RetrieveTag(); | |
| 225 | 1933 | return find_tag_->Reset(); | |
| 226 | } | ||
| 227 | |||
| 228 | |||
| 229 | 258 | bool SqliteHistory::GetByDate(const time_t timestamp, Tag *tag) const { | |
| 230 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 258 times.
|
258 | assert(database_.get() != nullptr); |
| 231 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 258 times.
|
258 | assert(find_tag_by_date_.get() != nullptr); |
| 232 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 258 times.
|
258 | assert(NULL != tag); |
| 233 | |||
| 234 | 258 | if (!find_tag_by_date_->BindTimestamp(timestamp) | |
| 235 |
5/6✓ Branch 0 taken 258 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 88 times.
✓ Branch 5 taken 170 times.
✓ Branch 6 taken 88 times.
✓ Branch 7 taken 170 times.
|
258 | || !find_tag_by_date_->FetchRow()) { |
| 236 | 88 | find_tag_by_date_->Reset(); | |
| 237 | 88 | return false; | |
| 238 | } | ||
| 239 | |||
| 240 | 170 | *tag = find_tag_by_date_->RetrieveTag(); | |
| 241 | 170 | return find_tag_by_date_->Reset(); | |
| 242 | } | ||
| 243 | |||
| 244 | |||
| 245 | 328 | bool SqliteHistory::List(std::vector<Tag> *tags) const { | |
| 246 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 328 times.
|
328 | assert(list_tags_.get() != nullptr); |
| 247 | 328 | return RunListing(tags, list_tags_.get()); | |
| 248 | } | ||
| 249 | |||
| 250 | |||
| 251 | template<class SqlListingT> | ||
| 252 | 902 | bool SqliteHistory::RunListing(std::vector<Tag> *list, SqlListingT *sql) const { | |
| 253 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 451 times.
|
902 | assert(database_.get() != nullptr); |
| 254 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 451 times.
|
902 | assert(NULL != list); |
| 255 | |||
| 256 |
2/2✓ Branch 1 taken 48011 times.
✓ Branch 2 taken 451 times.
|
96924 | while (sql->FetchRow()) { |
| 257 |
1/2✓ Branch 2 taken 48011 times.
✗ Branch 3 not taken.
|
96022 | list->push_back(sql->RetrieveTag()); |
| 258 | } | ||
| 259 | |||
| 260 | 902 | return sql->Reset(); | |
| 261 | } | ||
| 262 | |||
| 263 | |||
| 264 | 123 | bool SqliteHistory::GetBranchHead(const string &branch_name, Tag *tag) const { | |
| 265 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 123 times.
|
123 | assert(database_.get() != nullptr); |
| 266 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 123 times.
|
123 | assert(find_branch_head_.get() != nullptr); |
| 267 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
|
123 | assert(tag != NULL); |
| 268 | |||
| 269 | 123 | if (!find_branch_head_->BindBranchName(branch_name) | |
| 270 |
5/6✓ Branch 0 taken 123 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 41 times.
✓ Branch 5 taken 82 times.
✓ Branch 6 taken 41 times.
✓ Branch 7 taken 82 times.
|
123 | || !find_branch_head_->FetchRow()) { |
| 271 | 41 | find_branch_head_->Reset(); | |
| 272 | 41 | return false; | |
| 273 | } | ||
| 274 | |||
| 275 | 82 | *tag = find_branch_head_->RetrieveTag(); | |
| 276 | 82 | return find_branch_head_->Reset(); | |
| 277 | } | ||
| 278 | |||
| 279 | |||
| 280 | 123 | bool SqliteHistory::ExistsBranch(const string &branch_name) const { | |
| 281 | 123 | vector<Branch> branches; | |
| 282 |
2/4✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 123 times.
|
123 | if (!ListBranches(&branches)) |
| 283 | ✗ | return false; | |
| 284 |
2/2✓ Branch 1 taken 287 times.
✓ Branch 2 taken 41 times.
|
328 | for (unsigned i = 0; i < branches.size(); ++i) { |
| 285 |
2/2✓ Branch 2 taken 82 times.
✓ Branch 3 taken 205 times.
|
287 | if (branches[i].branch == branch_name) |
| 286 | 82 | return true; | |
| 287 | } | ||
| 288 | 41 | return false; | |
| 289 | 123 | } | |
| 290 | |||
| 291 | |||
| 292 | 738 | bool SqliteHistory::InsertBranch(const Branch &branch) { | |
| 293 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 738 times.
|
738 | assert(database_.get() != nullptr); |
| 294 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 738 times.
|
738 | assert(insert_branch_.get() != nullptr); |
| 295 | |||
| 296 |
2/2✓ Branch 4 taken 656 times.
✓ Branch 5 taken 41 times.
|
1435 | return insert_branch_->BindBranch(branch) && insert_branch_->Execute() |
| 297 |
3/4✓ Branch 0 taken 697 times.
✓ Branch 1 taken 41 times.
✓ Branch 4 taken 656 times.
✗ Branch 5 not taken.
|
1435 | && insert_branch_->Reset(); |
| 298 | } | ||
| 299 | |||
| 300 | |||
| 301 | 41 | bool SqliteHistory::PruneBranches() { | |
| 302 | // Parent pointers might point to abandoned branches. Redirect them to the | ||
| 303 | // parent of the abandoned branch. This has to be repeated until the fix | ||
| 304 | // point is reached. It always works because we never delete the root branch | ||
| 305 | sqlite::Sql sql_fix_parent_pointers( | ||
| 306 | 41 | database_->sqlite_db(), | |
| 307 | "INSERT OR REPLACE INTO branches (branch, parent, initial_revision) " | ||
| 308 | "SELECT branches.branch, abandoned_parent, branches.initial_revision " | ||
| 309 | " FROM branches " | ||
| 310 | " INNER JOIN (SELECT DISTINCT branches.branch AS abandoned_branch, " | ||
| 311 | " branches.parent AS abandoned_parent FROM branches " | ||
| 312 | " LEFT OUTER JOIN tags ON (branches.branch=tags.branch)" | ||
| 313 | " WHERE tags.branch IS NULL) " | ||
| 314 |
3/6✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 41 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 41 times.
✗ Branch 9 not taken.
|
123 | " ON (branches.parent=abandoned_branch);"); |
| 315 | // Detect if fix point is reached | ||
| 316 | sqlite::Sql sql_remaining_rows( | ||
| 317 | 41 | database_->sqlite_db(), | |
| 318 | "SELECT count(*) FROM branches " | ||
| 319 | "INNER JOIN " | ||
| 320 | " (SELECT DISTINCT branches.branch AS abandoned_branch FROM branches " | ||
| 321 | " LEFT OUTER JOIN tags ON (branches.branch=tags.branch) " | ||
| 322 | " WHERE tags.branch IS NULL) " | ||
| 323 |
3/6✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 41 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 41 times.
✗ Branch 9 not taken.
|
123 | "ON (branches.parent=abandoned_branch);"); |
| 324 | |||
| 325 | bool retval; | ||
| 326 | do { | ||
| 327 |
1/2✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
|
123 | retval = sql_remaining_rows.FetchRow(); |
| 328 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
|
123 | if (!retval) |
| 329 | ✗ | return false; | |
| 330 |
1/2✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
|
123 | const int64_t count = sql_remaining_rows.RetrieveInt64(0); |
| 331 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
|
123 | assert(count >= 0); |
| 332 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 82 times.
|
123 | if (count == 0) |
| 333 | 41 | break; | |
| 334 |
1/2✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
|
82 | retval = sql_remaining_rows.Reset(); |
| 335 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | assert(retval); |
| 336 | |||
| 337 |
1/2✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
|
82 | retval = sql_fix_parent_pointers.Execute(); |
| 338 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | if (!retval) |
| 339 | ✗ | return false; | |
| 340 |
1/2✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
|
82 | retval = sql_fix_parent_pointers.Reset(); |
| 341 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | assert(retval); |
| 342 | 82 | } while (true); | |
| 343 | |||
| 344 | sqlite::Sql sql_remove_branches( | ||
| 345 | 41 | database_->sqlite_db(), | |
| 346 | "DELETE FROM branches " | ||
| 347 |
3/6✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 41 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 41 times.
✗ Branch 9 not taken.
|
123 | "WHERE branch NOT IN (SELECT DISTINCT branch FROM tags);"); |
| 348 |
1/2✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
|
41 | retval = sql_remove_branches.Execute(); |
| 349 | 41 | return retval; | |
| 350 | 41 | } | |
| 351 | |||
| 352 | |||
| 353 | 410 | bool SqliteHistory::ListBranches(vector<Branch> *branches) const { | |
| 354 |
2/2✓ Branch 2 taken 1107 times.
✓ Branch 3 taken 410 times.
|
1517 | while (list_branches_->FetchRow()) { |
| 355 |
1/2✓ Branch 3 taken 1107 times.
✗ Branch 4 not taken.
|
1107 | branches->push_back(list_branches_->RetrieveBranch()); |
| 356 | } | ||
| 357 | |||
| 358 | 410 | return list_branches_->Reset(); | |
| 359 | } | ||
| 360 | |||
| 361 | |||
| 362 | 533 | bool SqliteHistory::ListRecycleBin(std::vector<shash::Any> *hashes) const { | |
| 363 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 533 times.
|
533 | assert(database_.get() != nullptr); |
| 364 | |||
| 365 |
2/2✓ Branch 2 taken 82 times.
✓ Branch 3 taken 451 times.
|
533 | if (!database_->ContainsRecycleBin()) { |
| 366 | 82 | return false; | |
| 367 | } | ||
| 368 | |||
| 369 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 451 times.
|
451 | assert(NULL != hashes); |
| 370 | 451 | hashes->clear(); | |
| 371 |
2/2✓ Branch 2 taken 41 times.
✓ Branch 3 taken 451 times.
|
492 | while (recycle_list_->FetchRow()) { |
| 372 |
1/2✓ Branch 3 taken 41 times.
✗ Branch 4 not taken.
|
41 | hashes->push_back(recycle_list_->RetrieveHash()); |
| 373 | } | ||
| 374 | |||
| 375 | 451 | return recycle_list_->Reset(); | |
| 376 | } | ||
| 377 | |||
| 378 | |||
| 379 | 164 | bool SqliteHistory::EmptyRecycleBin() { | |
| 380 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 164 times.
|
164 | assert(database_.get() != nullptr); |
| 381 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 164 times.
|
164 | assert(IsWritable()); |
| 382 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 164 times.
|
164 | assert(recycle_empty_.get() != nullptr); |
| 383 |
2/4✓ Branch 2 taken 164 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 164 times.
✗ Branch 7 not taken.
|
164 | return recycle_empty_->Execute() && recycle_empty_->Reset(); |
| 384 | } | ||
| 385 | |||
| 386 | |||
| 387 | 123 | bool SqliteHistory::Rollback(const Tag &updated_target_tag) { | |
| 388 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 123 times.
|
123 | assert(database_.get() != nullptr); |
| 389 |
2/4✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 123 times.
|
123 | assert(IsWritable()); |
| 390 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 123 times.
|
123 | assert(rollback_tag_.get() != nullptr); |
| 391 | |||
| 392 |
1/2✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
|
123 | Tag old_target_tag; |
| 393 | 123 | bool success = false; | |
| 394 | |||
| 395 | // open a transaction (if non open yet) | ||
| 396 |
1/2✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
|
123 | const bool need_to_commit = BeginTransaction(); |
| 397 | |||
| 398 | // retrieve the old version of the target tag from the history | ||
| 399 |
1/2✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
|
123 | success = GetByName(updated_target_tag.name, &old_target_tag); |
| 400 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 82 times.
|
123 | if (!success) { |
| 401 |
1/2✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
|
41 | LogCvmfs(kLogHistory, kLogDebug, "failed to retrieve old target tag '%s'", |
| 402 | updated_target_tag.name.c_str()); | ||
| 403 | 41 | return false; | |
| 404 | } | ||
| 405 | |||
| 406 | // sanity checks | ||
| 407 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 82 times.
|
82 | assert(old_target_tag.description == updated_target_tag.description); |
| 408 | |||
| 409 | // rollback the history to the target tag | ||
| 410 | // (essentially removing all intermediate tags + the old target tag) | ||
| 411 |
1/2✓ Branch 2 taken 82 times.
✗ Branch 3 not taken.
|
82 | success = rollback_tag_->BindTargetTag(old_target_tag) |
| 412 |
5/12✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 82 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 82 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 82 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 82 times.
✗ Branch 13 not taken.
|
82 | && rollback_tag_->Execute() && rollback_tag_->Reset(); |
| 413 |
4/8✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 82 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 82 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 82 times.
|
82 | if (!success || Exists(old_target_tag.name)) { |
| 414 | ✗ | LogCvmfs(kLogHistory, kLogDebug, | |
| 415 | "failed to remove intermediate tags " | ||
| 416 | "until '%s' - '%" PRIu64 "'", | ||
| 417 | old_target_tag.name.c_str(), old_target_tag.revision); | ||
| 418 | ✗ | return false; | |
| 419 | } | ||
| 420 | |||
| 421 | // insert the provided updated target tag into the history concluding the | ||
| 422 | // rollback operation | ||
| 423 |
1/2✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
|
82 | success = Insert(updated_target_tag); |
| 424 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | if (!success) { |
| 425 | ✗ | LogCvmfs(kLogHistory, kLogDebug, "failed to insert updated target tag '%s'", | |
| 426 | updated_target_tag.name.c_str()); | ||
| 427 | ✗ | return false; | |
| 428 | } | ||
| 429 | |||
| 430 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | if (need_to_commit) { |
| 431 | ✗ | success = CommitTransaction(); | |
| 432 | ✗ | assert(success); | |
| 433 | } | ||
| 434 | |||
| 435 | 82 | return true; | |
| 436 | 123 | } | |
| 437 | |||
| 438 | |||
| 439 | 164 | bool SqliteHistory::ListTagsAffectedByRollback( | |
| 440 | const std::string &target_tag_name, std::vector<Tag> *tags) const { | ||
| 441 | // retrieve the old version of the target tag from the history | ||
| 442 |
1/2✓ Branch 1 taken 164 times.
✗ Branch 2 not taken.
|
164 | Tag target_tag; |
| 443 |
3/4✓ Branch 1 taken 164 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 41 times.
✓ Branch 4 taken 123 times.
|
164 | if (!GetByName(target_tag_name, &target_tag)) { |
| 444 |
1/2✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
|
41 | LogCvmfs(kLogHistory, kLogDebug, "failed to retrieve target tag '%s'", |
| 445 | target_tag_name.c_str()); | ||
| 446 | 41 | return false; | |
| 447 | } | ||
| 448 | |||
| 449 | // prepage listing command to find affected tags for a potential rollback | ||
| 450 |
2/4✓ Branch 2 taken 123 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 123 times.
|
123 | if (!list_rollback_tags_->BindTargetTag(target_tag)) { |
| 451 | ✗ | LogCvmfs(kLogHistory, kLogDebug, | |
| 452 | "failed to prepare rollback listing query"); | ||
| 453 | ✗ | return false; | |
| 454 | } | ||
| 455 | |||
| 456 | // run the listing and return the results | ||
| 457 |
1/2✓ Branch 2 taken 123 times.
✗ Branch 3 not taken.
|
123 | return RunListing(tags, list_rollback_tags_.get()); |
| 458 | 164 | } | |
| 459 | |||
| 460 | |||
| 461 | 82 | bool SqliteHistory::GetHashes(std::vector<shash::Any> *hashes) const { | |
| 462 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 82 times.
|
82 | assert(database_.get() != nullptr); |
| 463 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | assert(NULL != hashes); |
| 464 | |||
| 465 |
2/2✓ Branch 2 taken 82041 times.
✓ Branch 3 taken 82 times.
|
82123 | while (get_hashes_->FetchRow()) { |
| 466 |
1/2✓ Branch 3 taken 82041 times.
✗ Branch 4 not taken.
|
82041 | hashes->push_back(get_hashes_->RetrieveHash()); |
| 467 | } | ||
| 468 | |||
| 469 | 82 | return get_hashes_->Reset(); | |
| 470 | } | ||
| 471 | |||
| 472 | |||
| 473 | 440 | void SqliteHistory::TakeDatabaseFileOwnership() { | |
| 474 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 440 times.
|
440 | assert(database_.get() != nullptr); |
| 475 | 440 | database_->TakeFileOwnership(); | |
| 476 | 440 | } | |
| 477 | |||
| 478 | |||
| 479 | ✗ | void SqliteHistory::DropDatabaseFileOwnership() { | |
| 480 | ✗ | assert(database_.get() != nullptr); | |
| 481 | ✗ | database_->DropFileOwnership(); | |
| 482 | } | ||
| 483 | |||
| 484 | } // namespace history | ||
| 485 |