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