////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMMigration_Private.h"
#import "RLMAccessor.h"
#import "RLMObject_Private.h"
#import "RLMObject_Private.hpp"
#import "RLMObjectSchema_Private.hpp"
#import "RLMObjectStore.h"
#import "RLMProperty_Private.h"
#import "RLMRealm_Dynamic.h"
#import "RLMRealm_Private.hpp"
#import "RLMResults_Private.hpp"
#import "RLMSchema_Private.hpp"
#import "RLMUtil.hpp"
#import <realm/object-store/object_store.hpp>
#import <realm/object-store/shared_realm.hpp>
#import <realm/object-store/schema.hpp>
#import <realm/table.hpp>
using namespace realm;
@implementation RLMMigration {
realm::Schema *_schema;
}
- (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema {
self = [super init];
if (self) {
_realm = realm;
_oldRealm = oldRealm;
_schema = &schema;
}
return self;
}
- (RLMSchema *)oldSchema {
return self.oldRealm.schema;
}
- (RLMSchema *)newSchema {
return self.realm.schema;
}
- (void)enumerateObjects:(NSString *)className block:(__attribute__((noescape)) RLMObjectMigrationBlock)block {
RLMResults *objects = [_realm.schema schemaForClassName:className] ? [_realm allObjects:className] : nil;
RLMResults *oldObjects = [_oldRealm.schema schemaForClassName:className] ? [_oldRealm allObjects:className] : nil;
// For whatever reason if this is a newly added table we enumerate the
// objects in it, while in all other cases we enumerate only the existing
// objects. It's unclear how this could be useful, but changing it would
// also be a pointless breaking change and it's unlikely to be hurting anyone.
if (objects && !oldObjects) {
for (RLMObject *object in objects) {
@autoreleasepool {
block(nil, object);
}
}
return;
}
// If a table will be deleted it can still be enumerated during the migration
// so that data can be saved or transfered to other tables if necessary.
if (!objects && oldObjects) {
for (RLMObject *oldObject in oldObjects) {
@autoreleasepool {
block(oldObject, nil);
}
}
return;
}
if (oldObjects.count == 0 || objects.count == 0) {
return;
}
auto& info = _realm->_info[className];
for (RLMObject *oldObject in oldObjects) {
@autoreleasepool {
Obj newObj;
try {
newObj = info.table()->get_object(oldObject->_row.get_key());
}
catch (KeyNotFound const&) {
continue;
}
block(oldObject, (id)RLMCreateObjectAccessor(info, std::move(newObj)));
}
}
}
- (void)execute:(RLMMigrationBlock)block objectClass:(Class)dynamicObjectClass {
if (!dynamicObjectClass) {
dynamicObjectClass = RLMDynamicObject.class;
}
@autoreleasepool {
// disable all primary keys for migration and use DynamicObject for all types
for (RLMObjectSchema *objectSchema in _realm.schema.objectSchema) {
objectSchema.accessorClass = dynamicObjectClass;
objectSchema.primaryKeyProperty.isPrimary = NO;
}
for (RLMObjectSchema *objectSchema in _oldRealm.schema.objectSchema) {
objectSchema.accessorClass = dynamicObjectClass;
}
block(self, _oldRealm->_realm->schema_version());
_oldRealm = nil;
_realm = nil;
}
}
- (RLMObject *)createObject:(NSString *)className withValue:(id)value {
return [_realm createObject:className withValue:value];
}
- (RLMObject *)createObject:(NSString *)className withObject:(id)object {
return [self createObject:className withValue:object];
}
- (void)deleteObject:(RLMObject *)object {
[_realm deleteObject:object];
}
- (BOOL)deleteDataForClassName:(NSString *)name {
if (!name) {
return false;
}
TableRef table = ObjectStore::table_for_object_type(_realm.group, name.UTF8String);
if (!table) {
return false;
}
if ([_realm.schema schemaForClassName:name]) {
table->clear();
}
else {
_realm.group.remove_table(table->get_key());
}
return true;
}
- (void)renamePropertyForClass:(NSString *)className oldName:(NSString *)oldName newName:(NSString *)newName {
realm::ObjectStore::rename_property(_realm.group, *_schema, className.UTF8String,
oldName.UTF8String, newName.UTF8String);
}
@end