RLMMigration.mm 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMMigration_Private.h"
  19. #import "RLMAccessor.h"
  20. #import "RLMObject_Private.h"
  21. #import "RLMObject_Private.hpp"
  22. #import "RLMObjectSchema_Private.hpp"
  23. #import "RLMObjectStore.h"
  24. #import "RLMProperty_Private.h"
  25. #import "RLMRealm_Dynamic.h"
  26. #import "RLMRealm_Private.hpp"
  27. #import "RLMResults_Private.hpp"
  28. #import "RLMSchema_Private.hpp"
  29. #import "RLMUtil.hpp"
  30. #import "object_store.hpp"
  31. #import "shared_realm.hpp"
  32. #import "schema.hpp"
  33. #import <realm/table.hpp>
  34. using namespace realm;
  35. // The source realm for a migration has to use a SharedGroup to be able to share
  36. // the file with the destination realm, but we don't want to let the user call
  37. // beginWriteTransaction on it as that would make no sense.
  38. @interface RLMMigrationRealm : RLMRealm
  39. @end
  40. @implementation RLMMigrationRealm
  41. - (BOOL)readonly {
  42. return YES;
  43. }
  44. - (void)beginWriteTransaction {
  45. @throw RLMException(@"Cannot modify the source Realm in a migration");
  46. }
  47. @end
  48. @implementation RLMMigration {
  49. realm::Schema *_schema;
  50. std::unordered_map<NSString *, realm::IndexSet> _deletedObjectIndices;
  51. }
  52. - (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema {
  53. self = [super init];
  54. if (self) {
  55. _realm = realm;
  56. _oldRealm = oldRealm;
  57. _schema = &schema;
  58. object_setClass(_oldRealm, RLMMigrationRealm.class);
  59. }
  60. return self;
  61. }
  62. - (RLMSchema *)oldSchema {
  63. return self.oldRealm.schema;
  64. }
  65. - (RLMSchema *)newSchema {
  66. return self.realm.schema;
  67. }
  68. - (void)enumerateObjects:(NSString *)className block:(__attribute__((noescape)) RLMObjectMigrationBlock)block {
  69. RLMResults *objects = [_realm.schema schemaForClassName:className] ? [_realm allObjects:className] : nil;
  70. RLMResults *oldObjects = [_oldRealm.schema schemaForClassName:className] ? [_oldRealm allObjects:className] : nil;
  71. // For whatever reason if this is a newly added table we enumerate the
  72. // objects in it, while in all other cases we enumerate only the existing
  73. // objects. It's unclear how this could be useful, but changing it would
  74. // also be a pointless breaking change and it's unlikely to be hurting anyone.
  75. if (objects && !oldObjects) {
  76. for (auto i = objects.count; i > 0; --i) {
  77. @autoreleasepool {
  78. block(nil, objects[i - 1]);
  79. }
  80. }
  81. return;
  82. }
  83. auto count = oldObjects.count;
  84. if (count == 0) {
  85. return;
  86. }
  87. auto deletedObjects = _deletedObjectIndices.find(className);
  88. for (auto i = count; i > 0; --i) {
  89. auto index = i - 1;
  90. if (deletedObjects != _deletedObjectIndices.end() && deletedObjects->second.contains(index)) {
  91. continue;
  92. }
  93. @autoreleasepool {
  94. block(oldObjects[index], objects[index]);
  95. }
  96. }
  97. }
  98. - (void)execute:(RLMMigrationBlock)block {
  99. @autoreleasepool {
  100. // disable all primary keys for migration and use DynamicObject for all types
  101. for (RLMObjectSchema *objectSchema in _realm.schema.objectSchema) {
  102. objectSchema.accessorClass = RLMDynamicObject.class;
  103. objectSchema.primaryKeyProperty.isPrimary = NO;
  104. }
  105. for (RLMObjectSchema *objectSchema in _oldRealm.schema.objectSchema) {
  106. objectSchema.accessorClass = RLMDynamicObject.class;
  107. }
  108. block(self, _oldRealm->_realm->schema_version());
  109. [self deleteObjectsMarkedForDeletion];
  110. _oldRealm = nil;
  111. _realm = nil;
  112. }
  113. }
  114. - (RLMObject *)createObject:(NSString *)className withValue:(id)value {
  115. return [_realm createObject:className withValue:value];
  116. }
  117. - (RLMObject *)createObject:(NSString *)className withObject:(id)object {
  118. return [self createObject:className withValue:object];
  119. }
  120. - (void)deleteObject:(RLMObject *)object {
  121. _deletedObjectIndices[object.objectSchema.className].add(object->_row.get_index());
  122. }
  123. - (void)deleteObjectsMarkedForDeletion {
  124. for (auto& objectType : _deletedObjectIndices) {
  125. TableRef table = ObjectStore::table_for_object_type(_realm.group, objectType.first.UTF8String);
  126. if (!table) {
  127. continue;
  128. }
  129. auto& indices = objectType.second;
  130. // Just clear the table if we're removing all of the rows
  131. if (table->size() == indices.count()) {
  132. table->clear();
  133. }
  134. // Otherwise delete in reverse order to avoid invaliding any of the
  135. // not-yet-deleted indices
  136. else {
  137. for (auto it = std::make_reverse_iterator(indices.end()), end = std::make_reverse_iterator(indices.begin()); it != end; ++it) {
  138. for (size_t i = it->second; i > it->first; --i) {
  139. table->move_last_over(i - 1);
  140. }
  141. }
  142. }
  143. }
  144. }
  145. - (BOOL)deleteDataForClassName:(NSString *)name {
  146. if (!name) {
  147. return false;
  148. }
  149. TableRef table = ObjectStore::table_for_object_type(_realm.group, name.UTF8String);
  150. if (!table) {
  151. return false;
  152. }
  153. _deletedObjectIndices[name].set(table->size());
  154. if (![_realm.schema schemaForClassName:name]) {
  155. realm::ObjectStore::delete_data_for_object(_realm.group, name.UTF8String);
  156. }
  157. return true;
  158. }
  159. - (void)renamePropertyForClass:(NSString *)className oldName:(NSString *)oldName newName:(NSString *)newName {
  160. realm::ObjectStore::rename_property(_realm.group, *_schema, className.UTF8String,
  161. oldName.UTF8String, newName.UTF8String);
  162. }
  163. @end