FSTLevelDBMigrations.mm 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  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 <absl/strings/match.h>
  18. #include "leveldb/write_batch.h"
  19. #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
  20. #import "Firestore/Source/Local/FSTLevelDBKey.h"
  21. #import "Firestore/Source/Local/FSTLevelDBQueryCache.h"
  22. #import "Firestore/Source/Util/FSTAssert.h"
  23. NS_ASSUME_NONNULL_BEGIN
  24. // Current version of the schema defined in this file.
  25. static FSTLevelDBSchemaVersion kSchemaVersion = 2;
  26. using firebase::firestore::local::LevelDbTransaction;
  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. */
  35. static void EnsureTargetGlobal(LevelDbTransaction *transaction) {
  36. FSTPBTargetGlobal *targetGlobal =
  37. [FSTLevelDBQueryCache readTargetMetadataWithTransaction:transaction];
  38. if (!targetGlobal) {
  39. transaction->Put([FSTLevelDBTargetGlobalKey key], [FSTPBTargetGlobal message]);
  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 transaction The transaction in which to save the new version number
  46. */
  47. static void SaveVersion(FSTLevelDBSchemaVersion version, LevelDbTransaction *transaction) {
  48. std::string key = [FSTLevelDBVersionKey key];
  49. std::string version_string = std::to_string(version);
  50. transaction->Put(key, version_string);
  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(LevelDbTransaction *transaction) {
  60. auto it = transaction->NewIterator();
  61. std::string start_key = [FSTLevelDBTargetKey keyPrefix];
  62. it->Seek(start_key);
  63. int32_t count = 0;
  64. while (it->Valid() && absl::StartsWith(it->key(), start_key)) {
  65. count++;
  66. it->Next();
  67. }
  68. FSTPBTargetGlobal *targetGlobal =
  69. [FSTLevelDBQueryCache readTargetMetadataWithTransaction:transaction];
  70. FSTCAssert(targetGlobal != nil,
  71. @"We should have a metadata row as it was added in an earlier migration");
  72. targetGlobal.targetCount = count;
  73. transaction->Put([FSTLevelDBTargetGlobalKey key], targetGlobal);
  74. }
  75. @implementation FSTLevelDBMigrations
  76. + (FSTLevelDBSchemaVersion)schemaVersionWithTransaction:
  77. (firebase::firestore::local::LevelDbTransaction *)transaction {
  78. std::string key = [FSTLevelDBVersionKey key];
  79. std::string version_string;
  80. Status status = transaction->Get(key, &version_string);
  81. if (status.IsNotFound()) {
  82. return 0;
  83. } else {
  84. return stoi(version_string);
  85. }
  86. }
  87. + (void)runMigrationsWithTransaction:(firebase::firestore::local::LevelDbTransaction *)transaction {
  88. FSTLevelDBSchemaVersion currentVersion = [self schemaVersionWithTransaction:transaction];
  89. // Each case in this switch statement intentionally falls through. This lets us
  90. // start at the current schema version and apply any migrations that have not yet
  91. // been applied, to bring us up to current, as defined by the kSchemaVersion constant.
  92. switch (currentVersion) {
  93. case 0:
  94. EnsureTargetGlobal(transaction);
  95. // Fallthrough
  96. case 1:
  97. // We're now guaranteed that the target global exists. We can safely add a count to it.
  98. AddTargetCount(transaction);
  99. // Fallthrough
  100. default:
  101. if (currentVersion < kSchemaVersion) {
  102. SaveVersion(kSchemaVersion, transaction);
  103. }
  104. }
  105. }
  106. @end
  107. NS_ASSUME_NONNULL_END