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