FSTLevelDBMigrations.mm 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  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. #import "Firestore/Source/Local/FSTLevelDBMigrations.h"
  17. #include <string>
  18. #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
  19. #import "Firestore/Source/Local/FSTLevelDBKey.h"
  20. #import "Firestore/Source/Local/FSTLevelDBQueryCache.h"
  21. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  22. #include "absl/strings/match.h"
  23. #include "leveldb/write_batch.h"
  24. NS_ASSUME_NONNULL_BEGIN
  25. // Current version of the schema defined in this file.
  26. static FSTLevelDBSchemaVersion kSchemaVersion = 2;
  27. using firebase::firestore::local::LevelDbTransaction;
  28. using leveldb::DB;
  29. using leveldb::Iterator;
  30. using leveldb::Status;
  31. using leveldb::Slice;
  32. using leveldb::WriteOptions;
  33. /**
  34. * Ensures that the global singleton target metadata row exists in LevelDB.
  35. */
  36. static void EnsureTargetGlobal(LevelDbTransaction *transaction) {
  37. FSTPBTargetGlobal *targetGlobal =
  38. [FSTLevelDBQueryCache readTargetMetadataWithTransaction:transaction];
  39. if (!targetGlobal) {
  40. transaction->Put([FSTLevelDBTargetGlobalKey key], [FSTPBTargetGlobal message]);
  41. }
  42. }
  43. /**
  44. * Save the given version number as the current version of the schema of the database.
  45. * @param version The version to save
  46. * @param transaction The transaction in which to save the new version number
  47. */
  48. static void SaveVersion(FSTLevelDBSchemaVersion version, LevelDbTransaction *transaction) {
  49. std::string key = [FSTLevelDBVersionKey key];
  50. std::string version_string = std::to_string(version);
  51. transaction->Put(key, version_string);
  52. }
  53. /**
  54. * This function counts the number of targets that currently exist in the given db. It
  55. * then reads the target global row, adds the count to the metadata from that row, and writes
  56. * the metadata back.
  57. *
  58. * It assumes the metadata has already been written and is able to be read in this transaction.
  59. */
  60. static void AddTargetCount(LevelDbTransaction *transaction) {
  61. auto it = transaction->NewIterator();
  62. std::string start_key = [FSTLevelDBTargetKey keyPrefix];
  63. it->Seek(start_key);
  64. int32_t count = 0;
  65. while (it->Valid() && absl::StartsWith(it->key(), start_key)) {
  66. count++;
  67. it->Next();
  68. }
  69. FSTPBTargetGlobal *targetGlobal =
  70. [FSTLevelDBQueryCache readTargetMetadataWithTransaction:transaction];
  71. HARD_ASSERT(targetGlobal != nil,
  72. "We should have a metadata row as it was added in an earlier migration");
  73. targetGlobal.targetCount = count;
  74. transaction->Put([FSTLevelDBTargetGlobalKey key], targetGlobal);
  75. }
  76. @implementation FSTLevelDBMigrations
  77. + (FSTLevelDBSchemaVersion)schemaVersionWithTransaction:
  78. (firebase::firestore::local::LevelDbTransaction *)transaction {
  79. std::string key = [FSTLevelDBVersionKey key];
  80. std::string version_string;
  81. Status status = transaction->Get(key, &version_string);
  82. if (status.IsNotFound()) {
  83. return 0;
  84. } else {
  85. return stoi(version_string);
  86. }
  87. }
  88. + (void)runMigrationsWithTransaction:(firebase::firestore::local::LevelDbTransaction *)transaction {
  89. FSTLevelDBSchemaVersion currentVersion = [self schemaVersionWithTransaction:transaction];
  90. // Each case in this switch statement intentionally falls through. This lets us
  91. // start at the current schema version and apply any migrations that have not yet
  92. // been applied, to bring us up to current, as defined by the kSchemaVersion constant.
  93. switch (currentVersion) {
  94. case 0:
  95. EnsureTargetGlobal(transaction);
  96. // Fallthrough
  97. case 1:
  98. // We're now guaranteed that the target global exists. We can safely add a count to it.
  99. AddTargetCount(transaction);
  100. // Fallthrough
  101. default:
  102. if (currentVersion < kSchemaVersion) {
  103. SaveVersion(kSchemaVersion, transaction);
  104. }
  105. }
  106. }
  107. @end
  108. NS_ASSUME_NONNULL_END