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  shash::Md5 root_path_hash = shash::Md5(shash::AsciiPtr(root_path));
308  shash::Md5 root_parent_hash = (root_path == "")
309  ? shash::Md5()
311  GetParentPath(root_path)));
312 
313  // Start initial filling transaction
314  retval = BeginTransaction();
315  if (!retval) {
316  PrintSqlError("failed to enter initial filling transaction");
317  return false;
318  }
319 
320  // Insert initial values to properties
321  if (!this->SetProperty("revision", 0)) {
322  PrintSqlError(
323  "failed to insert default initial values into the newly created "
324  "catalog tables.");
325  return false;
326  }
327 
328  if (volatile_content) {
329  if (!this->SetProperty("volatile", 1)) {
330  PrintSqlError("failed to insert volatile flag into the newly created "
331  "catalog tables.");
332  return false;
333  }
334  }
335 
336  if (!voms_authz.empty()) {
337  if (!SetVOMSAuthz(voms_authz)) {
338  PrintSqlError("failed to insert VOMS authz flag into the newly created "
339  "catalog tables.");
340  return false;
341  }
342  }
343 
344  // Create initial statistics counters
345  catalog::Counters counters;
346 
347  // Insert root entry (when given)
348  if (!root_entry.IsNegative()) {
349  SqlDirentInsert sql_insert(*this);
350  retval = sql_insert.BindPathHash(root_path_hash)
351  && sql_insert.BindParentPathHash(root_parent_hash)
352  && sql_insert.BindDirent(root_entry) && sql_insert.Execute();
353  if (!retval) {
354  PrintSqlError("failed to insert root entry into newly created catalog.");
355  return false;
356  }
357 
358  // account for the created root entry
359  counters.self.directories = 1;
360  }
361 
362  // Save initial statistics counters
363  if (!counters.InsertIntoDatabase(*this)) {
364  PrintSqlError("failed to insert initial catalog statistics counters.");
365  return false;
366  }
367 
368  // Insert root path (when given)
369  if (!root_path.empty()) {
370  if (!this->SetProperty("root_prefix", root_path)) {
371  PrintSqlError(
372  "failed to store root prefix in the newly created catalog.");
373  return false;
374  }
375  }
376 
377  // Set creation timestamp
378  if (!this->SetProperty("last_modified", static_cast<uint64_t>(time(NULL)))) {
379  PrintSqlError("failed to store creation timestamp in the new catalog.");
380  return false;
381  }
382 
383  // Commit initial filling transaction
384  retval = CommitTransaction();
385  if (!retval) {
386  PrintSqlError("failed to commit initial filling transaction");
387  return false;
388  }
389 
390  return true;
391 }
392 
393 
394 bool CatalogDatabase::SetVOMSAuthz(const std::string &voms_authz) {
395  return this->SetProperty("voms_authz", voms_authz);
396 }
397 
398 
399 double CatalogDatabase::GetRowIdWasteRatio() const {
400  SqlCatalog rowid_waste_ratio_query(
401  *this,
402  "SELECT 1.0 - CAST(COUNT(*) AS DOUBLE) / MAX(rowid) "
403  "AS ratio FROM catalog;");
404  const bool retval = rowid_waste_ratio_query.FetchRow();
405  assert(retval);
406 
407  return rowid_waste_ratio_query.RetrieveDouble(0);
408 }
409 
430 bool CatalogDatabase::CompactDatabase() const {
431  assert(read_write());
432 
433  return SqlCatalog(*this, "PRAGMA foreign_keys = OFF;").Execute()
434  && BeginTransaction()
435  && SqlCatalog(*this, "CREATE TEMPORARY TABLE duplicate AS "
436  " SELECT * FROM catalog "
437  " ORDER BY rowid ASC;")
438  .Execute()
439  && SqlCatalog(*this, "DELETE FROM catalog;").Execute()
440  && SqlCatalog(*this, "INSERT INTO catalog "
441  " SELECT * FROM duplicate "
442  " ORDER BY rowid")
443  .Execute()
444  && SqlCatalog(*this, "DROP TABLE duplicate;").Execute()
445  && CommitTransaction()
446  && SqlCatalog(*this, "PRAGMA foreign_keys = ON;").Execute();
447 }
448 
449 
450 //------------------------------------------------------------------------------
451 
452 
453 unsigned SqlDirent::CreateDatabaseFlags(const DirectoryEntry &entry) const {
454  unsigned int database_flags = 0;
455 
456  if (entry.IsNestedCatalogRoot())
457  database_flags |= kFlagDirNestedRoot;
458  else if (entry.IsNestedCatalogMountpoint())
459  database_flags |= kFlagDirNestedMountpoint;
460  else if (entry.IsBindMountpoint())
461  database_flags |= kFlagDirBindMountpoint;
462 
463  if (entry.IsDirectory()) {
464  database_flags |= kFlagDir;
465  } else if (entry.IsLink()) {
466  database_flags |= kFlagFile | kFlagLink;
467  } else if (entry.IsSpecial()) {
468  database_flags |= kFlagFile | kFlagFileSpecial;
469  } else {
470  database_flags |= kFlagFile;
471  database_flags |= entry.compression_algorithm() << kFlagPosCompression;
472  if (entry.IsChunkedFile())
473  database_flags |= kFlagFileChunk;
474  if (entry.IsExternalFile())
475  database_flags |= kFlagFileExternal;
476  if (entry.IsDirectIo())
477  database_flags |= kFlagDirectIo;
478  }
479 
480  if (!entry.checksum_ptr()->IsNull() || entry.IsChunkedFile())
481  StoreHashAlgorithm(entry.checksum_ptr()->algorithm, &database_flags);
482 
483  if (entry.IsHidden())
484  database_flags |= kFlagHidden;
485 
486  return database_flags;
487 }
488 
489 
490 void SqlDirent::StoreHashAlgorithm(const shash::Algorithms algo,
491  unsigned *flags) const {
492  assert(algo != shash::kAny);
493  // Md5 unusable for content hashes
494  *flags |= (algo - 1) << kFlagPosHash;
495 }
496 
497 
498 shash::Algorithms SqlDirent::RetrieveHashAlgorithm(const unsigned flags) const {
499  unsigned in_flags = ((7 << kFlagPosHash) & flags) >> kFlagPosHash;
500  // Skip Md5
501  in_flags++;
502  assert(in_flags < shash::kAny);
503  return static_cast<shash::Algorithms>(in_flags);
504 }
505 
506 
507 zlib::Algorithms SqlDirent::RetrieveCompressionAlgorithm(
508  const unsigned flags) const {
509  // 3 bits, so use 7 (111) to only pull out the flags we want
510  unsigned in_flags = ((7 << kFlagPosCompression) & flags)
511  >> kFlagPosCompression;
512  return static_cast<zlib::Algorithms>(in_flags);
513 }
514 
515 
516 uint32_t SqlDirent::Hardlinks2Linkcount(const uint64_t hardlinks) const {
517  return (hardlinks << 32) >> 32;
518 }
519 
520 
521 uint32_t SqlDirent::Hardlinks2HardlinkGroup(const uint64_t hardlinks) const {
522  return hardlinks >> 32;
523 }
524 
525 
526 uint64_t SqlDirent::MakeHardlinks(const uint32_t hardlink_group,
527  const uint32_t linkcount) const {
528  assert(linkcount > 0);
529  return (static_cast<uint64_t>(hardlink_group) << 32) | linkcount;
530 }
531 
532 
537 void SqlDirent::ExpandSymlink(LinkString *raw_symlink) const {
538  const char *c = raw_symlink->GetChars();
539  const char *cEnd = c + raw_symlink->GetLength();
540  for (; c < cEnd; ++c) {
541  if (*c == '$')
542  goto expand_symlink;
543  }
544  return;
545 
546 expand_symlink:
547  LinkString result;
548  for (c = raw_symlink->GetChars(); c < cEnd; ++c) {
549  if ((*c == '$') && (c < cEnd - 2) && (*(c + 1) == '(')) {
550  c += 2;
551  const char *rpar = c;
552  while (rpar < cEnd) {
553  if (*rpar == ')')
554  goto expand_symlink_getenv;
555  rpar++;
556  }
557  // right parenthesis missing
558  result.Append("$(", 2);
559  result.Append(c, 1);
560  continue;
561 
562  expand_symlink_getenv:
563  // Check for default value
564  const char *default_separator = c;
565  const char *default_value = rpar;
566  while (default_separator != rpar) {
567  if ((*default_separator == ':') && (*(default_separator + 1) == '-')) {
568  default_value = default_separator + 2;
569  break;
570  }
571  default_separator++;
572  }
573 
574  const unsigned environ_var_length = default_separator - c;
575  char environ_var[environ_var_length + 1];
576  environ_var[environ_var_length] = '\0';
577  memcpy(environ_var, c, environ_var_length);
578  const char *environ_value = getenv(environ_var); // Don't free!
579  if (environ_value) {
580  result.Append(environ_value, strlen(environ_value));
581  } else {
582  const unsigned default_length = rpar - default_value;
583  result.Append(default_value, default_length);
584  }
585  c = rpar;
586  continue;
587  }
588  result.Append(c, 1);
589  }
590  raw_symlink->Assign(result);
591  return;
592 }
593 
594 
595 //------------------------------------------------------------------------------
596 
597 
598 bool SqlDirentWrite::BindDirentFields(const int hash_idx,
599  const int hardlinks_idx,
600  const int size_idx,
601  const int mode_idx,
602  const int mtime_idx,
603  const int mtimens_idx,
604  const int flags_idx,
605  const int name_idx,
606  const int symlink_idx,
607  const int uid_idx,
608  const int gid_idx,
609  const DirectoryEntry &entry) {
610  const uint64_t hardlinks = MakeHardlinks(entry.hardlink_group_,
611  entry.linkcount_);
612 
613  bool result = BindHashBlob(hash_idx, entry.checksum_)
614  && BindInt64(hardlinks_idx, hardlinks)
615  && BindInt64(size_idx, entry.size_)
616  && BindInt(mode_idx, entry.mode_)
617  && BindInt64(uid_idx, entry.uid_)
618  && BindInt64(gid_idx, entry.gid_)
619  && BindInt64(mtime_idx, entry.mtime_)
620  && BindInt(flags_idx, CreateDatabaseFlags(entry))
621  && BindText(name_idx, entry.name_.GetChars(),
622  static_cast<int>(entry.name_.GetLength()))
623  && BindText(symlink_idx, entry.symlink_.GetChars(),
624  static_cast<int>(entry.symlink_.GetLength()));
625  if (entry.HasMtimeNs()) {
626  result &= BindInt(mtimens_idx, entry.mtime_ns_);
627  } else {
628  result &= BindNull(mtimens_idx);
629  }
630 
631  return result;
632 }
633 
634 
635 //------------------------------------------------------------------------------
636 
637 
638 SqlListContentHashes::SqlListContentHashes(const CatalogDatabase &database) {
639  static const char *stmt_lt_2_4 = "SELECT hash, flags, 0 "
640  " FROM catalog "
641  " WHERE length(hash) > 0;";
642 
643  static const char
644  *stmt_ge_2_4 = "SELECT hash, flags, 0 "
645  " FROM catalog "
646  " WHERE (length(catalog.hash) > 0) AND "
647  " ((flags & 128) = 0) " // kFlagFileExternal
648  "UNION "
649  "SELECT chunks.hash, catalog.flags, 1 "
650  " FROM catalog "
651  " LEFT JOIN chunks "
652  " ON catalog.md5path_1 = chunks.md5path_1 AND "
653  " catalog.md5path_2 = chunks.md5path_2 "
654  " WHERE (catalog.flags & 128) = 0;"; // kFlagFileExternal
655 
656  if (database.schema_version() < 2.4 - CatalogDatabase::kSchemaEpsilon) {
657  DeferredInit(database.sqlite_db(), stmt_lt_2_4);
658  } else {
659  DeferredInit(database.sqlite_db(), stmt_ge_2_4);
660  }
661 }
662 
663 
664 shash::Any SqlListContentHashes::GetHash() const {
665  const unsigned int db_flags = RetrieveInt(1);
666  const shash::Algorithms hash_algorithm = RetrieveHashAlgorithm(db_flags);
667  shash::Any hash = RetrieveHashBlob(0, hash_algorithm);
668  if (RetrieveInt(2) == 1) {
670  }
671 
672  return hash;
673 }
674 
675 
676 //------------------------------------------------------------------------------
677 
678 #define DB_FIELDS_LT_V2_1 \
679  "catalog.hash, catalog.inode, catalog.size, " \
680  "catalog.mode, catalog.mtime, catalog.flags, " \
681  "catalog.name, catalog.symlink, catalog.md5path_1, " \
682  "catalog.md5path_2, catalog.parent_1, catalog.parent_2, " \
683  "catalog.rowid"
684 #define DB_FIELDS_GE_V2_1_LT_R2 \
685  "catalog.hash, catalog.hardlinks, catalog.size, " \
686  "catalog.mode, catalog.mtime, catalog.flags, " \
687  "catalog.name, catalog.symlink, catalog.md5path_1, " \
688  "catalog.md5path_2, catalog.parent_1, catalog.parent_2, " \
689  "catalog.rowid, catalog.uid, catalog.gid, " \
690  "0, NULL"
691 #define DB_FIELDS_GE_V2_1_LT_R7 \
692  "catalog.hash, catalog.hardlinks, catalog.size, " \
693  "catalog.mode, catalog.mtime, catalog.flags, " \
694  "catalog.name, catalog.symlink, catalog.md5path_1, " \
695  "catalog.md5path_2, catalog.parent_1, catalog.parent_2, " \
696  "catalog.rowid, catalog.uid, catalog.gid, " \
697  "catalog.xattr IS NOT NULL, NULL"
698 #define DB_FIELDS_GE_V2_1_GE_R7 \
699  "catalog.hash, catalog.hardlinks, catalog.size, " \
700  "catalog.mode, catalog.mtime, catalog.flags, " \
701  "catalog.name, catalog.symlink, catalog.md5path_1, " \
702  "catalog.md5path_2, catalog.parent_1, catalog.parent_2, " \
703  "catalog.rowid, catalog.uid, catalog.gid, " \
704  "catalog.xattr IS NOT NULL, catalog.mtimens"
705 
706 #define MAKE_STATEMENT(STMT_TMPL, REV) \
707  static const std::string REV = ReplaceAll(STMT_TMPL, "@DB_FIELDS@", \
708  DB_FIELDS_##REV)
709 
710 #define MAKE_STATEMENTS(STMT_TMPL) \
711  MAKE_STATEMENT(STMT_TMPL, LT_V2_1); \
712  MAKE_STATEMENT(STMT_TMPL, GE_V2_1_LT_R2); \
713  MAKE_STATEMENT(STMT_TMPL, GE_V2_1_LT_R7); \
714  MAKE_STATEMENT(STMT_TMPL, GE_V2_1_GE_R7)
715 
716 #define DEFERRED_INIT(DB, REV) DeferredInit((DB).sqlite_db(), (REV).c_str())
717 
718 #define DEFERRED_INITS(DB) \
719  if ((DB).schema_version() < 2.1 - CatalogDatabase::kSchemaEpsilon) { \
720  DEFERRED_INIT((DB), LT_V2_1); \
721  } else if ((DB).schema_revision() < 2) { \
722  DEFERRED_INIT((DB), GE_V2_1_LT_R2); \
723  } else if ((DB).schema_revision() < 7) { \
724  DEFERRED_INIT((DB), GE_V2_1_LT_R7); \
725  } else { \
726  DEFERRED_INIT((DB), GE_V2_1_GE_R7); \
727  }
728 
729 
730 shash::Md5 SqlLookup::GetPathHash() const { return RetrieveMd5(8, 9); }
731 
732 
733 shash::Md5 SqlLookup::GetParentPathHash() const { return RetrieveMd5(10, 11); }
734 
735 
739 DirectoryEntry SqlLookup::GetDirent(const Catalog *catalog,
740  const bool expand_symlink) const {
741  DirectoryEntry result;
742 
743  const unsigned database_flags = RetrieveInt(5);
744  result.is_nested_catalog_root_ = (database_flags & kFlagDirNestedRoot);
745  result.is_nested_catalog_mountpoint_ = (database_flags
746  & kFlagDirNestedMountpoint);
747  const char *name = reinterpret_cast<const char *>(RetrieveText(6));
748  const char *symlink = reinterpret_cast<const char *>(RetrieveText(7));
749 
750  // Retrieve the hardlink information from the hardlinks database field
751  if (catalog->schema() < 2.1 - CatalogDatabase::kSchemaEpsilon) {
752  result.linkcount_ = 1;
753  result.hardlink_group_ = 0;
754  result.inode_ = catalog->GetMangledInode(RetrieveInt64(12), 0);
755  result.is_chunked_file_ = false;
756  result.has_xattrs_ = false;
757  result.checksum_ = RetrieveHashBlob(0, shash::kSha1);
758  result.uid_ = g_uid;
759  result.gid_ = g_gid;
760  } else {
761  const uint64_t hardlinks = RetrieveInt64(1);
762  result.linkcount_ = Hardlinks2Linkcount(hardlinks);
763  result.hardlink_group_ = Hardlinks2HardlinkGroup(hardlinks);
764  result.inode_ = catalog->GetMangledInode(RetrieveInt64(12),
765  result.hardlink_group_);
766  result.is_bind_mountpoint_ = (database_flags & kFlagDirBindMountpoint);
767  result.is_chunked_file_ = (database_flags & kFlagFileChunk);
768  result.is_hidden_ = (database_flags & kFlagHidden);
769  result.is_direct_io_ = (database_flags & kFlagDirectIo);
770  result.is_external_file_ = (database_flags & kFlagFileExternal);
771  result.has_xattrs_ = RetrieveInt(15) != 0;
772  result.mtime_ns_ = RetrieveNullableInt(16, -1);
773  result.checksum_ = RetrieveHashBlob(0,
774  RetrieveHashAlgorithm(database_flags));
775  result.compression_algorithm_ = RetrieveCompressionAlgorithm(
776  database_flags);
777 
778  if (g_claim_ownership) {
779  result.uid_ = g_uid;
780  result.gid_ = g_gid;
781  } else {
782  result.uid_ = catalog->MapUid(RetrieveInt64(13));
783  result.gid_ = catalog->MapGid(RetrieveInt64(14));
784  }
785  }
786 
787  result.mode_ = RetrieveInt(3);
788  result.size_ = RetrieveInt64(2);
789  result.mtime_ = RetrieveInt64(4);
790  result.name_.Assign(name, strlen(name));
791  result.symlink_.Assign(symlink, strlen(symlink));
792  if (expand_symlink && !g_raw_symlinks)
793  ExpandSymlink(&result.symlink_);
794 
795  if (g_world_readable) {
796  if (S_ISDIR(result.mode_)) {
797  result.mode_ |= 0555;
798  } else {
799  result.mode_ |= 0444;
800  }
801  }
802 
803  return result;
804 }
805 
806 
807 //------------------------------------------------------------------------------
808 
809 
810 SqlListing::SqlListing(const CatalogDatabase &database) {
811  MAKE_STATEMENTS("SELECT @DB_FIELDS@ FROM catalog "
812  "WHERE (parent_1 = :p_1) AND (parent_2 = :p_2);");
813  DEFERRED_INITS(database);
814 }
815 
816 
817 bool SqlListing::BindPathHash(const struct shash::Md5 &hash) {
818  return BindMd5(1, 2, hash);
819 }
820 
821 
822 //------------------------------------------------------------------------------
823 
824 
825 SqlLookupPathHash::SqlLookupPathHash(const CatalogDatabase &database) {
826  MAKE_STATEMENTS("SELECT @DB_FIELDS@ FROM catalog "
827  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
828  DEFERRED_INITS(database);
829 }
830 
831 bool SqlLookupPathHash::BindPathHash(const struct shash::Md5 &hash) {
832  return BindMd5(1, 2, hash);
833 }
834 
835 
836 //------------------------------------------------------------------------------
837 
838 
839 SqlLookupInode::SqlLookupInode(const CatalogDatabase &database) {
840  MAKE_STATEMENTS("SELECT @DB_FIELDS@ FROM catalog WHERE rowid = :rowid;");
841  DEFERRED_INITS(database);
842 }
843 
844 
845 bool SqlLookupInode::BindRowId(const uint64_t inode) {
846  return BindInt64(1, inode);
847 }
848 
849 
850 //------------------------------------------------------------------------------
851 
852 
853 SqlLookupDanglingMountpoints::SqlLookupDanglingMountpoints(
854  const catalog::CatalogDatabase &database) {
855  MAKE_STATEMENTS("SELECT DISTINCT @DB_FIELDS@ FROM catalog "
856  "JOIN catalog AS c2 "
857  "ON catalog.md5path_1 = c2.parent_1 AND "
858  " catalog.md5path_2 = c2.parent_2 "
859  "WHERE catalog.flags & :nested_mountpoint_flag");
860  DEFERRED_INITS(database);
861 
862  // this pretty much removes the advantage of a deferred init but the statement
863  // is anyway only used directly.
864  const bool success = BindInt64(1, SqlDirent::kFlagDirNestedMountpoint);
865  assert(success);
866 }
867 
868 
869 //------------------------------------------------------------------------------
870 
871 
872 SqlDirentTouch::SqlDirentTouch(const CatalogDatabase &database) {
873  DeferredInit(database.sqlite_db(),
874  "UPDATE catalog "
875  "SET hash = :hash, size = :size, mode = :mode, mtime = :mtime, "
876  // 1 2 3 4
877  "name = :name, symlink = :symlink, uid = :uid, gid = :gid, "
878  "xattr = :xattr, "
879  // 5 6 7 8 9
880  "mtimens = :mtimens "
881  // 10
882  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
883  // 11 12
884 }
885 
886 
887 bool SqlDirentTouch::BindDirentBase(const DirectoryEntryBase &entry) {
888  bool result = BindHashBlob(1, entry.checksum_) && BindInt64(2, entry.size_)
889  && BindInt(3, entry.mode_) && BindInt64(4, entry.mtime_)
890  && BindText(5, entry.name_.GetChars(), entry.name_.GetLength())
891  && BindText(6, entry.symlink_.GetChars(),
892  entry.symlink_.GetLength())
893  && BindInt64(7, entry.uid_) && BindInt64(8, entry.gid_);
894  if (entry.HasMtimeNs()) {
895  result &= BindInt(10, entry.mtime_ns_);
896  } else {
897  result &= BindNull(10);
898  }
899  return result;
900 }
901 
902 
903 bool SqlDirentTouch::BindPathHash(const shash::Md5 &hash) {
904  return BindMd5(11, 12, hash);
905 }
906 
907 
908 bool SqlDirentTouch::BindXattr(const XattrList &xattrs) {
909  unsigned char *packed_xattrs;
910  unsigned size;
911  xattrs.Serialize(&packed_xattrs, &size);
912  if (packed_xattrs == NULL)
913  return BindNull(9);
914  return BindBlobTransient(9, packed_xattrs, size);
915 }
916 
917 
918 bool SqlDirentTouch::BindXattrEmpty() { return BindNull(9); }
919 
920 
921 //------------------------------------------------------------------------------
922 
923 
924 SqlNestedCatalogLookup::SqlNestedCatalogLookup(
925  const CatalogDatabase &database) {
926  // We cannot access nested catalogs where the content hash is missing
927  static const char *stmt_0_9 = "SELECT '', 0 FROM nested_catalogs;";
928  static const char *stmt_2_5_ge_4 =
929  "SELECT sha1, size FROM nested_catalogs WHERE path=:path "
930  "UNION ALL SELECT sha1, size FROM bind_mountpoints WHERE path=:path;";
931  static const char *stmt_2_5_ge_1_lt_4 = "SELECT sha1, size FROM "
932  "nested_catalogs WHERE path=:path;";
933  // Internally converts NULL to 0 for size
934  static const char
935  *stmt_2_5_lt_1 = "SELECT sha1, 0 FROM nested_catalogs WHERE path=:path;";
936 
937  if (database.IsEqualSchema(database.schema_version(), 2.5)
938  && (database.schema_revision() >= 4)) {
939  DeferredInit(database.sqlite_db(), stmt_2_5_ge_4);
940  } else if (database.IsEqualSchema(database.schema_version(), 2.5)
941  && (database.schema_revision() >= 1)) {
942  DeferredInit(database.sqlite_db(), stmt_2_5_ge_1_lt_4);
943  } else {
944  if (database.IsEqualSchema(database.schema_version(), 0.9)) {
945  DeferredInit(database.sqlite_db(), stmt_0_9);
946  } else {
947  DeferredInit(database.sqlite_db(), stmt_2_5_lt_1);
948  }
949  }
950 }
951 
952 
953 bool SqlNestedCatalogLookup::BindSearchPath(const PathString &path) {
954  return BindText(1, path.GetChars(), path.GetLength());
955 }
956 
957 
958 shash::Any SqlNestedCatalogLookup::GetContentHash() const {
959  const string hash = string(reinterpret_cast<const char *>(RetrieveText(0)));
960  return (hash.empty())
963 }
964 
965 
966 uint64_t SqlNestedCatalogLookup::GetSize() const { return RetrieveInt64(1); }
967 
968 
969 //------------------------------------------------------------------------------
970 
971 
972 SqlNestedCatalogListing::SqlNestedCatalogListing(
973  const CatalogDatabase &database) {
974  // We cannot access nested catalogs where the content hash is missing
975  static const char *stmt_0_9 = "SELECT '', '', 0 FROM nested_catalogs;";
976  static const char *stmt_2_5_ge_4 =
977  "SELECT path, sha1, size FROM nested_catalogs "
978  "UNION ALL SELECT path, sha1, size FROM bind_mountpoints;";
979  static const char
980  *stmt_2_5_ge_1_lt_4 = "SELECT path, sha1, size FROM nested_catalogs;";
981  // Internally converts NULL to 0 for size
982  static const char
983  *stmt_2_5_lt_1 = "SELECT path, sha1, 0 FROM nested_catalogs;";
984 
985  if (database.IsEqualSchema(database.schema_version(), 2.5)
986  && (database.schema_revision() >= 4)) {
987  DeferredInit(database.sqlite_db(), stmt_2_5_ge_4);
988  } else if (database.IsEqualSchema(database.schema_version(), 2.5)
989  && (database.schema_revision() >= 1)) {
990  DeferredInit(database.sqlite_db(), stmt_2_5_ge_1_lt_4);
991  } else {
992  if (database.IsEqualSchema(database.schema_version(), 0.9)) {
993  DeferredInit(database.sqlite_db(), stmt_0_9);
994  } else {
995  DeferredInit(database.sqlite_db(), stmt_2_5_lt_1);
996  }
997  }
998 }
999 
1000 
1001 PathString SqlNestedCatalogListing::GetPath() const {
1002  const char *path = reinterpret_cast<const char *>(RetrieveText(0));
1003  return PathString(path, strlen(path));
1004 }
1005 
1006 
1007 shash::Any SqlNestedCatalogListing::GetContentHash() const {
1008  const string hash = string(reinterpret_cast<const char *>(RetrieveText(1)));
1009  return (hash.empty())
1012 }
1013 
1014 
1015 uint64_t SqlNestedCatalogListing::GetSize() const { return RetrieveInt64(2); }
1016 
1017 
1018 //------------------------------------------------------------------------------
1019 
1020 
1021 SqlOwnNestedCatalogListing::SqlOwnNestedCatalogListing(
1022  const CatalogDatabase &database) {
1023  // We cannot access nested catalogs where the content hash is missing
1024  static const char *stmt_0_9 = "SELECT '', '', 0 FROM nested_catalogs;";
1025  static const char
1026  *stmt_2_5_ge_1 = "SELECT path, sha1, size FROM nested_catalogs;";
1027  // Internally converts NULL to 0 for size
1028  static const char
1029  *stmt_2_5_lt_1 = "SELECT path, sha1, 0 FROM nested_catalogs;";
1030 
1031  if (database.IsEqualSchema(database.schema_version(), 2.5)
1032  && (database.schema_revision() >= 1)) {
1033  DeferredInit(database.sqlite_db(), stmt_2_5_ge_1);
1034  } else {
1035  if (database.IsEqualSchema(database.schema_version(), 0.9)) {
1036  DeferredInit(database.sqlite_db(), stmt_0_9);
1037  } else {
1038  DeferredInit(database.sqlite_db(), stmt_2_5_lt_1);
1039  }
1040  }
1041 }
1042 
1043 
1044 PathString SqlOwnNestedCatalogListing::GetPath() const {
1045  const char *path = reinterpret_cast<const char *>(RetrieveText(0));
1046  return PathString(path, strlen(path));
1047 }
1048 
1049 
1050 shash::Any SqlOwnNestedCatalogListing::GetContentHash() const {
1051  const string hash = string(reinterpret_cast<const char *>(RetrieveText(1)));
1052  return (hash.empty())
1055 }
1056 
1057 
1058 uint64_t SqlOwnNestedCatalogListing::GetSize() const {
1059  return RetrieveInt64(2);
1060 }
1061 
1062 
1063 //------------------------------------------------------------------------------
1064 
1065 
1066 SqlDirentInsert::SqlDirentInsert(const CatalogDatabase &database) {
1067  DeferredInit(
1068  database.sqlite_db(),
1069  "INSERT INTO catalog "
1070  "(md5path_1, md5path_2, parent_1, parent_2, hash, hardlinks, size, mode,"
1071  // 1 2 3 4 5 6 7 8
1072  "mtime, flags, name, symlink, uid, gid, xattr, mtimens) "
1073  // 9, 10 11 12 13 14 15 16
1074  "VALUES (:md5_1, :md5_2, :p_1, :p_2, :hash, :links, :size, :mode, :mtime,"
1075  " :flags, :name, :symlink, :uid, :gid, :xattr, :mtimens);");
1076 }
1077 
1078 
1079 bool SqlDirentInsert::BindPathHash(const shash::Md5 &hash) {
1080  return BindMd5(1, 2, hash);
1081 }
1082 
1083 
1084 bool SqlDirentInsert::BindParentPathHash(const shash::Md5 &hash) {
1085  return BindMd5(3, 4, hash);
1086 }
1087 
1088 
1089 bool SqlDirentInsert::BindDirent(const DirectoryEntry &entry) {
1090  return BindDirentFields(5, 6, 7, 8, 9, 16, 10, 11, 12, 13, 14, entry);
1091 }
1092 
1093 
1094 bool SqlDirentInsert::BindXattr(const XattrList &xattrs) {
1095  unsigned char *packed_xattrs;
1096  unsigned size;
1097  xattrs.Serialize(&packed_xattrs, &size);
1098  if (packed_xattrs == NULL)
1099  return BindNull(15);
1100  return BindBlobTransient(15, packed_xattrs, size);
1101 }
1102 
1103 
1104 bool SqlDirentInsert::BindXattrEmpty() { return BindNull(15); }
1105 
1106 
1107 //------------------------------------------------------------------------------
1108 
1109 
1110 SqlDirentUpdate::SqlDirentUpdate(const CatalogDatabase &database) {
1111  DeferredInit(database.sqlite_db(),
1112  "UPDATE catalog "
1113  "SET hash = :hash, size = :size, mode = :mode, mtime = :mtime, "
1114  // 1 2 3 4
1115  "flags = :flags, name = :name, symlink = :symlink, hardlinks = "
1116  ":hardlinks, "
1117  // 5 6 7 8
1118  "uid = :uid, gid = :gid, mtimens = :mtimens "
1119  // 9 10 11
1120  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
1121  // 12 13
1122 }
1123 
1124 
1125 bool SqlDirentUpdate::BindPathHash(const shash::Md5 &hash) {
1126  return BindMd5(12, 13, hash);
1127 }
1128 
1129 
1130 bool SqlDirentUpdate::BindDirent(const DirectoryEntry &entry) {
1131  return BindDirentFields(1, 8, 2, 3, 4, 11, 5, 6, 7, 9, 10, entry);
1132 }
1133 
1134 
1135 //------------------------------------------------------------------------------
1136 
1137 
1138 SqlDirentUnlink::SqlDirentUnlink(const CatalogDatabase &database) {
1139  DeferredInit(database.sqlite_db(),
1140  "DELETE FROM catalog "
1141  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
1142 }
1143 
1144 bool SqlDirentUnlink::BindPathHash(const shash::Md5 &hash) {
1145  return BindMd5(1, 2, hash);
1146 }
1147 
1148 
1149 //------------------------------------------------------------------------------
1150 
1151 
1152 SqlIncLinkcount::SqlIncLinkcount(const CatalogDatabase &database) {
1153  // This command changes the linkcount of a whole hardlink group at once!
1154  // We can do this, since the 'hardlinks'-field contains the hardlink group ID
1155  // in the higher 32bit as well as the 'linkcount' in the lower 32bit.
1156  // This field will be equal for all entries belonging to the same hardlink
1157  // group while adding/subtracting small values from it will only effect the
1158  // linkcount in the lower 32bit.
1159  // Take a deep breath!
1160  DeferredInit(database.sqlite_db(),
1161  "UPDATE catalog SET hardlinks = hardlinks + :delta "
1162  "WHERE hardlinks = (SELECT hardlinks from catalog "
1163  "WHERE md5path_1 = :md5_1 AND md5path_2 = :md5_2);");
1164 }
1165 
1166 
1167 bool SqlIncLinkcount::BindPathHash(const shash::Md5 &hash) {
1168  return BindMd5(2, 3, hash);
1169 }
1170 
1171 
1172 bool SqlIncLinkcount::BindDelta(const int delta) { return BindInt(1, delta); }
1173 
1174 
1175 //------------------------------------------------------------------------------
1176 
1177 
1178 SqlChunkInsert::SqlChunkInsert(const CatalogDatabase &database) {
1179  DeferredInit(database.sqlite_db(),
1180  "INSERT INTO chunks (md5path_1, md5path_2, offset, size, hash) "
1181  // 1 2 3 4 5
1182  "VALUES (:md5_1, :md5_2, :offset, :size, :hash);");
1183 }
1184 
1185 
1186 bool SqlChunkInsert::BindPathHash(const shash::Md5 &hash) {
1187  return BindMd5(1, 2, hash);
1188 }
1189 
1190 
1191 bool SqlChunkInsert::BindFileChunk(const FileChunk &chunk) {
1192  return BindInt64(3, chunk.offset()) && BindInt64(4, chunk.size())
1193  && BindHashBlob(5, chunk.content_hash());
1194 }
1195 
1196 
1197 //------------------------------------------------------------------------------
1198 
1199 
1200 SqlChunksRemove::SqlChunksRemove(const CatalogDatabase &database) {
1201  DeferredInit(database.sqlite_db(),
1202  "DELETE FROM chunks "
1203  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
1204 }
1205 
1206 
1207 bool SqlChunksRemove::BindPathHash(const shash::Md5 &hash) {
1208  return BindMd5(1, 2, hash);
1209 }
1210 
1211 
1212 //------------------------------------------------------------------------------
1213 
1214 
1215 SqlChunksListing::SqlChunksListing(const CatalogDatabase &database) {
1216  DeferredInit(database.sqlite_db(),
1217  "SELECT offset, size, hash FROM chunks "
1218  // 0 1 2
1219  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2) "
1220  // 1 2
1221  "ORDER BY offset ASC;");
1222 }
1223 
1224 
1225 bool SqlChunksListing::BindPathHash(const shash::Md5 &hash) {
1226  return BindMd5(1, 2, hash);
1227 }
1228 
1229 
1230 FileChunk SqlChunksListing::GetFileChunk(
1231  const shash::Algorithms interpret_hash_as) const {
1232  return FileChunk(
1233  RetrieveHashBlob(2, interpret_hash_as, shash::kSuffixPartial),
1234  RetrieveInt64(0),
1235  RetrieveInt64(1));
1236 }
1237 
1238 
1239 //------------------------------------------------------------------------------
1240 
1241 
1242 SqlChunksCount::SqlChunksCount(const CatalogDatabase &database) {
1243  DeferredInit(database.sqlite_db(),
1244  "SELECT count(*) FROM chunks "
1245  // 0
1246  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2)");
1247  // 1 2
1248 }
1249 
1250 
1251 bool SqlChunksCount::BindPathHash(const shash::Md5 &hash) {
1252  return BindMd5(1, 2, hash);
1253 }
1254 
1255 
1256 int SqlChunksCount::GetChunkCount() const { return RetrieveInt64(0); }
1257 
1258 
1259 //------------------------------------------------------------------------------
1260 
1261 
1262 SqlMaxHardlinkGroup::SqlMaxHardlinkGroup(const CatalogDatabase &database) {
1263  DeferredInit(database.sqlite_db(), "SELECT max(hardlinks) FROM catalog;");
1264 }
1265 
1266 
1267 uint32_t SqlMaxHardlinkGroup::GetMaxGroupId() const {
1268  return RetrieveInt64(0) >> 32;
1269 }
1270 
1271 
1272 //------------------------------------------------------------------------------
1273 
1274 
1275 SqlGetCounter::SqlGetCounter(const CatalogDatabase &database) {
1276  static const char
1277  *stmt_ge_2_4 = "SELECT value from statistics WHERE counter = :counter;";
1278  static const char *stmt_lt_2_4 = "SELECT 0;";
1279 
1280  if (database.schema_version() >= 2.4 - CatalogDatabase::kSchemaEpsilon) {
1281  compat_ = false;
1282  DeferredInit(database.sqlite_db(), stmt_ge_2_4);
1283  } else {
1284  compat_ = true;
1285  DeferredInit(database.sqlite_db(), stmt_lt_2_4);
1286  }
1287 }
1288 
1289 
1290 bool SqlGetCounter::BindCounter(const std::string &counter) {
1291  if (compat_)
1292  return true;
1293  return BindText(1, counter);
1294 }
1295 
1296 
1297 uint64_t SqlGetCounter::GetCounter() const {
1298  if (compat_)
1299  return 0;
1300  return RetrieveInt64(0);
1301 }
1302 
1303 
1304 //------------------------------------------------------------------------------
1305 
1306 
1307 SqlUpdateCounter::SqlUpdateCounter(const CatalogDatabase &database) {
1308  DeferredInit(
1309  database.sqlite_db(),
1310  "UPDATE statistics SET value=value+:val WHERE counter=:counter;");
1311 }
1312 
1313 
1314 bool SqlUpdateCounter::BindCounter(const std::string &counter) {
1315  return BindText(2, counter);
1316 }
1317 
1318 
1319 bool SqlUpdateCounter::BindDelta(const int64_t delta) {
1320  return BindInt64(1, delta);
1321 }
1322 
1323 
1324 //------------------------------------------------------------------------------
1325 
1326 
1327 SqlCreateCounter::SqlCreateCounter(const CatalogDatabase &database) {
1328  DeferredInit(database.sqlite_db(),
1329  "INSERT OR REPLACE INTO statistics (counter, value) "
1330  "VALUES (:counter, :value);");
1331 }
1332 
1333 
1334 bool SqlCreateCounter::BindCounter(const std::string &counter) {
1335  return BindText(1, counter);
1336 }
1337 
1338 
1339 bool SqlCreateCounter::BindInitialValue(const int64_t value) {
1340  return BindInt64(2, value);
1341 }
1342 
1343 
1344 //------------------------------------------------------------------------------
1345 
1346 
1347 SqlAllChunks::SqlAllChunks(const CatalogDatabase &database) {
1348  int hash_mask = 7 << SqlDirent::kFlagPosHash;
1349  string flags2hash = " ((flags&" + StringifyInt(hash_mask) + ") >> "
1350  + StringifyInt(SqlDirent::kFlagPosHash)
1351  + ")+1 AS hash_algorithm ";
1352 
1353  int compression_mask = 7 << SqlDirent::kFlagPosCompression;
1354  string flags2compression = " ((flags&" + StringifyInt(compression_mask)
1355  + ") >> "
1356  + StringifyInt(SqlDirent::kFlagPosCompression)
1357  + ") " + "AS compression_algorithm ";
1358 
1359  // TODO(reneme): this depends on shash::kSuffix* being a char!
1360  // it should be more generic or replaced entirely
1361  // TODO(reneme): this is practically the same as SqlListContentHashes and
1362  // should be consolidated
1363  string sql = "SELECT DISTINCT hash, "
1364  "CASE WHEN flags & "
1365  + StringifyInt(SqlDirent::kFlagFile) + " THEN "
1366  + StringifyInt(shash::kSuffixNone) + " " + "WHEN flags & "
1367  + StringifyInt(SqlDirent::kFlagDir) + " THEN "
1369  + "AS chunk_type, " + flags2hash + "," + flags2compression
1370  + "FROM catalog WHERE (hash IS NOT NULL) AND "
1371  "(flags & "
1372  + StringifyInt(SqlDirent::kFlagFileExternal) + " = 0)";
1373  if (database.schema_version() >= 2.4 - CatalogDatabase::kSchemaEpsilon) {
1374  sql += " UNION "
1375  "SELECT DISTINCT chunks.hash, "
1376  + StringifyInt(shash::kSuffixPartial) + ", " + flags2hash + ","
1377  + flags2compression
1378  + "FROM chunks, catalog WHERE "
1379  "chunks.md5path_1=catalog.md5path_1 AND "
1380  "chunks.md5path_2=catalog.md5path_2 AND "
1381  "(catalog.flags & "
1382  + StringifyInt(SqlDirent::kFlagFileExternal) + " = 0)";
1383  }
1384  sql += ";";
1385  Init(database.sqlite_db(), sql);
1386 }
1387 
1388 
1389 bool SqlAllChunks::Open() { return true; }
1390 
1391 
1392 bool SqlAllChunks::Next(shash::Any *hash, zlib::Algorithms *compression_alg) {
1393  if (!FetchRow()) {
1394  return false;
1395  }
1396 
1397  *hash = RetrieveHashBlob(0, static_cast<shash::Algorithms>(RetrieveInt(2)),
1398  static_cast<shash::Suffix>(RetrieveInt(1)));
1399  *compression_alg = static_cast<zlib::Algorithms>(RetrieveInt(3));
1400  return true;
1401 }
1402 
1403 
1404 bool SqlAllChunks::Close() { return Reset(); }
1405 
1406 
1407 //------------------------------------------------------------------------------
1408 
1409 
1410 SqlLookupXattrs::SqlLookupXattrs(const CatalogDatabase &database) {
1411  DeferredInit(database.sqlite_db(),
1412  "SELECT xattr FROM catalog "
1413  "WHERE (md5path_1 = :md5_1) AND (md5path_2 = :md5_2);");
1414 }
1415 
1416 
1417 bool SqlLookupXattrs::BindPathHash(const shash::Md5 &hash) {
1418  return BindMd5(1, 2, hash);
1419 }
1420 
1421 
1422 XattrList SqlLookupXattrs::GetXattrs() {
1423  const unsigned char *packed_xattrs = reinterpret_cast<const unsigned char *>(
1424  RetrieveBlob(0));
1425  if (packed_xattrs == NULL)
1426  return XattrList();
1427 
1428  int size = RetrieveBytes(0);
1429  assert(size >= 0);
1430  UniquePtr<XattrList> xattrs(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:710
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:718
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:198
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:2309
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