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