Newer
Older
bremer-ios-app / Pods / Realm / Realm / RLMMigration.mm
yhornisse on 10 Sep 2023 5 KB Initial Commit
////////////////////////////////////////////////////////////////////////////
//
// 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