123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // 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 "object_store.hpp"
- #import "shared_realm.hpp"
- #import "schema.hpp"
- #import <realm/table.hpp>
- using namespace realm;
- // The source realm for a migration has to use a SharedGroup to be able to share
- // the file with the destination realm, but we don't want to let the user call
- // beginWriteTransaction on it as that would make no sense.
- @interface RLMMigrationRealm : RLMRealm
- @end
- @implementation RLMMigrationRealm
- - (BOOL)readonly {
- return YES;
- }
- - (void)beginWriteTransaction {
- @throw RLMException(@"Cannot modify the source Realm in a migration");
- }
- @end
- @implementation RLMMigration {
- realm::Schema *_schema;
- std::unordered_map<NSString *, realm::IndexSet> _deletedObjectIndices;
- }
- - (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema {
- self = [super init];
- if (self) {
- _realm = realm;
- _oldRealm = oldRealm;
- _schema = &schema;
- object_setClass(_oldRealm, RLMMigrationRealm.class);
- }
- 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 (auto i = objects.count; i > 0; --i) {
- @autoreleasepool {
- block(nil, objects[i - 1]);
- }
- }
- return;
- }
- auto count = oldObjects.count;
- if (count == 0) {
- return;
- }
- auto deletedObjects = _deletedObjectIndices.find(className);
- for (auto i = count; i > 0; --i) {
- auto index = i - 1;
- if (deletedObjects != _deletedObjectIndices.end() && deletedObjects->second.contains(index)) {
- continue;
- }
- @autoreleasepool {
- block(oldObjects[index], objects[index]);
- }
- }
- }
- - (void)execute:(RLMMigrationBlock)block {
- @autoreleasepool {
- // disable all primary keys for migration and use DynamicObject for all types
- for (RLMObjectSchema *objectSchema in _realm.schema.objectSchema) {
- objectSchema.accessorClass = RLMDynamicObject.class;
- objectSchema.primaryKeyProperty.isPrimary = NO;
- }
- for (RLMObjectSchema *objectSchema in _oldRealm.schema.objectSchema) {
- objectSchema.accessorClass = RLMDynamicObject.class;
- }
- block(self, _oldRealm->_realm->schema_version());
- [self deleteObjectsMarkedForDeletion];
- _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 {
- _deletedObjectIndices[object.objectSchema.className].add(object->_row.get_index());
- }
- - (void)deleteObjectsMarkedForDeletion {
- for (auto& objectType : _deletedObjectIndices) {
- TableRef table = ObjectStore::table_for_object_type(_realm.group, objectType.first.UTF8String);
- if (!table) {
- continue;
- }
- auto& indices = objectType.second;
- // Just clear the table if we're removing all of the rows
- if (table->size() == indices.count()) {
- table->clear();
- }
- // Otherwise delete in reverse order to avoid invaliding any of the
- // not-yet-deleted indices
- else {
- for (auto it = std::make_reverse_iterator(indices.end()), end = std::make_reverse_iterator(indices.begin()); it != end; ++it) {
- for (size_t i = it->second; i > it->first; --i) {
- table->move_last_over(i - 1);
- }
- }
- }
- }
- }
- - (BOOL)deleteDataForClassName:(NSString *)name {
- if (!name) {
- return false;
- }
- TableRef table = ObjectStore::table_for_object_type(_realm.group, name.UTF8String);
- if (!table) {
- return false;
- }
- _deletedObjectIndices[name].set(table->size());
- if (![_realm.schema schemaForClassName:name]) {
- realm::ObjectStore::delete_data_for_object(_realm.group, name.UTF8String);
- }
- 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
|