CernVM-FS  2.13.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 
52  "created empty history database '%s' for"
53  "repository '%s'",
54  file_name.c_str(), fqrn.c_str());
55  return history;
56 }
57 
58 
59 bool SqliteHistory::OpenDatabase(const std::string &file_name,
60  const bool read_write) {
61  assert(!database_.IsValid());
62  const HistoryDatabase::OpenMode mode = (read_write)
63  ? HistoryDatabase::kOpenReadWrite
64  : HistoryDatabase::kOpenReadOnly;
65  database_ = HistoryDatabase::Open(file_name, mode);
66  if (!database_.IsValid()) {
67  return false;
68  }
69 
70  if (!database_->HasProperty(HistoryDatabase::kFqrnKey)) {
72  "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 = database_->GetProperty<std::string>(
148  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) && insert_tag_->Execute()
175  && insert_tag_->Reset();
176 }
177 
178 
179 bool SqliteHistory::Remove(const std::string &name) {
180  assert(database_.IsValid());
181  assert(remove_tag_.IsValid());
182 
183  Tag condemned_tag;
184  if (!GetByName(name, &condemned_tag)) {
185  return true;
186  }
187 
188  return remove_tag_->BindName(name) && remove_tag_->Execute()
189  && remove_tag_->Reset();
190 }
191 
192 
193 bool SqliteHistory::Exists(const std::string &name) const {
194  Tag existing_tag;
195  return GetByName(name, &existing_tag);
196 }
197 
198 
199 bool SqliteHistory::GetByName(const std::string &name, Tag *tag) const {
200  assert(database_.IsValid());
201  assert(find_tag_.IsValid());
202  assert(NULL != tag);
203 
204  if (!find_tag_->BindName(name) || !find_tag_->FetchRow()) {
205  find_tag_->Reset();
206  return false;
207  }
208 
209  *tag = find_tag_->RetrieveTag();
210  return find_tag_->Reset();
211 }
212 
213 
214 bool SqliteHistory::GetByDate(const time_t timestamp, Tag *tag) const {
215  assert(database_.IsValid());
216  assert(find_tag_by_date_.IsValid());
217  assert(NULL != tag);
218 
219  if (!find_tag_by_date_->BindTimestamp(timestamp)
220  || !find_tag_by_date_->FetchRow()) {
221  find_tag_by_date_->Reset();
222  return false;
223  }
224 
225  *tag = find_tag_by_date_->RetrieveTag();
226  return find_tag_by_date_->Reset();
227 }
228 
229 
230 bool SqliteHistory::List(std::vector<Tag> *tags) const {
231  assert(list_tags_.IsValid());
232  return RunListing(tags, list_tags_.weak_ref());
233 }
234 
235 
236 template<class SqlListingT>
237 bool SqliteHistory::RunListing(std::vector<Tag> *list, SqlListingT *sql) const {
238  assert(database_.IsValid());
239  assert(NULL != list);
240 
241  while (sql->FetchRow()) {
242  list->push_back(sql->RetrieveTag());
243  }
244 
245  return sql->Reset();
246 }
247 
248 
249 bool SqliteHistory::GetBranchHead(const string &branch_name, Tag *tag) const {
250  assert(database_.IsValid());
251  assert(find_branch_head_.IsValid());
252  assert(tag != NULL);
253 
254  if (!find_branch_head_->BindBranchName(branch_name)
255  || !find_branch_head_->FetchRow()) {
256  find_branch_head_->Reset();
257  return false;
258  }
259 
260  *tag = find_branch_head_->RetrieveTag();
261  return find_branch_head_->Reset();
262 }
263 
264 
265 bool SqliteHistory::ExistsBranch(const string &branch_name) const {
266  vector<Branch> branches;
267  if (!ListBranches(&branches))
268  return false;
269  for (unsigned i = 0; i < branches.size(); ++i) {
270  if (branches[i].branch == branch_name)
271  return true;
272  }
273  return false;
274 }
275 
276 
277 bool SqliteHistory::InsertBranch(const Branch &branch) {
278  assert(database_.IsValid());
279  assert(insert_branch_.IsValid());
280 
281  return insert_branch_->BindBranch(branch) && insert_branch_->Execute()
282  && insert_branch_->Reset();
283 }
284 
285 
286 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  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  " ON (branches.parent=abandoned_branch);");
300  // Detect if fix point is reached
301  sqlite::Sql sql_remaining_rows(
302  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  "ON (branches.parent=abandoned_branch);");
309 
310  bool retval;
311  do {
312  retval = sql_remaining_rows.FetchRow();
313  if (!retval)
314  return false;
315  int64_t count = sql_remaining_rows.RetrieveInt64(0);
316  assert(count >= 0);
317  if (count == 0)
318  break;
319  retval = sql_remaining_rows.Reset();
320  assert(retval);
321 
322  retval = sql_fix_parent_pointers.Execute();
323  if (!retval)
324  return false;
325  retval = sql_fix_parent_pointers.Reset();
326  assert(retval);
327  } while (true);
328 
329  sqlite::Sql sql_remove_branches(
330  database_->sqlite_db(),
331  "DELETE FROM branches "
332  "WHERE branch NOT IN (SELECT DISTINCT branch FROM tags);");
333  retval = sql_remove_branches.Execute();
334  return retval;
335 }
336 
337 
338 bool SqliteHistory::ListBranches(vector<Branch> *branches) const {
339  while (list_branches_->FetchRow()) {
340  branches->push_back(list_branches_->RetrieveBranch());
341  }
342 
343  return list_branches_->Reset();
344 }
345 
346 
347 bool SqliteHistory::ListRecycleBin(std::vector<shash::Any> *hashes) const {
348  assert(database_.IsValid());
349 
350  if (!database_->ContainsRecycleBin()) {
351  return false;
352  }
353 
354  assert(NULL != hashes);
355  hashes->clear();
356  while (recycle_list_->FetchRow()) {
357  hashes->push_back(recycle_list_->RetrieveHash());
358  }
359 
360  return recycle_list_->Reset();
361 }
362 
363 
364 bool SqliteHistory::EmptyRecycleBin() {
365  assert(database_.IsValid());
366  assert(IsWritable());
367  assert(recycle_empty_.IsValid());
368  return recycle_empty_->Execute() && recycle_empty_->Reset();
369 }
370 
371 
372 bool SqliteHistory::Rollback(const Tag &updated_target_tag) {
373  assert(database_.IsValid());
374  assert(IsWritable());
375  assert(rollback_tag_.IsValid());
376 
377  Tag old_target_tag;
378  bool success = false;
379 
380  // open a transaction (if non open yet)
381  const bool need_to_commit = BeginTransaction();
382 
383  // retrieve the old version of the target tag from the history
384  success = GetByName(updated_target_tag.name, &old_target_tag);
385  if (!success) {
386  LogCvmfs(kLogHistory, kLogDebug, "failed to retrieve old target tag '%s'",
387  updated_target_tag.name.c_str());
388  return false;
389  }
390 
391  // sanity checks
392  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  success = rollback_tag_->BindTargetTag(old_target_tag)
397  && rollback_tag_->Execute() && rollback_tag_->Reset();
398  if (!success || Exists(old_target_tag.name)) {
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  success = Insert(updated_target_tag);
409  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  if (need_to_commit) {
416  success = CommitTransaction();
417  assert(success);
418  }
419 
420  return true;
421 }
422 
423 
424 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  Tag target_tag;
428  if (!GetByName(target_tag_name, &target_tag)) {
429  LogCvmfs(kLogHistory, kLogDebug, "failed to retrieve target tag '%s'",
430  target_tag_name.c_str());
431  return false;
432  }
433 
434  // prepage listing command to find affected tags for a potential rollback
435  if (!list_rollback_tags_->BindTargetTag(target_tag)) {
437  "failed to prepare rollback listing query");
438  return false;
439  }
440 
441  // run the listing and return the results
442  return RunListing(tags, list_rollback_tags_.weak_ref());
443 }
444 
445 
446 bool SqliteHistory::GetHashes(std::vector<shash::Any> *hashes) const {
447  assert(database_.IsValid());
448  assert(NULL != hashes);
449 
450  while (get_hashes_->FetchRow()) {
451  hashes->push_back(get_hashes_->RetrieveHash());
452  }
453 
454  return get_hashes_->Reset();
455 }
456 
457 
458 void SqliteHistory::TakeDatabaseFileOwnership() {
459  assert(database_.IsValid());
460  database_->TakeFileOwnership();
461 }
462 
463 
464 void SqliteHistory::DropDatabaseFileOwnership() {
465  assert(database_.IsValid());
466  database_->DropFileOwnership();
467 }
468 
469 } // namespace history
bool Execute()
Definition: sql.cc:41
bool FetchRow()
Definition: sql.cc:61
static Publisher * Create(const SettingsPublisher &settings)
std::string name
Definition: history.h:92
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:241
void Open()
const history::History * history() const
assert((mem||(size==0))&&"Out Of Memory")
void List()
const std::string & fqrn() const
Definition: history.h:197
std::string description
Definition: history.h:97
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:82
void Rollback()
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545