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