1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2014 Realm Inc.
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
17 ////////////////////////////////////////////////////////////////////////////
19 #import "RLMMigration_Private.h"
21 #import "RLMAccessor.h"
22 #import "RLMObject_Private.h"
23 #import "RLMObject_Private.hpp"
24 #import "RLMObjectSchema_Private.hpp"
25 #import "RLMObjectStore.h"
26 #import "RLMProperty_Private.h"
27 #import "RLMRealm_Dynamic.h"
28 #import "RLMRealm_Private.hpp"
29 #import "RLMResults_Private.hpp"
30 #import "RLMSchema_Private.hpp"
33 #import "object_store.hpp"
34 #import "shared_realm.hpp"
37 #import <realm/table.hpp>
39 using namespace realm;
41 // The source realm for a migration has to use a SharedGroup to be able to share
42 // the file with the destination realm, but we don't want to let the user call
43 // beginWriteTransaction on it as that would make no sense.
44 @interface RLMMigrationRealm : RLMRealm
47 @implementation RLMMigrationRealm
52 - (void)beginWriteTransaction {
53 @throw RLMException(@"Cannot modify the source Realm in a migration");
57 @implementation RLMMigration {
58 realm::Schema *_schema;
59 NSMutableDictionary *deletedObjectIndices;
60 NSMutableSet *deletedClasses;
63 - (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema {
69 object_setClass(_oldRealm, RLMMigrationRealm.class);
70 deletedObjectIndices = [NSMutableDictionary dictionary];
71 deletedClasses = [NSMutableSet set];
76 - (RLMSchema *)oldSchema {
77 return self.oldRealm.schema;
80 - (RLMSchema *)newSchema {
81 return self.realm.schema;
84 - (void)enumerateObjects:(NSString *)className block:(RLMObjectMigrationBlock)block {
85 if ([deletedClasses containsObject:className]) {
90 RLMResults *objects = [_realm.schema schemaForClassName:className] ? [_realm allObjects:className] : nil;
91 RLMResults *oldObjects = [_oldRealm.schema schemaForClassName:className] ? [_oldRealm allObjects:className] : nil;
93 if (objects && oldObjects) {
94 NSArray *deletedObjects = deletedObjectIndices[className];
95 if (!deletedObjects) {
96 deletedObjects = [NSMutableArray array];
97 deletedObjectIndices[className] = deletedObjects;
100 for (long i = oldObjects.count - 1; i >= 0; i--) {
102 if ([deletedObjects containsObject:@(i)]) {
105 block(oldObjects[i], objects[i]);
110 for (long i = objects.count - 1; i >= 0; i--) {
112 block(nil, objects[i]);
116 else if (oldObjects) {
117 for (long i = oldObjects.count - 1; i >= 0; i--) {
119 block(oldObjects[i], nil);
125 - (void)execute:(RLMMigrationBlock)block {
127 // disable all primary keys for migration and use DynamicObject for all types
128 for (RLMObjectSchema *objectSchema in _realm.schema.objectSchema) {
129 objectSchema.accessorClass = RLMDynamicObject.class;
130 objectSchema.primaryKeyProperty.isPrimary = NO;
132 for (RLMObjectSchema *objectSchema in _oldRealm.schema.objectSchema) {
133 objectSchema.accessorClass = RLMDynamicObject.class;
136 block(self, _oldRealm->_realm->schema_version());
138 [self deleteObjectsMarkedForDeletion];
139 [self deleteDataMarkedForDeletion];
146 - (RLMObject *)createObject:(NSString *)className withValue:(id)value {
147 return [_realm createObject:className withValue:value];
150 - (RLMObject *)createObject:(NSString *)className withObject:(id)object {
151 return [self createObject:className withValue:object];
154 - (void)deleteObject:(RLMObject *)object {
155 [deletedObjectIndices[object.objectSchema.className] addObject:@(object->_row.get_index())];
158 - (void)deleteObjectsMarkedForDeletion {
159 for (NSString *className in deletedObjectIndices.allKeys) {
160 RLMResults *objects = [_realm allObjects:className];
161 for (NSNumber *index in deletedObjectIndices[className]) {
162 RLMObject *object = objects[index.longValue];
163 [_realm deleteObject:object];
168 - (BOOL)deleteDataForClassName:(NSString *)name {
173 TableRef table = ObjectStore::table_for_object_type(_realm.group, name.UTF8String);
178 [deletedClasses addObject:name];
182 - (void)deleteDataMarkedForDeletion {
183 for (NSString *className in deletedClasses) {
184 TableRef table = ObjectStore::table_for_object_type(_realm.group, className.UTF8String);
188 if ([_realm.schema schemaForClassName:className]) {
192 realm::ObjectStore::delete_data_for_object(_realm.group, className.UTF8String);
197 - (void)renamePropertyForClass:(NSString *)className oldName:(NSString *)oldName newName:(NSString *)newName {
198 const char *objectType = className.UTF8String;
199 realm::ObjectStore::rename_property(_realm.group, *_schema, objectType, oldName.UTF8String, newName.UTF8String);