realm-swift
realm-swift copied to clipboard
Attempting to delete a model's data in a migration when another linking model has also been removed crashes
Goals
Refactored code that resulted in a few Object subclasses being made redundant. These were removed from the code and a migration was performed that calls Migration.deleteData
on these.
Expected Results
Existing data and columns are removed from the Realm file.
Actual Results
Migration crashes with the following error message:
fatal error: 'try!' expression unexpectedly raised an error: Error Domain=io.realm Code=1 "Table is target of cross-table link columns" UserInfo={
"Error Code" = 1;
NSLocalizedDescription = "Table is target of cross-table link columns";
}: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-703.0.18.8/src/swift/stdlib/public/core/ErrorType.swift, line 54
Steps to Reproduce
Open the attached default.realm
file and try to call migration.deleteData("ReceiptRowSection")
during a migration.
Version of Realm and Tooling
Realm version: 1.0 Xcode version: 7.3.1
Thanks for reporting this @anlaital!
@anlaital Currently, Realm cannot delete a model class that has backlink property and the class definition no longer exists when migration. The workaround is to keep leaving the model definition to be deleted.
@kishikawakatsumi so what would be the workaround when I have deleteRealmIfMigrationNeeded set to true with backlink properties in a model? We're still in our early days and love the deleteRealmIfMigrationNeeded feature for whenever we're throwing new properties in, but my app went into an eternal death spiral when I added a backlink.
I can see that this has been set as P2
, but would it be possible to enhance the error message given in this scenario? Our current project has quite a few models, so just stating Table is target of cross-table link columns
is not very helpful. Could this contain information on the offending class or table?
Hello,
I'm curious about this, I'm having the same issue when reworking ours migrations logics.
The first draft used to leave the tables
for deleted objects after migrations inside the realm.
I though I would tried to improved it and got this migration logics:
let newClassNames = migration.newSchema.objectSchema.map { $0.className }
migration.oldSchema.objectSchema.forEach { (objectSchema) in
guard newClassNames.contains(objectSchema.className) == false else {
return
}
migration.deleteData(forType: objectSchema.className)
print("Realm migration :: deleting removed type \(objectSchema.className)")
}
@anlaital proposition would be welcomed. I'd even see this behaviour as a default for removed classes during migration to cleanup realm databases.
Not sure if I'm posting this in the correct issue as the issue I've submitted was marked a duplicate of this issue.
I've attempted to avoid the cross table link error by setting the config's deleteRealmIfMigrationNeeded
to true. This works but means the persisted data is always deleted when a migration is needed.
Rather than always setting deleteRealmIfMigrationNeeded
to true so I can do migrations, I'm looking into catching the cross table link error when initializing realm, and then attempting to initialize realm again with another configuration with deleteRealmIfMigrationNeeded set to true like so:
let defaultConfig = Realm.Configuration()
defaultConfig.schemaVersion = newSchemaVersion
defaultConfig.migrationBlock = {
// perform migrations...
}
let realm: Realm
do {
realm = try Realm(configuration: defaultConfig)
} catch {
let deleteMigrationConfig = Realm.Configuration()
deleteMigrationConfig.schemaVersion = newSchemaVersion
deleteMigrationConfig.deleteRealmIfMigrationNeeded = true
do {
realm = try Realm(configuration: deleteMigrationConfig)
} catch {
fatalError("Failed to instantiate: \(error.localizedDescription)")
}
}
I'm getting the Table is target of cross-table link columns
in the first catch like expected but I'm getting Realm at path '.../default.realm' already opened with a different schema mode.
when it's performing the initialization in the second catch:
Why is my realm file opened if it has failed to be initialized?
I've got a related problem. I've moved a table from one table into a readonly/asset table and at migration time I do the following:
if (realm.getSchema().contains("SearchArea")) { realm.getSchema().remove("SearchArea"); }
Even though it is no longer in the realm module definition. It does have self referential links (e.g., its hierarchical).
How should this be dealt with?
In case this is useful for anyone: I had two classes:
class Foo: RealmObject(){
val bar: Bar
...
}
and
class Bar: RealmObject {
...
}
I didn't need to store those in realm anymore, so in my Migration
class I wrote the following:
schema?.remove("Bar")
schema?.remove("Foo")
and when the migration was executed, my app crashed and I received the error message:
"Table is target of cross-table link columns"
I solved it by reversing the remove order
schema?.remove("Foo")
schema?.remove("Bar")
Because Foo
depends on Bar
, so if I removed it first, Foo
would have an invalid field, because Bar
wouldn't be a RealmObject anymore.
Hope this helps!
@ivanebernal Can you please tell me what is the "remove" function? I can't find it in RLMSchema object, thanks.
EDIT:
I found the equivalent function in swift: https://realm.io/docs/swift/latest/api/Classes/Migration.html#/s:FC10RealmSwift9Migration10deleteDataFT7forTypeSS_Sb You can check this question in SO: https://stackoverflow.com/questions/36717796/how-to-delete-a-class-from-realm-file
Hope this helps!
@amoshsueh hello. My code is actually written in Kotlin (for android). The remove function can be found on the RealmSchema object of the DynamicRealm provided by the overridden migrate method in your RealmMigration implementation. In other words:
//On your migrate method in your RealmMigration
realm?.schema?.remove("something")
Here's an example of a migration class:
class Migration : RealmMigration {
override fun migrate(realm: DynamicRealm?, oldVersion: Long, newVersion: Long) {
val schema = realm?.schema
if(newVersion == 2L) {
schema?.remove("YourRealmObject")
}
}
}
where 'YourRealmObject' is the name of your class extending RealmObject.
An example of a migration in swift can be found here: https://realm.io/docs/swift/latest/#updating-values
Hope this helps!
I still face the problem
class Model { (...) specificState: SpecificState } class SubObject { (...) parent: Model }
Realm configuration's object type contains SpecificState.self
I want to remove SubObject from my app, but I cannot manage to perform a migration that would at the same time remove the class & the data. deleteData(forType: "SubObject") seems to be the one with the issue.
If I don't call deleteData(...) , the only issue is that the data is remaining.
I don't want to need to create two configuration, I aspect a migration to be able to do so.