FSTLevelDBMigrations.mm 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /*
  2. * Copyright 2018 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "Firestore/Source/Local/FSTLevelDBMigrations.h"
  17. #include "leveldb/write_batch.h"
  18. #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
  19. #import "Firestore/Source/Local/FSTLevelDB.h"
  20. #import "Firestore/Source/Local/FSTLevelDBKey.h"
  21. #import "Firestore/Source/Local/FSTLevelDBQueryCache.h"
  22. #import "Firestore/Source/Local/FSTWriteGroup.h"
  23. #import "Firestore/Source/Util/FSTAssert.h"
  24. NS_ASSUME_NONNULL_BEGIN
  25. // Current version of the schema defined in this file.
  26. static FSTLevelDBSchemaVersion kSchemaVersion = 2;
  27. using leveldb::DB;
  28. using leveldb::Iterator;
  29. using leveldb::Status;
  30. using leveldb::Slice;
  31. using leveldb::WriteOptions;
  32. /**
  33. * Ensures that the global singleton target metadata row exists in LevelDB.
  34. * @param db The db in which to require the row.
  35. */
  36. static void EnsureTargetGlobal(std::shared_ptr<DB> db, FSTWriteGroup *group) {
  37. FSTPBTargetGlobal *targetGlobal = [FSTLevelDBQueryCache readTargetMetadataFromDB:db];
  38. if (!targetGlobal) {
  39. [group setMessage:[FSTPBTargetGlobal message] forKey:[FSTLevelDBTargetGlobalKey key]];
  40. }
  41. }
  42. /**
  43. * Save the given version number as the current version of the schema of the database.
  44. * @param version The version to save
  45. * @param group The transaction in which to save the new version number
  46. */
  47. static void SaveVersion(FSTLevelDBSchemaVersion version, FSTWriteGroup *group) {
  48. std::string key = [FSTLevelDBVersionKey key];
  49. std::string version_string = std::to_string(version);
  50. [group setData:version_string forKey:key];
  51. }
  52. /**
  53. * This function counts the number of targets that currently exist in the given db. It
  54. * then reads the target global row, adds the count to the metadata from that row, and writes
  55. * the metadata back.
  56. *
  57. * It assumes the metadata has already been written and is able to be read in this transaction.
  58. */
  59. static void AddTargetCount(std::shared_ptr<DB> db, FSTWriteGroup *group) {
  60. std::unique_ptr<Iterator> it(db->NewIterator([FSTLevelDB standardReadOptions]));
  61. Slice start_key = [FSTLevelDBTargetKey keyPrefix];
  62. it->Seek(start_key);
  63. int32_t count = 0;
  64. while (it->Valid() && it->key().starts_with(start_key)) {
  65. count++;
  66. it->Next();
  67. }
  68. FSTPBTargetGlobal *targetGlobal = [FSTLevelDBQueryCache readTargetMetadataFromDB:db];
  69. FSTCAssert(targetGlobal != nil,
  70. @"We should have a metadata row as it was added in an earlier migration");
  71. targetGlobal.targetCount = count;
  72. [group setMessage:targetGlobal forKey:[FSTLevelDBTargetGlobalKey key]];
  73. }
  74. @implementation FSTLevelDBMigrations
  75. + (FSTLevelDBSchemaVersion)schemaVersionForDB:(std::shared_ptr<DB>)db {
  76. std::string key = [FSTLevelDBVersionKey key];
  77. std::string version_string;
  78. Status status = db->Get([FSTLevelDB standardReadOptions], key, &version_string);
  79. if (status.IsNotFound()) {
  80. return 0;
  81. } else {
  82. return stoi(version_string);
  83. }
  84. }
  85. + (void)runMigrationsOnDB:(std::shared_ptr<DB>)db {
  86. FSTWriteGroup *group = [FSTWriteGroup groupWithAction:@"Migrations"];
  87. FSTLevelDBSchemaVersion currentVersion = [self schemaVersionForDB:db];
  88. // Each case in this switch statement intentionally falls through. This lets us
  89. // start at the current schema version and apply any migrations that have not yet
  90. // been applied, to bring us up to current, as defined by the kSchemaVersion constant.
  91. switch (currentVersion) {
  92. case 0:
  93. EnsureTargetGlobal(db, group);
  94. // Fallthrough
  95. case 1:
  96. // We need to make sure we have metadata, since we're going to read and modify it
  97. // in this migration. Commit the current transaction and start a new one. Since we're
  98. // committing, we need to save a version. It's safe to save this one, if we crash
  99. // after saving we'll resume from this step when we try to migrate.
  100. SaveVersion(1, group);
  101. [group writeToDB:db];
  102. group = [FSTWriteGroup groupWithAction:@"Migrations"];
  103. AddTargetCount(db, group);
  104. // Fallthrough
  105. default:
  106. if (currentVersion < kSchemaVersion) {
  107. SaveVersion(kSchemaVersion, group);
  108. }
  109. }
  110. if (!group.isEmpty) {
  111. [group writeToDB:db];
  112. }
  113. }
  114. @end
  115. NS_ASSUME_NONNULL_END