CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
catalog_sql.cc
Go to the documentation of this file.
1 
6 #include "catalog_sql.h"
7 
8 #include <cstdlib>
9 #include <cstring>
10 
11 #include "catalog.h"
12 #include "globals.h"
13 #include "util/logging.h"
14 #include "util/posix.h"
15 #include "xattr.h"
16 
17 using namespace std; // NOLINT
18 
19 namespace catalog {
20 
34 // ChangeLog
35 // 2.5 (Jun 26 2013 - Git: e79baec22c6abd6ddcdf8f8d7d33921027a052ab)
36 // * add (backward compatible) schema revision - see below
37 // * add statistics counters for chunked files
38 // Note: this was retrofitted and needed a catalog migration step
39 //
40 // 2.4 (Aug 15 2012 - Git: 17de8fc782b5b8dc4404dda925627b5ec2b552e1)
41 // 2.3 (Aug 15 2012 - Git: ab77688cdb2f851af3fe983bf3694dc2465e65be)
42 // 2.2 (never existed)
43 // 2.1 (Aug 7 2012 - Git: beba36c12d2b1123ffbb169f865a861e570adc68)
44 // * add 'chunks' table for file chunks
45 // * add 'statistics' table for accumulative counters
46 // * rename 'inode' field to 'hardlinks'
47 // * containing both hardlink group ID and linkcount
48 // * .cvmfscatalog files become first-class entries in the catalogs
49 //
50 // 2.0 (Aug 6 2012 - Git: c8a81ede603e57fbe4324b6ab6bc8c41e3a2fa5f)
51 // * beginning of CernVM-FS 2.1.x branch ('modern' era)
52 //
53 // 1.x (earlier - code base still in SVN)
54 // * pre-historic times
55 // 0.9 (some time 2011, artificial version)
56 // * 1.0 catalogs that lack the SHA-1 value for nested catalogs
57 const float CatalogDatabase::kLatestSchema = 2.5;
58 const float CatalogDatabase::kLatestSupportedSchema = 2.5; // + 1.X (r/o)
59 
60 // ChangeLog
61 // 0 --> 1: (Jan 6 2014 - Git: 3667fe7a669d0d65e07275b753a7c6f23fc267df)
62 // * add size column to nested catalog table,
63 // * add schema_revision property
64 // 1 --> 2: (Jan 22 2014 - Git: 85e6680e52cfe56dc1213a5ad74a5cc62fd50ead):
65 // * add xattr column to catalog table
66 // * add self_xattr and subtree_xattr statistics counters
67 // 2 --> 3: (Sep 28 2015 - Git: f4171234b13ea448589820c1524ee52eae141bb4):
68 // * add kFlagFileExternal to entries in catalog table
69 // * add self_external and subtree_external statistics counters
70 // * store compression algorithm in flags
71 // 3 --> 4: (Nov 11 2016 - Git):
72 // * add kFlagDirBindMountpoint
73 // * add kFlagHidden
74 // * add table bind_mountpoints
75 // 4 --> 5: (Dec 07 2017):
76 // * add kFlagFileSpecial (rebranded unused kFlagFileStat)
77 // * add self_special and subtree_special statistics counters
78 // 5 --> 6: (Jul 01 2021):
79 // * Add kFlagDirectIo
80 // 6 --> 7: (Feb 23 2024):
81 // * Store nanosecond timestamps (mtimens column) in catalog table.
82 // The mtimens column has only the nanosecond part of the timestamp
83 // and may be NULL
84 const unsigned CatalogDatabase::kLatestSchemaRevision = 7;
85 
86 bool CatalogDatabase::CheckSchemaCompatibility() {
87  return !((schema_version() >= 2.0 - kSchemaEpsilon)
88  && (!IsEqualSchema(schema_version(), kLatestSupportedSchema))
89  && (!IsEqualSchema(schema_version(), 2.4)
90  || !IsEqualSchema(kLatestSupportedSchema, 2.5)));
91 }
92 
93 
94 bool CatalogDatabase::LiveSchemaUpgradeIfNecessary() {
95  assert(read_write());
96 
97  if (IsEqualSchema(schema_version(), 2.5) && (schema_revision() == 0)) {
98  LogCvmfs(kLogCatalog, kLogDebug, "upgrading schema revision (0 --> 1)");
99 
100  SqlCatalog sql_upgrade(*this, "ALTER TABLE nested_catalogs "
101  "ADD size INTEGER;");
102  if (!sql_upgrade.Execute()) {
103  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade nested_catalogs");
104  return false;
105  }
106 
107  set_schema_revision(1);
108  if (!StoreSchemaRevision()) {
109  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade schema revision");
110  return false;
111  }
112  }
113 
114  if (IsEqualSchema(schema_version(), 2.5) && (schema_revision() == 1)) {
115  LogCvmfs(kLogCatalog, kLogDebug, "upgrading schema revision (1 --> 2)");
116 
117  SqlCatalog sql_upgrade1(*this, "ALTER TABLE catalog ADD xattr BLOB;");
118  SqlCatalog sql_upgrade2(
119  *this,
120  "INSERT INTO statistics (counter, value) VALUES ('self_xattr', 0);");
121  SqlCatalog sql_upgrade3(
122  *this,
123  "INSERT INTO statistics (counter, value) VALUES ('subtree_xattr', 0);");
124  if (!sql_upgrade1.Execute() || !sql_upgrade2.Execute()
125  || !sql_upgrade3.Execute()) {
126  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade catalogs (1 --> 2)");
127  return false;
128  }
129 
130  set_schema_revision(2);
131  if (!StoreSchemaRevision()) {
132  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade schema revision");
133  return false;
134  }
135  }
136 
137  if (IsEqualSchema(schema_version(), 2.5) && (schema_revision() == 2)) {
138  LogCvmfs(kLogCatalog, kLogDebug, "upgrading schema revision (2 --> 3)");
139 
140  SqlCatalog sql_upgrade4(*this,
141  "INSERT INTO statistics (counter, value) VALUES "
142  "('self_external', 0);");
143  SqlCatalog sql_upgrade5(*this,
144  "INSERT INTO statistics (counter, value) VALUES "
145  "('self_external_file_size', 0);");
146  SqlCatalog sql_upgrade6(*this,
147  "INSERT INTO statistics (counter, value) VALUES "
148  "('subtree_external', 0);");
149  SqlCatalog sql_upgrade7(*this,
150  "INSERT INTO statistics (counter, value) VALUES "
151  "('subtree_external_file_size', 0);");
152  if (!sql_upgrade4.Execute() || !sql_upgrade5.Execute()
153  || !sql_upgrade6.Execute() || !sql_upgrade7.Execute()) {
154  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade catalogs (2 --> 3)");
155  return false;
156  }
157 
158  set_schema_revision(3);
159  if (!StoreSchemaRevision()) {
160  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade schema revision");
161  return false;
162  }
163  }
164 
165  if (IsEqualSchema(schema_version(), 2.5) && (schema_revision() == 3)) {
166  LogCvmfs(kLogCatalog, kLogDebug, "upgrading schema revision (3 --> 4)");
167 
168  SqlCatalog sql_upgrade8(
169  *this,
170  "CREATE TABLE bind_mountpoints (path TEXT, sha1 TEXT, size INTEGER, "
171  "CONSTRAINT pk_bind_mountpoints PRIMARY KEY (path));");
172  if (!sql_upgrade8.Execute()) {
173  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade catalogs (3 --> 4)");
174  return false;
175  }
176 
177  set_schema_revision(4);
178  if (!StoreSchemaRevision()) {
179  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade schema revision");
180  return false;
181  }
182  }
183 
184 
185  if (IsEqualSchema(schema_version(), 2.5) && (schema_revision() == 4)) {
186  LogCvmfs(kLogCatalog, kLogDebug, "upgrading schema revision (4 --> 5)");
187 
188  SqlCatalog sql_upgrade9(*this,
189  "INSERT INTO statistics (counter, value) VALUES "
190  "('self_special', 0);");
191  SqlCatalog sql_upgrade10(*this,
192  "INSERT INTO statistics (counter, value) VALUES "
193  "('subtree_special', 0);");
194  if (!sql_upgrade9.Execute() || !sql_upgrade10.Execute()) {
195  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade catalogs (4 --> 5)");
196  return false;
197  }
198 
199  set_schema_revision(5);
200  if (!StoreSchemaRevision()) {
201  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade schema revision");
202  return false;
203  }
204  }
205 
206 
207  if (IsEqualSchema(schema_version(), 2.5) && (schema_revision() == 5)) {
208  LogCvmfs(kLogCatalog, kLogDebug, "upgrading schema revision (5 --> 6)");
209 
210  set_schema_revision(6);
211  if (!StoreSchemaRevision()) {
212  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade schema revision");
213  return false;
214  }
215  }
216 
217  if (IsEqualSchema(schema_version(), 2.5) && (schema_revision() == 6)) {
218  LogCvmfs(kLogCatalog, kLogDebug, "upgrading schema revision (6 --> 7)");
219 
220  SqlCatalog sql_upgrade1(*this, "ALTER TABLE catalog ADD mtimens INTEGER;");
221  if (!sql_upgrade1.Execute()) {
222  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade catalogs (6 --> 7)");
223  return false;
224  }
225 
226  set_schema_revision(7);
227  if (!StoreSchemaRevision()) {
228  LogCvmfs(kLogCatalog, kLogDebug, "failed to upgrade schema revision");
229  return false;
230  }
231  }
232 
233  return true;
234 }
235 
236 
237 bool CatalogDatabase::CreateEmptyDatabase() {
238  assert(read_write());
239 
240  // generate the catalog table and index structure
241  const bool retval =
242  SqlCatalog(*this,
243  "CREATE TABLE catalog "
244  "(md5path_1 INTEGER, md5path_2 INTEGER, parent_1 INTEGER, "
245  "parent_2 INTEGER,"
246  " hardlinks INTEGER, hash BLOB, size INTEGER, mode INTEGER, "
247  "mtime INTEGER,"
248  " mtimens INTEGER, flags INTEGER, name TEXT, symlink TEXT, "
249  "uid INTEGER,"
250  " gid INTEGER, xattr BLOB, "
251  " CONSTRAINT pk_catalog PRIMARY KEY (md5path_1, md5path_2));")
252  .Execute()
253  && SqlCatalog(*this,
254  "CREATE INDEX idx_catalog_parent "
255  "ON catalog (parent_1, parent_2);")
256  .Execute()
257  && SqlCatalog(*this,
258  "CREATE TABLE chunks "
259  "(md5path_1 INTEGER, md5path_2 INTEGER, offset INTEGER, "
260  "size INTEGER, "
261  " hash BLOB, "
262  " CONSTRAINT pk_chunks PRIMARY KEY (md5path_1, md5path_2, "
263  "offset, size), "
264  " FOREIGN KEY (md5path_1, md5path_2) REFERENCES "
265  " catalog(md5path_1, md5path_2));")
266  .Execute()
267  && SqlCatalog(*this,
268  "CREATE TABLE nested_catalogs (path TEXT, sha1 TEXT, size "
269  "INTEGER, "
270  "CONSTRAINT pk_nested_catalogs PRIMARY KEY (path));")
271  .Execute()
272  &&
273  // Bind mountpoints and nested catalogs are almost the same. We put them
274  // in separate tables to
275  // - not confuse previous client versions, which would crash on bind
276  // mountpoints
277  // - prevent catalogs referenced as bind mountpoints from being
278  // replicated,
279  // which would cause exhaustive recursive catalog tree walking
280  // - don't walk into bind mountpoints in catalog traversal (e.g. GC)
281  SqlCatalog(
282  *this,
283  "CREATE TABLE bind_mountpoints (path TEXT, sha1 TEXT, size INTEGER, "
284  "CONSTRAINT pk_bind_mountpoints PRIMARY KEY (path));")
285  .Execute()
286  && SqlCatalog(*this,
287  "CREATE TABLE statistics (counter TEXT, value INTEGER, "
288  "CONSTRAINT pk_statistics PRIMARY KEY (counter));")
289  .Execute();
290 
291  if (!retval) {
292  PrintSqlError("failed to create catalog database tables.");
293  }
294 
295  return retval;
296 }
297 
298 
299 bool CatalogDatabase::InsertInitialValues(const std::string &root_path,
300  const bool volatile_content,
301  const std::string &voms_authz,
302  const DirectoryEntry &root_entry) {
303  assert(read_write());
304  bool retval = false;
305 
306  // Path hashes
307  const shash::Md5 root_path_hash = shash::Md5(shash::AsciiPtr(root_path));
308  const shash::Md5 root_parent_hash =
309  (root_path == "") ? shash::Md5()
311 
312  // Start initial filling transaction
313  retval = BeginTransaction();
314  if (!retval) {
315  PrintSqlError("failed to enter initial filling transaction");
316  return false;
317  }
318 
319  // Insert initial values to properties
320  if (!this->SetProperty("revision", 0)) {
321  PrintSqlError(
322  "failed to insert default initial values into the newly created "
323  "catalog tables.");
324  return false;
325  }
326 
327  if (volatile_content) {
328  if (!this->SetProperty("volatile", 1)) {
329  PrintSqlError("failed to insert volatile flag into the newly created "
330  "catalog tables.");
331  return false;
332  }
333  }
334 
335  if (!voms_authz.empty()) {
336  if (!SetVOMSAuthz(voms_authz)) {
337  PrintSqlError("failed to insert VOMS authz flag into the newly created "
338  "catalog tables.");
339  return false;
340  }
341  }
342 
343  // Create initial statistics counters
344  catalog::Counters counters;
345 
346  // Insert root entry (when given)
347  if (!root_entry.IsNegative()) {
348  SqlDirentInsert sql_insert(*this);
349  retval = sql_insert.BindPathHash(root_path_hash)
350  && sql_insert.BindParentPathHash(root_parent_hash)
351  && sql_insert.BindDirent(root_entry) && sql_insert.Execute();
352  if (!retval) {
353  PrintSqlError("failed to insert root entry into newly created catalog.");
354  return false;
355  }
356 
357  // account for the created root entry
358  counters.self.directories = 1;
359  }
360 
361  // Save initial statistics counters
362  if (!counters.InsertIntoDatabase(*this)) {
363  PrintSqlError("failed to insert initial catalog statistics counters.");
364  return false;
365  }
366 
367  // Insert root path (when given)
368  if (!root_path.empty()) {
369  if (!this->SetProperty("root_prefix", root_path)) {
370  PrintSqlError(
371  "failed to store root prefix in the newly created catalog.");
372  return false;
373  }
374  }
375 
376  // Set creation timestamp
377  if (!this->SetProperty("last_modified", static_cast<uint64_t>(time(NULL)))) {
378  PrintSqlError("failed to store creation timestamp in the new catalog.");
379  return false;
380  }
381 
382  // Commit initial filling transaction
383  retval = CommitTransaction();
384  if (!retval) {
385  PrintSqlError("failed to commit initial filling transaction");
386  return false;
387  }
388 
389  return true;
390 }
391 
392 
393 bool CatalogDatabase::SetVOMSAuthz(const std::string &voms_authz) {
394  return this->SetProperty("voms_authz", voms_authz);
395 }
396 
397 
398 double CatalogDatabase::GetRowIdWasteRatio() const {
399  SqlCatalog rowid_waste_ratio_query(
400  *this,
401  "SELECT 1.0 - CAST(COUNT(*) AS DOUBLE) / MAX(rowid) "
402  "AS ratio FROM catalog;");
403  const bool retval = rowid_waste_ratio_query.FetchRow();
404  assert(retval);
405 
406  return rowid_waste_ratio_query.RetrieveDouble(0);
407 }
408 
429 bool CatalogDatabase::CompactDatabase() const {
430  assert(read_write());
431 
432  return SqlCatalog(*this, "PRAGMA foreign_keys = OFF;").Execute()
433  && BeginTransaction()
434  && SqlCatalog(*this, "CREATE TEMPORARY TABLE duplicate AS "
435  " SELECT * FROM catalog "
436  " ORDER BY rowid ASC;")
437  .Execute()
438  && SqlCatalog(*this, "DELETE FROM catalog;").Execute()
439  && SqlCatalog(*this, "INSERT INTO catalog "
440  " SELECT * FROM duplicate "
441  " ORDER BY rowid")
442  .Execute()
443  && SqlCatalog(*this, "DROP TABLE duplicate;").Execute()
444  && CommitTransaction()
445  && SqlCatalog(*this, "PRAGMA foreign_keys = ON;").Execute();
446 }
447 
448 
449 //------------------------------------------------------------------------------
450 
451 
452 unsigned SqlDirent::CreateDatabaseFlags(const DirectoryEntry &entry) const {
453  unsigned int database_flags = 0;
454 
455  if (entry.IsNestedCatalogRoot())
456  database_flags |= kFlagDirNestedRoot;
457  else if (entry.IsNestedCatalogMountpoint())
458  database_flags |= kFlagDirNestedMountpoint;
459  else if (entry.IsBindMountpoint())
460  database_flags |= kFlagDirBindMountpoint;
461 
462  if (entry.IsDirectory()) {
463  database_flags |= kFlagDir;
464  } else if (entry.IsLink()) {
465  database_flags |= kFlagFile | kFlagLink;
466  } else if (entry.IsSpecial()) {
467  database_flags |= kFlagFile | kFlagFileSpecial;
468  } else {
469  database_flags |= kFlagFile;
470  database_flags |= entry.compression_algorithm() << kFlagPosCompression;
471  if (entry.IsChunkedFile())
472  database_flags |= kFlagFileChunk;
473  if (entry.IsExternalFile())
474  database_flags |= kFlagFileExternal;
475  if (entry.IsDirectIo())
476  database_flags |= kFlagDirectIo;
477  }
478 
479  if (!entry.checksum_ptr()->IsNull() || entry.IsChunkedFile())
480  StoreHashAlgorithm(entry.checksum_ptr()->algorithm, &database_flags);
481 
482  if (entry.IsHidden())
483  database_flags |= kFlagHidden;
484 
485  return database_flags;
486 }
487 
488 
489 void SqlDirent::StoreHashAlgorithm(const shash::Algorithms algo,
490  unsigned *flags) const {
491  assert(algo != shash::kAny);
492  // Md5 unusable for content hashes
493  *flags |= (algo - 1) << kFlagPosHash;
494 }
495 
496 
497 shash::Algorithms SqlDirent::RetrieveHashAlgorithm(const unsigned flags) const {
498  unsigned in_flags = ((7 << kFlagPosHash) & flags) >> kFlagPosHash;
499  // Skip Md5
500  in_flags++;
501  assert(in_flags < shash::kAny);
502  return static_cast<shash::Algorithms>(in_flags);
503 }
504 
505 
506 zlib::Algorithms SqlDirent::RetrieveCompressionAlgorithm(
507  const unsigned flags) const {
508  // 3 bits, so use 7 (111) to only pull out the flags we want
509  const unsigned in_flags =
510  ((7 << kFlagPosCompression) & flags) >> kFlagPosCompression;
511  return static_cast<zlib::Algorithms>(in_flags);
512 }
513 
514 
515 uint32_t SqlDirent::Hardlinks2Linkcount(const uint64_t hardlinks) const {
516  return (hardlinks << 32) >> 32;
517 }
518 
519 
520 uint32_t SqlDirent::Hardlinks2HardlinkGroup(const uint64_t hardlinks) const {
521  return hardlinks >> 32;
522 }
523 
524 
525 uint64_t SqlDirent::MakeHardlinks(const uint32_t hardlink_group,
526  const uint32_t linkcount) const {
527  assert(linkcount > 0);
528  return (static_cast<uint64_t>(hardlink_group) << 32) | linkcount;
529 }
530 
531 
536 void SqlDirent::ExpandSymlink(LinkString *raw_symlink) const {
537  const char *c = raw_symlink->GetChars();
538  const char *cEnd = c + raw_symlink->GetLength();
539  for (; c < cEnd; ++c) {
540  if (*c == '$')
541  goto expand_symlink;
542  }
543  return;
544 
545 expand_symlink:
546  LinkString result;
547  for (c = raw_symlink->GetChars(); c < cEnd; ++c) {
548  if ((*c == '$') && (c < cEnd - 2) && (*(c + 1) == '(')) {
549  c += 2;
550  const char *rpar = c;
551  while (rpar < cEnd) {
552  if (*rpar == ')')
553  goto expand_symlink_getenv;
554  rpar++;
555  }
556  // right parenthesis missing
557  result.Append("$(", 2);
558  result.Append(c, 1);
559  continue;
560 
561  expand_symlink_getenv:
562  // Check for default value
563  const char *default_separator = c;
564  const char *default_value = rpar;
565  while (default_separator != rpar) {
566  if ((*default_separator == ':') && (*(default_separator + 1) == '-')) {
567  default_value = default_separator + 2;
568  break;
569  }
570  default_separator++;
571  }
572 
573  const unsigned environ_var_length = default_separator - c;
574  char environ_var[environ_var_length + 1];
575  environ_var[environ_var_length] = '\0';
576  memcpy(environ_var, c, environ_var_length);
577  const char *environ_value = getenv(environ_var); // Don't free!
578  if (environ_value) {
579  result.Append(environ_value, strlen(environ_value));
580  } else {
581  const unsigned default_length = rpar - default_value;
582  result.Append(default_value, default_length);
583  }
584  c = rpar;
585  continue;
586  }
587  result.Append(c, 1);
588  }
589  raw_symlink->Assign(result);
590  return;
591 }
592 
593 
594 //------------------------------------------------------------------------------
595 
596 
597 bool SqlDirentWrite::BindDirentFields(const int hash_idx,
598  const int hardlinks_idx,
599  const int size_idx,
600  const int mode_idx,
601  const int mtime_idx,
602  const int mtimens_idx,
603  const int flags_idx,
604  const int name_idx,
605  const int symlink_idx,
606  const int uid_idx,
607  const int gid_idx,
608  const DirectoryEntry &entry) {
609  const uint64_t hardlinks = MakeHardlinks(entry.hardlink_group_,
610  entry.linkcount_);
611 
612  bool result = BindHashBlob(hash_idx, entry.checksum_)
613  && BindInt64(hardlinks_idx, hardlinks)
614  && BindInt64(size_idx, entry.size_)
615  && BindInt(mode_idx, entry.mode_)
616  && BindInt64(uid_idx, entry.uid_)
617  && BindInt64(gid_idx, entry.gid_)
618  && BindInt64(mtime_idx, entry.mtime_)
619  && BindInt(flags_idx, CreateDatabaseFlags(entry))
620  && BindText(name_idx, entry.name_.GetChars(),
621  static_cast<int>(entry.name_.GetLength()))
622  && BindText(symlink_idx, entry.symlink_.GetChars(),
623  static_cast<int>(entry.symlink_.GetLength()));
624  if (entry.HasMtimeNs()) {
625  result &= BindInt(mtimens_idx, entry.mtime_ns_);
626  } else {
627  result &= BindNull(mtimens_idx);
628  }
629 
630  return result;
631 }
632 
633 
634 //------------------------------------------------------------------------------
635 
636 
637 SqlListContentHashes::SqlListContentHashes(const CatalogDatabase &database) {
638  static const char *stmt_lt_2_4 = "SELECT hash, flags, 0 "
639  " FROM catalog "
640  " WHERE length(hash) > 0;";
641 
642  static const char
643  *stmt_ge_2_4 = "SELECT hash, flags, 0 "
644  " FROM catalog "
645  " WHERE (length(catalog.hash) > 0) AND "
646  " ((flags & 128) = 0) " // kFlagFileExternal
647  "UNION "
648  "SELECT chunks.hash, catalog.flags, 1 "
649  " FROM catalog "
650  " LEFT JOIN chunks "
651  " ON catalog.md5path_1 = chunks.md5path_1 AND "
652  " catalog.md5path_2 = chunks.md5path_2 "
653  " WHERE (catalog.flags & 128) = 0;"; // kFlagFileExternal
654 
655  if (database.schema_version() < 2.4 - CatalogDatabase::kSchemaEpsilon) {
656  DeferredInit(database.sqlite_db(), stmt_lt_2_4);
657  } else {
658  DeferredInit(database.sqlite_db(), stmt_ge_2_4);
659  }
660 }
661 
662 
663 shash::Any SqlListContentHashes::GetHash() const {
664  const unsigned int db_flags = RetrieveInt(1);
665  const shash::Algorithms hash_algorithm = RetrieveHashAlgorithm(db_flags);
666  shash::Any hash = RetrieveHashBlob(0, hash_algorithm);
667  if (RetrieveInt(2) == 1) {
669  }
670 
671  return hash;
672 }
673 
674 
675 //------------------------------------------------------------------------------
676 
677 #define DB_FIELDS_LT_V2_1 \
678  "catalog.hash, catalog.inode, catalog.size, " \
679  "catalog.mode, catalog.mtime, catalog.flags, " \
680  "catalog.name, catalog.symlink, catalog.md5path_1, " \
681  "catalog.md5path_2, catalog.parent_1, catalog.parent_2, " \
682  "catalog.rowid"
683 #define DB_FIELDS_GE_V2_1_LT_R2 \
684  "catalog.hash, catalog.hardlinks, catalog.size, " \
685  "catalog.mode, catalog.mtime, catalog.flags, " \
686  "catalog.name, catalog.symlink, catalog.md5path_1, " \
687  "catalog.md5path_2, catalog.parent_1, catalog.parent_2, " \
688  "catalog.rowid, catalog.uid, catalog.gid, " \
689  "0, NULL"
690 #define DB_FIELDS_GE_V2_1_LT_R7 \
691  "catalog.hash, catalog.hardlinks, catalog.size, " \
692  "catalog.mode, catalog.mtime, catalog.flags, " \
693  "catalog.name, catalog.symlink, catalog.md5path_1, " \
694  "catalog.md5path_2, catalog.parent_1, catalog.parent_2, " \
695  "catalog.rowid, catalog.uid, catalog.gid, " \
696  "catalog.xattr IS NOT NULL, NULL"
697 #define DB_FIELDS_GE_V2_1_GE_R7 \
698  "catalog.hash, catalog.hardlinks, catalog.size, " \
699  "catalog.mode, catalog.mtime, catalog.flags, " \
700  "catalog.name, catalog.symlink, catalog.md5path_1, " \
701  "catalog.md5path_2, catalog.parent_1, catalog.parent_2, " \
702  "catalog.rowid, catalog.uid, catalog.gid, " \
703  "catalog.xattr IS NOT NULL, catalog.mtimens"
704 
705 #define MAKE_STATEMENT(STMT_TMPL, REV) \
706  static const std::string REV = ReplaceAll(STMT_TMPL, "@DB_FIELDS@", \
707  DB_FIELDS_##REV)
708 
709 #define MAKE_STATEMENTS(STMT_TMPL) \
710  MAKE_STATEMENT(STMT_TMPL, LT_V2_1); \
711  MAKE_STATEMENT(STMT_TMPL, GE_V2_1_LT_R2); \
712  MAKE_STATEMENT(STMT_TMPL, GE_V2_1_LT_R7); \
713  MAKE_STATEMENT(STMT_TMPL, GE_V2_1_GE_R7)
714 
715 #define DEFERRED_INIT(DB, REV) DeferredInit((DB).sqlite_db(), (REV).c_str())
716 
717 #define DEFERRED_INITS(DB) \
718  if ((DB).schema_version() < 2.1 - CatalogDatabase::kSchemaEpsilon) { \
719  DEFERRED_INIT((DB), LT_V2_1); \
720  } else if ((DB).schema_revision() < 2) { \
721  DEFERRED_INIT((DB), GE_V2_1_LT_R2); \
722  } else if ((DB).schema_revision() < 7) { \
723  DEFERRED_INIT((DB), GE_V2_1_LT_R7); \
724  } else { \
725  DEFERRED_INIT((DB), GE_V2_1_GE_R7); \
726  }
727 
728 
729 shash::Md5 SqlLookup::GetPathHash() const { return RetrieveMd5(8, 9); }
730 
731 
732 shash::Md5 SqlLookup::GetParentPathHash() const { return RetrieveMd5(10, 11); }
733 
734 
738 DirectoryEntry SqlLookup::GetDirent(const Catalog *catalog,
739  const bool expand_symlink) const {
740  DirectoryEntry result;
741 
742  const unsigned database_flags = RetrieveInt(5);
743  result.is_nested_catalog_root_ = (database_flags & kFlagDirNestedRoot);
744  result.is_nested_catalog_mountpoint_ = (database_flags
745  & kFlagDirNestedMountpoint);
746  const char *name = reinterpret_cast<const char *>(RetrieveText(6));
747  const char *symlink = reinterpret_cast<const char *>(RetrieveText(7));
748 
749  // Retrieve the hardlink information from the hardlinks database field
750  if (catalog->schema() < 2.1 - CatalogDatabase::kSchemaEpsilon) {
751  result.linkcount_ = 1;
752  result.hardlink_group_ = 0;
753  result.inode_ = catalog->GetMangledInode(RetrieveInt64(12), 0);
754  result.is_chunked_file_ = false;
755  result.has_xattrs_ = false;
756  result.checksum_ = RetrieveHashBlob(0, shash::kSha1);
757  result.uid_ = g_uid;
758  result.gid_ = g_gid;
759  } else {
760  const uint64_t hardlinks = RetrieveInt64(1);
761  result.linkcount_ = Hardlinks2Linkcount(hardlinks);
762  result.hardlink_group_ = Hardlinks2HardlinkGroup(hardlinks);
763  result.inode_ = catalog->GetMangledInode(RetrieveInt64(12),
764  result.hardlink_group_);
765  result.is_bind_mountpoint_ = (database_flags & kFlagDirBindMountpoint);
766  result.is_chunked_file_ = (database_flags & kFlagFileChunk);
767  result.is_hidden_ = (database_flags & kFlagHidden);
768  result.is_direct_io_ = (database_flags & kFlagDirectIo);
769  result.is_external_file_ = (database_flags & kFlagFileExternal);
770  result.has_xattrs_ = RetrieveInt(15) != 0;
771  result.mtime_ns_ = RetrieveNullableInt(16, -1);
772  result.checksum_ = RetrieveHashBlob(0,
773  RetrieveHashAlgorithm(database_flags));
774  result.compression_algorithm_ = RetrieveCompressionAlgorithm(
775  database_flags);
776 
777  if (g_claim_ownership) {
778  result.uid_ = g_uid;
779  result.gid_ = g_gid;
780  } else {
781  result.uid_ = catalog->MapUid(RetrieveInt64(13));
782  result.gid_ = catalog->MapGid(RetrieveInt64(14));
783  }
784  }
785 
786  result.mode_ = RetrieveInt(3);
787  result.size_ = RetrieveInt64(2);
788  result.mtime_ = RetrieveInt64(4);
789  result.name_.Assign(name, strlen(name));
790  result.symlink_.Assign(symlink, strlen(symlink));
791  if (expand_symlink && !g_raw_symlinks)
792  ExpandSymlink(&result.symlink_);
793 
794  if (g_world_readable) {
795  if (S_ISDIR(result.mode_)) {
796  result.mode_ |= 0555;
797  } else {
798  result.mode_ |= 0444;
799  }
800  }
801 
802  return result;
803 }
804 
805 
806 //------------------------------------------------------------------------------
807 
808 
809 SqlListing::SqlListing(const CatalogDatabase &database) {
810  MAKE_STATEMENTS("SELECT @DB_FIELDS@ FROM catalog "
811  "WHERE (parent_1 = :p_1) AND (parent_2 = :p_2);");
812  DEFERRED_INITS(database);
813 }
814 
815 
816 bool SqlListing::BindPathHash(const struct shash::Md5 &hash) {
817  return BindMd5(1, 2, hash);
818 }
819 
820 
821 //------------------------------------------------------------------------------
822 
823 
824 SqlLookupPathHash::SqlLookupPathHash(const CatalogDatabase &database) {
825  MAKE_STATEMENTS("SELECT @DB_FIELDS@ FROM catalog "
826  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
827  DEFERRED_INITS(database);
828 }
829 
830 bool SqlLookupPathHash::BindPathHash(const struct shash::Md5 &hash) {
831  return BindMd5(1, 2, hash);
832 }
833 
834 
835 //------------------------------------------------------------------------------
836 
837 
838 SqlLookupInode::SqlLookupInode(const CatalogDatabase &database) {
839  MAKE_STATEMENTS("SELECT @DB_FIELDS@ FROM catalog WHERE rowid = :rowid;");
840  DEFERRED_INITS(database);
841 }
842 
843 
844 bool SqlLookupInode::BindRowId(const uint64_t inode) {
845  return BindInt64(1, inode);
846 }
847 
848 
849 //------------------------------------------------------------------------------
850 
851 
852 SqlLookupDanglingMountpoints::SqlLookupDanglingMountpoints(
853  const catalog::CatalogDatabase &database) {
854  MAKE_STATEMENTS("SELECT DISTINCT @DB_FIELDS@ FROM catalog "
855  "JOIN catalog AS c2 "
856  "ON catalog.md5path_1 = c2.parent_1 AND "
857  " catalog.md5path_2 = c2.parent_2 "
858  "WHERE catalog.flags & :nested_mountpoint_flag");
859  DEFERRED_INITS(database);
860 
861  // this pretty much removes the advantage of a deferred init but the statement
862  // is anyway only used directly.
863  const bool success = BindInt64(1, SqlDirent::kFlagDirNestedMountpoint);
864  assert(success);
865 }
866 
867 
868 //------------------------------------------------------------------------------
869 
870 
871 SqlDirentTouch::SqlDirentTouch(const CatalogDatabase &database) {
872  DeferredInit(database.sqlite_db(),
873  "UPDATE catalog "
874  "SET hash = :hash, size = :size, mode = :mode, mtime = :mtime, "
875  // 1 2 3 4
876  "name = :name, symlink = :symlink, uid = :uid, gid = :gid, "
877  "xattr = :xattr, "
878  // 5 6 7 8 9
879  "mtimens = :mtimens "
880  // 10
881  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
882  // 11 12
883 }
884 
885 
886 bool SqlDirentTouch::BindDirentBase(const DirectoryEntryBase &entry) {
887  bool result = BindHashBlob(1, entry.checksum_) && BindInt64(2, entry.size_)
888  && BindInt(3, entry.mode_) && BindInt64(4, entry.mtime_)
889  && BindText(5, entry.name_.GetChars(), entry.name_.GetLength())
890  && BindText(6, entry.symlink_.GetChars(),
891  entry.symlink_.GetLength())
892  && BindInt64(7, entry.uid_) && BindInt64(8, entry.gid_);
893  if (entry.HasMtimeNs()) {
894  result &= BindInt(10, entry.mtime_ns_);
895  } else {
896  result &= BindNull(10);
897  }
898  return result;
899 }
900 
901 
902 bool SqlDirentTouch::BindPathHash(const shash::Md5 &hash) {
903  return BindMd5(11, 12, hash);
904 }
905 
906 
907 bool SqlDirentTouch::BindXattr(const XattrList &xattrs) {
908  unsigned char *packed_xattrs;
909  unsigned size;
910  xattrs.Serialize(&packed_xattrs, &size);
911  if (packed_xattrs == NULL)
912  return BindNull(9);
913  return BindBlobTransient(9, packed_xattrs, size);
914 }
915 
916 
917 bool SqlDirentTouch::BindXattrEmpty() { return BindNull(9); }
918 
919 
920 //------------------------------------------------------------------------------
921 
922 
923 SqlNestedCatalogLookup::SqlNestedCatalogLookup(
924  const CatalogDatabase &database) {
925  // We cannot access nested catalogs where the content hash is missing
926  static const char *stmt_0_9 = "SELECT '', 0 FROM nested_catalogs;";
927  static const char *stmt_2_5_ge_4 =
928  "SELECT sha1, size FROM nested_catalogs WHERE path=:path "
929  "UNION ALL SELECT sha1, size FROM bind_mountpoints WHERE path=:path;";
930  static const char *stmt_2_5_ge_1_lt_4 = "SELECT sha1, size FROM "
931  "nested_catalogs WHERE path=:path;";
932  // Internally converts NULL to 0 for size
933  static const char
934  *stmt_2_5_lt_1 = "SELECT sha1, 0 FROM nested_catalogs WHERE path=:path;";
935 
936  if (database.IsEqualSchema(database.schema_version(), 2.5)
937  && (database.schema_revision() >= 4)) {
938  DeferredInit(database.sqlite_db(), stmt_2_5_ge_4);
939  } else if (database.IsEqualSchema(database.schema_version(), 2.5)
940  && (database.schema_revision() >= 1)) {
941  DeferredInit(database.sqlite_db(), stmt_2_5_ge_1_lt_4);
942  } else {
943  if (database.IsEqualSchema(database.schema_version(), 0.9)) {
944  DeferredInit(database.sqlite_db(), stmt_0_9);
945  } else {
946  DeferredInit(database.sqlite_db(), stmt_2_5_lt_1);
947  }
948  }
949 }
950 
951 
952 bool SqlNestedCatalogLookup::BindSearchPath(const PathString &path) {
953  return BindText(1, path.GetChars(), path.GetLength());
954 }
955 
956 
957 shash::Any SqlNestedCatalogLookup::GetContentHash() const {
958  const string hash = string(reinterpret_cast<const char *>(RetrieveText(0)));
959  return (hash.empty())
962 }
963 
964 
965 uint64_t SqlNestedCatalogLookup::GetSize() const { return RetrieveInt64(1); }
966 
967 
968 //------------------------------------------------------------------------------
969 
970 
971 SqlNestedCatalogListing::SqlNestedCatalogListing(
972  const CatalogDatabase &database) {
973  // We cannot access nested catalogs where the content hash is missing
974  static const char *stmt_0_9 = "SELECT '', '', 0 FROM nested_catalogs;";
975  static const char *stmt_2_5_ge_4 =
976  "SELECT path, sha1, size FROM nested_catalogs "
977  "UNION ALL SELECT path, sha1, size FROM bind_mountpoints;";
978  static const char
979  *stmt_2_5_ge_1_lt_4 = "SELECT path, sha1, size FROM nested_catalogs;";
980  // Internally converts NULL to 0 for size
981  static const char
982  *stmt_2_5_lt_1 = "SELECT path, sha1, 0 FROM nested_catalogs;";
983 
984  if (database.IsEqualSchema(database.schema_version(), 2.5)
985  && (database.schema_revision() >= 4)) {
986  DeferredInit(database.sqlite_db(), stmt_2_5_ge_4);
987  } else if (database.IsEqualSchema(database.schema_version(), 2.5)
988  && (database.schema_revision() >= 1)) {
989  DeferredInit(database.sqlite_db(), stmt_2_5_ge_1_lt_4);
990  } else {
991  if (database.IsEqualSchema(database.schema_version(), 0.9)) {
992  DeferredInit(database.sqlite_db(), stmt_0_9);
993  } else {
994  DeferredInit(database.sqlite_db(), stmt_2_5_lt_1);
995  }
996  }
997 }
998 
999 
1000 PathString SqlNestedCatalogListing::GetPath() const {
1001  const char *path = reinterpret_cast<const char *>(RetrieveText(0));
1002  return PathString(path, strlen(path));
1003 }
1004 
1005 
1006 shash::Any SqlNestedCatalogListing::GetContentHash() const {
1007  const string hash = string(reinterpret_cast<const char *>(RetrieveText(1)));
1008  return (hash.empty())
1011 }
1012 
1013 
1014 uint64_t SqlNestedCatalogListing::GetSize() const { return RetrieveInt64(2); }
1015 
1016 
1017 //------------------------------------------------------------------------------
1018 
1019 
1020 SqlOwnNestedCatalogListing::SqlOwnNestedCatalogListing(
1021  const CatalogDatabase &database) {
1022  // We cannot access nested catalogs where the content hash is missing
1023  static const char *stmt_0_9 = "SELECT '', '', 0 FROM nested_catalogs;";
1024  static const char
1025  *stmt_2_5_ge_1 = "SELECT path, sha1, size FROM nested_catalogs;";
1026  // Internally converts NULL to 0 for size
1027  static const char
1028  *stmt_2_5_lt_1 = "SELECT path, sha1, 0 FROM nested_catalogs;";
1029 
1030  if (database.IsEqualSchema(database.schema_version(), 2.5)
1031  && (database.schema_revision() >= 1)) {
1032  DeferredInit(database.sqlite_db(), stmt_2_5_ge_1);
1033  } else {
1034  if (database.IsEqualSchema(database.schema_version(), 0.9)) {
1035  DeferredInit(database.sqlite_db(), stmt_0_9);
1036  } else {
1037  DeferredInit(database.sqlite_db(), stmt_2_5_lt_1);
1038  }
1039  }
1040 }
1041 
1042 
1043 PathString SqlOwnNestedCatalogListing::GetPath() const {
1044  const char *path = reinterpret_cast<const char *>(RetrieveText(0));
1045  return PathString(path, strlen(path));
1046 }
1047 
1048 
1049 shash::Any SqlOwnNestedCatalogListing::GetContentHash() const {
1050  const string hash = string(reinterpret_cast<const char *>(RetrieveText(1)));
1051  return (hash.empty())
1054 }
1055 
1056 
1057 uint64_t SqlOwnNestedCatalogListing::GetSize() const {
1058  return RetrieveInt64(2);
1059 }
1060 
1061 
1062 //------------------------------------------------------------------------------
1063 
1064 
1065 SqlDirentInsert::SqlDirentInsert(const CatalogDatabase &database) {
1066  DeferredInit(
1067  database.sqlite_db(),
1068  "INSERT INTO catalog "
1069  "(md5path_1, md5path_2, parent_1, parent_2, hash, hardlinks, size, mode,"
1070  // 1 2 3 4 5 6 7 8
1071  "mtime, flags, name, symlink, uid, gid, xattr, mtimens) "
1072  // 9, 10 11 12 13 14 15 16
1073  "VALUES (:md5_1, :md5_2, :p_1, :p_2, :hash, :links, :size, :mode, :mtime,"
1074  " :flags, :name, :symlink, :uid, :gid, :xattr, :mtimens);");
1075 }
1076 
1077 
1078 bool SqlDirentInsert::BindPathHash(const shash::Md5 &hash) {
1079  return BindMd5(1, 2, hash);
1080 }
1081 
1082 
1083 bool SqlDirentInsert::BindParentPathHash(const shash::Md5 &hash) {
1084  return BindMd5(3, 4, hash);
1085 }
1086 
1087 
1088 bool SqlDirentInsert::BindDirent(const DirectoryEntry &entry) {
1089  return BindDirentFields(5, 6, 7, 8, 9, 16, 10, 11, 12, 13, 14, entry);
1090 }
1091 
1092 
1093 bool SqlDirentInsert::BindXattr(const XattrList &xattrs) {
1094  unsigned char *packed_xattrs;
1095  unsigned size;
1096  xattrs.Serialize(&packed_xattrs, &size);
1097  if (packed_xattrs == NULL)
1098  return BindNull(15);
1099  return BindBlobTransient(15, packed_xattrs, size);
1100 }
1101 
1102 
1103 bool SqlDirentInsert::BindXattrEmpty() { return BindNull(15); }
1104 
1105 
1106 //------------------------------------------------------------------------------
1107 
1108 
1109 SqlDirentUpdate::SqlDirentUpdate(const CatalogDatabase &database) {
1110  DeferredInit(database.sqlite_db(),
1111  "UPDATE catalog "
1112  "SET hash = :hash, size = :size, mode = :mode, mtime = :mtime, "
1113  // 1 2 3 4
1114  "flags = :flags, name = :name, symlink = :symlink, hardlinks = "
1115  ":hardlinks, "
1116  // 5 6 7 8
1117  "uid = :uid, gid = :gid, mtimens = :mtimens "
1118  // 9 10 11
1119  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
1120  // 12 13
1121 }
1122 
1123 
1124 bool SqlDirentUpdate::BindPathHash(const shash::Md5 &hash) {
1125  return BindMd5(12, 13, hash);
1126 }
1127 
1128 
1129 bool SqlDirentUpdate::BindDirent(const DirectoryEntry &entry) {
1130  return BindDirentFields(1, 8, 2, 3, 4, 11, 5, 6, 7, 9, 10, entry);
1131 }
1132 
1133 
1134 //------------------------------------------------------------------------------
1135 
1136 
1137 SqlDirentUnlink::SqlDirentUnlink(const CatalogDatabase &database) {
1138  DeferredInit(database.sqlite_db(),
1139  "DELETE FROM catalog "
1140  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
1141 }
1142 
1143 bool SqlDirentUnlink::BindPathHash(const shash::Md5 &hash) {
1144  return BindMd5(1, 2, hash);
1145 }
1146 
1147 
1148 //------------------------------------------------------------------------------
1149 
1150 
1151 SqlIncLinkcount::SqlIncLinkcount(const CatalogDatabase &database) {
1152  // This command changes the linkcount of a whole hardlink group at once!
1153  // We can do this, since the 'hardlinks'-field contains the hardlink group ID
1154  // in the higher 32bit as well as the 'linkcount' in the lower 32bit.
1155  // This field will be equal for all entries belonging to the same hardlink
1156  // group while adding/subtracting small values from it will only effect the
1157  // linkcount in the lower 32bit.
1158  // Take a deep breath!
1159  DeferredInit(database.sqlite_db(),
1160  "UPDATE catalog SET hardlinks = hardlinks + :delta "
1161  "WHERE hardlinks = (SELECT hardlinks from catalog "
1162  "WHERE md5path_1 = :md5_1 AND md5path_2 = :md5_2);");
1163 }
1164 
1165 
1166 bool SqlIncLinkcount::BindPathHash(const shash::Md5 &hash) {
1167  return BindMd5(2, 3, hash);
1168 }
1169 
1170 
1171 bool SqlIncLinkcount::BindDelta(const int delta) { return BindInt(1, delta); }
1172 
1173 
1174 //------------------------------------------------------------------------------
1175 
1176 
1177 SqlChunkInsert::SqlChunkInsert(const CatalogDatabase &database) {
1178  DeferredInit(database.sqlite_db(),
1179  "INSERT INTO chunks (md5path_1, md5path_2, offset, size, hash) "
1180  // 1 2 3 4 5
1181  "VALUES (:md5_1, :md5_2, :offset, :size, :hash);");
1182 }
1183 
1184 
1185 bool SqlChunkInsert::BindPathHash(const shash::Md5 &hash) {
1186  return BindMd5(1, 2, hash);
1187 }
1188 
1189 
1190 bool SqlChunkInsert::BindFileChunk(const FileChunk &chunk) {
1191  return BindInt64(3, chunk.offset()) && BindInt64(4, chunk.size())
1192  && BindHashBlob(5, chunk.content_hash());
1193 }
1194 
1195 
1196 //------------------------------------------------------------------------------
1197 
1198 
1199 SqlChunksRemove::SqlChunksRemove(const CatalogDatabase &database) {
1200  DeferredInit(database.sqlite_db(),
1201  "DELETE FROM chunks "
1202  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
1203 }
1204 
1205 
1206 bool SqlChunksRemove::BindPathHash(const shash::Md5 &hash) {
1207  return BindMd5(1, 2, hash);
1208 }
1209 
1210 
1211 //------------------------------------------------------------------------------
1212 
1213 
1214 SqlChunksListing::SqlChunksListing(const CatalogDatabase &database) {
1215  DeferredInit(database.sqlite_db(),
1216  "SELECT offset, size, hash FROM chunks "
1217  // 0 1 2
1218  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2) "
1219  // 1 2
1220  "ORDER BY offset ASC;");
1221 }
1222 
1223 
1224 bool SqlChunksListing::BindPathHash(const shash::Md5 &hash) {
1225  return BindMd5(1, 2, hash);
1226 }
1227 
1228 
1229 FileChunk SqlChunksListing::GetFileChunk(
1230  const shash::Algorithms interpret_hash_as) const {
1231  return FileChunk(
1232  RetrieveHashBlob(2, interpret_hash_as, shash::kSuffixPartial),
1233  RetrieveInt64(0),
1234  RetrieveInt64(1));
1235 }
1236 
1237 
1238 //------------------------------------------------------------------------------
1239 
1240 
1241 SqlChunksCount::SqlChunksCount(const CatalogDatabase &database) {
1242  DeferredInit(database.sqlite_db(),
1243  "SELECT count(*) FROM chunks "
1244  // 0
1245  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2)");
1246  // 1 2
1247 }
1248 
1249 
1250 bool SqlChunksCount::BindPathHash(const shash::Md5 &hash) {
1251  return BindMd5(1, 2, hash);
1252 }
1253 
1254 
1255 int SqlChunksCount::GetChunkCount() const { return RetrieveInt64(0); }
1256 
1257 
1258 //------------------------------------------------------------------------------
1259 
1260 
1261 SqlMaxHardlinkGroup::SqlMaxHardlinkGroup(const CatalogDatabase &database) {
1262  DeferredInit(database.sqlite_db(), "SELECT max(hardlinks) FROM catalog;");
1263 }
1264 
1265 
1266 uint32_t SqlMaxHardlinkGroup::GetMaxGroupId() const {
1267  return RetrieveInt64(0) >> 32;
1268 }
1269 
1270 
1271 //------------------------------------------------------------------------------
1272 
1273 
1274 SqlGetCounter::SqlGetCounter(const CatalogDatabase &database) {
1275  static const char
1276  *stmt_ge_2_4 = "SELECT value from statistics WHERE counter = :counter;";
1277  static const char *stmt_lt_2_4 = "SELECT 0;";
1278 
1279  if (database.schema_version() >= 2.4 - CatalogDatabase::kSchemaEpsilon) {
1280  compat_ = false;
1281  DeferredInit(database.sqlite_db(), stmt_ge_2_4);
1282  } else {
1283  compat_ = true;
1284  DeferredInit(database.sqlite_db(), stmt_lt_2_4);
1285  }
1286 }
1287 
1288 
1289 bool SqlGetCounter::BindCounter(const std::string &counter) {
1290  if (compat_)
1291  return true;
1292  return BindText(1, counter);
1293 }
1294 
1295 
1296 uint64_t SqlGetCounter::GetCounter() const {
1297  if (compat_)
1298  return 0;
1299  return RetrieveInt64(0);
1300 }
1301 
1302 
1303 //------------------------------------------------------------------------------
1304 
1305 
1306 SqlUpdateCounter::SqlUpdateCounter(const CatalogDatabase &database) {
1307  DeferredInit(
1308  database.sqlite_db(),
1309  "UPDATE statistics SET value=value+:val WHERE counter=:counter;");
1310 }
1311 
1312 
1313 bool SqlUpdateCounter::BindCounter(const std::string &counter) {
1314  return BindText(2, counter);
1315 }
1316 
1317 
1318 bool SqlUpdateCounter::BindDelta(const int64_t delta) {
1319  return BindInt64(1, delta);
1320 }
1321 
1322 
1323 //------------------------------------------------------------------------------
1324 
1325 
1326 SqlCreateCounter::SqlCreateCounter(const CatalogDatabase &database) {
1327  DeferredInit(database.sqlite_db(),
1328  "INSERT OR REPLACE INTO statistics (counter, value) "
1329  "VALUES (:counter, :value);");
1330 }
1331 
1332 
1333 bool SqlCreateCounter::BindCounter(const std::string &counter) {
1334  return BindText(1, counter);
1335 }
1336 
1337 
1338 bool SqlCreateCounter::BindInitialValue(const int64_t value) {
1339  return BindInt64(2, value);
1340 }
1341 
1342 
1343 //------------------------------------------------------------------------------
1344 
1345 
1346 SqlAllChunks::SqlAllChunks(const CatalogDatabase &database) {
1347  const int hash_mask = 7 << SqlDirent::kFlagPosHash;
1348  const string flags2hash = " ((flags&" + StringifyInt(hash_mask) + ") >> " +
1349  StringifyInt(SqlDirent::kFlagPosHash) +
1350  ")+1 AS hash_algorithm ";
1351 
1352  const int compression_mask = 7 << SqlDirent::kFlagPosCompression;
1353  const string flags2compression =
1354  " ((flags&" + StringifyInt(compression_mask) + ") >> " +
1355  StringifyInt(SqlDirent::kFlagPosCompression) + ") " +
1356  "AS compression_algorithm ";
1357 
1358  // TODO(reneme): this depends on shash::kSuffix* being a char!
1359  // it should be more generic or replaced entirely
1360  // TODO(reneme): this is practically the same as SqlListContentHashes and
1361  // should be consolidated
1362  string sql = "SELECT DISTINCT hash, "
1363  "CASE WHEN flags & "
1364  + StringifyInt(SqlDirent::kFlagFile) + " THEN "
1365  + StringifyInt(shash::kSuffixNone) + " " + "WHEN flags & "
1366  + StringifyInt(SqlDirent::kFlagDir) + " THEN "
1368  + "AS chunk_type, " + flags2hash + "," + flags2compression
1369  + "FROM catalog WHERE (hash IS NOT NULL) AND "
1370  "(flags & "
1371  + StringifyInt(SqlDirent::kFlagFileExternal) + " = 0)";
1372  if (database.schema_version() >= 2.4 - CatalogDatabase::kSchemaEpsilon) {
1373  sql += " UNION "
1374  "SELECT DISTINCT chunks.hash, "
1375  + StringifyInt(shash::kSuffixPartial) + ", " + flags2hash + ","
1376  + flags2compression
1377  + "FROM chunks, catalog WHERE "
1378  "chunks.md5path_1=catalog.md5path_1 AND "
1379  "chunks.md5path_2=catalog.md5path_2 AND "
1380  "(catalog.flags & "
1381  + StringifyInt(SqlDirent::kFlagFileExternal) + " = 0)";
1382  }
1383  sql += ";";
1384  Init(database.sqlite_db(), sql);
1385 }
1386 
1387 
1388 bool SqlAllChunks::Open() { return true; }
1389 
1390 
1391 bool SqlAllChunks::Next(shash::Any *hash, zlib::Algorithms *compression_alg) {
1392  if (!FetchRow()) {
1393  return false;
1394  }
1395 
1396  *hash = RetrieveHashBlob(0, static_cast<shash::Algorithms>(RetrieveInt(2)),
1397  static_cast<shash::Suffix>(RetrieveInt(1)));
1398  *compression_alg = static_cast<zlib::Algorithms>(RetrieveInt(3));
1399  return true;
1400 }
1401 
1402 
1403 bool SqlAllChunks::Close() { return Reset(); }
1404 
1405 
1406 //------------------------------------------------------------------------------
1407 
1408 
1409 SqlLookupXattrs::SqlLookupXattrs(const CatalogDatabase &database) {
1410  DeferredInit(database.sqlite_db(),
1411  "SELECT xattr FROM catalog "
1412  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
1413 }
1414 
1415 
1416 bool SqlLookupXattrs::BindPathHash(const shash::Md5 &hash) {
1417  return BindMd5(1, 2, hash);
1418 }
1419 
1420 
1421 XattrList SqlLookupXattrs::GetXattrs() {
1422  const unsigned char *packed_xattrs = reinterpret_cast<const unsigned char *>(
1423  RetrieveBlob(0));
1424  if (packed_xattrs == NULL)
1425  return XattrList();
1426 
1427  const int size = RetrieveBytes(0);
1428  assert(size >= 0);
1429  const UniquePtr<XattrList> xattrs(
1430  XattrList::Deserialize(packed_xattrs, size));
1431  if (!xattrs.IsValid()) {
1432  LogCvmfs(kLogCatalog, kLogDebug, "corrupted xattr data");
1433  return XattrList();
1434  }
1435  return *xattrs;
1436 }
1437 
1438 } // namespace catalog
bool InsertIntoDatabase(const CatalogDatabase &database) const
bool IsExternalFile() const
bool IsNull() const
Definition: hash.h:371
bool IsSpecial() const
bool Execute()
Definition: sql.cc:41
bool IsDirectory() const
bool FetchRow()
Definition: sql.cc:61
uint64_t MapUid(const uint64_t uid) const
Definition: catalog.h:215
bool IsHidden() const
bool HasMtimeNs() const
bool IsChunkedFile() const
inode_t inode_
bool is_hidden_
bool BindDirent(const DirectoryEntry &entry)
void Assign(const char *chars, const unsigned length)
Definition: shortstring.h:61
void Open()
double RetrieveDouble(const int idx_column) const
Definition: sql.h:434
gid_t gid_
const char kSuffixMicroCatalog
Definition: hash.h:56
const shash::Any & content_hash() const
Definition: file_chunk.h:37
float schema() const
Definition: catalog.h:174
assert((mem||(size==0))&&"Out Of Memory")
int32_t mtime_ns_
float schema_version() const
Definition: sql.h:147
bool IsDirectIo() const
#define MAKE_STATEMENTS(STMT_TMPL)
Definition: catalog_sql.cc:709
Algorithms algorithm
Definition: hash.h:122
bool has_xattrs_
uint64_t size_
unsigned schema_revision() const
Definition: sql.h:148
#define DEFERRED_INITS(DB)
Definition: catalog_sql.cc:717
bool IsNestedCatalogMountpoint() const
bool BindPathHash(const shash::Md5 &hash)
Algorithms
Definition: hash.h:41
bool IsNestedCatalogRoot() const
NameString name_
zlib::Algorithms compression_algorithm_
bool IsLink() const
Algorithms
Definition: compression.h:44
gid_t g_gid
Definition: globals.cc:14
const char kSuffixPartial
Definition: hash.h:57
void Serialize(unsigned char **outbuf, unsigned *size, const std::vector< std::string > *blacklist=NULL) const
Definition: xattr.cc:199
const char kSuffixCatalog
Definition: hash.h:54
uint32_t linkcount_
off_t offset() const
Definition: file_chunk.h:38
bool is_nested_catalog_mountpoint_
bool is_external_file_
zlib::Algorithms compression_algorithm() const
uid_t g_uid
Definition: globals.cc:13
uint32_t hardlink_group_
time_t mtime_
static int Init(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:2320
void Append(const char *chars, const unsigned length)
Definition: shortstring.h:80
string StringifyInt(const int64_t value)
Definition: string.cc:77
sqlite3 * sqlite_db() const
Definition: sql.h:145
bool IsBindMountpoint() const
bool is_chunked_file_
bool g_raw_symlinks
Definition: globals.cc:12
bool IsEqualSchema(const float value, const float compare) const
Definition: sql.h:129
bool g_world_readable
Definition: globals.cc:15
inode_t GetMangledInode(const uint64_t row_id, const uint64_t hardlink_group) const
Definition: catalog.cc:583
static XattrList * Deserialize(const unsigned char *inbuf, const unsigned size)
Definition: xattr.cc:69
unsigned int mode_
ShortString< kDefaultMaxPath, 0 > PathString
Definition: shortstring.h:213
size_t size() const
Definition: file_chunk.h:39
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:14
Any MkFromHexPtr(const HexPtr hex, const char suffix)
Definition: hash.cc:82
unsigned GetLength() const
Definition: shortstring.h:131
shash::Any checksum_
Suffix suffix
Definition: hash.h:123
bool is_direct_io_
bool IsNegative() const
bool is_nested_catalog_root_
const char * GetChars() const
Definition: shortstring.h:123
bool is_bind_mountpoint_
LinkString symlink_
bool BindParentPathHash(const shash::Md5 &hash)
static void size_t size
Definition: smalloc.h:54
const char kSuffixNone
Definition: hash.h:53
bool g_claim_ownership
Definition: globals.cc:11
const shash::Any * checksum_ptr() const
uint64_t MapGid(const uint64_t gid) const
Definition: catalog.h:221
uid_t uid_
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545