realm / realm-kotlin

Kotlin Multiplatform and Android SDK for the Realm Mobile Database: Build Better Apps Faster.
Apache License 2.0
889 stars 52 forks source link

RealmSet<class String> is detected as RealmSet<class UnmanagedRealmSet> for DynamicMutableRealmObject.set when doing migration #1619

Closed p0058781 closed 6 months ago

p0058781 commented 6 months ago

How frequently does the bug occur?

Always

Description

Old schema:

class RealmGame : RealmObject {
    ...
    var executablePath: String? = null
    ...
}

New schema:

class RealmGame : RealmObject {
    ...
    var executablePaths: RealmSet<String> = realmSetOf()
    ...
}

Migration:

private class MyMigration : AutomaticSchemaMigration {
    override fun migrate(migrationContext: AutomaticSchemaMigration.MigrationContext) {
        migrationContext.enumerate(RealmGame::class.simpleName!!) { oldObject: DynamicRealmObject, newObject: DynamicMutableRealmObject? ->
            newObject?.run {
                val executablePath: String? = oldObject.getNullableValue("executablePath")
                executablePath?.let {
                    set<RealmSet<String>>(
                        "executablePaths",
                        realmSetOf<String>(it)
                    )
                }
            }
        }
    }

}

Stacktrace & log output

Caused by: java.lang.IllegalArgumentException: Trying to access property 'RealmGame.executablePaths' as type: 'RealmSet<class io.realm.kotlin.internal.UnmanagedRealmSet>' but actual schema type is 'RealmSet<class kotlin.String>'
    at io.realm.kotlin.internal.RealmObjectHelper.checkPropertyType(RealmObjectHelper.kt:1230)
    at io.realm.kotlin.internal.RealmObjectHelper.dynamicGetSet$io_realm_kotlin_library(RealmObjectHelper.kt:965)
    at io.realm.kotlin.internal.RealmObjectHelper.dynamicGetSet$io_realm_kotlin_library$default(RealmObjectHelper.kt:957)
    at io.realm.kotlin.internal.RealmObjectHelper.dynamicSetValue$io_realm_kotlin_library(RealmObjectHelper.kt:1132)
    at io.realm.kotlin.internal.RealmObjectHelper.dynamicSetValue$io_realm_kotlin_library$default(RealmObjectHelper.kt:1028)
    at io.realm.kotlin.internal.dynamic.DynamicMutableRealmObjectImpl.set(DynamicMutableRealmObjectImpl.kt:134)
    at org.skynetsoftware.avnlauncher.data.database.MyMigration$migrate$1.invoke(DatabaseKoinModule.kt:32)
    at org.skynetsoftware.avnlauncher.data.database.MyMigration$migrate$1.invoke(DatabaseKoinModule.kt:28)
    at io.realm.kotlin.migration.AutomaticSchemaMigration$MigrationContext$DefaultImpls.enumerate(AutomaticSchemaMigration.kt:112)
    at io.realm.kotlin.internal.ConfigurationImpl$migrationCallback$1$1$1.enumerate(ConfigurationImpl.kt:172)
    at org.skynetsoftware.avnlauncher.data.database.MyMigration.migrate(DatabaseKoinModule.kt:28)
    at io.realm.kotlin.internal.ConfigurationImpl.lambda$3$lambda$2(ConfigurationImpl.kt:172)
    at io.realm.kotlin.internal.interop.realmcJNI.realm_open(Native Method)
    at io.realm.kotlin.internal.interop.realmc.realm_open(realmc.java:426)
    at io.realm.kotlin.internal.interop.RealmInterop.realm_open(RealmInterop.kt:235)
    at io.realm.kotlin.internal.ConfigurationImpl$openRealm$2.invoke(ConfigurationImpl.kt:115)
    at io.realm.kotlin.internal.ConfigurationImpl$openRealm$2.invoke(ConfigurationImpl.kt:114)
    at io.realm.kotlin.internal.interop.NativePointerKt.use(NativePointer.kt:53)
    at io.realm.kotlin.internal.ConfigurationImpl.openRealm$suspendImpl(ConfigurationImpl.kt:114)
    at io.realm.kotlin.internal.ConfigurationImpl.openRealm(ConfigurationImpl.kt)
    at io.realm.kotlin.internal.RealmImpl$1.invokeSuspend(RealmImpl.kt:130)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at io.realm.kotlin.internal.platform.CoroutineUtilsSharedJvmKt.runBlocking(CoroutineUtilsSharedJvm.kt:22)
    at io.realm.kotlin.internal.platform.CoroutineUtilsSharedJvmKt.runBlocking$default(CoroutineUtilsSharedJvm.kt:21)
    at io.realm.kotlin.internal.RealmImpl.<init>(RealmImpl.kt:112)
    at io.realm.kotlin.internal.RealmImpl.<init>(RealmImpl.kt)
    at io.realm.kotlin.internal.RealmImpl$Companion.create$io_realm_kotlin_library(RealmImpl.kt:308)
    at io.realm.kotlin.Realm$Companion.open(Realm.kt:83)
    at org.skynetsoftware.avnlauncher.data.database.DatabaseKoinModuleKt$databaseKoinModule$1$1.invoke(DatabaseKoinModule.kt:22)
    at org.skynetsoftware.avnlauncher.data.database.DatabaseKoinModuleKt$databaseKoinModule$1$1.invoke(DatabaseKoinModule.kt:17)
    at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:50)
    ... 37 more

Can you reproduce the bug?

Always

Reproduction Steps

No response

Version

1.13.0

What Atlas App Services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

Kotlin Multiplatform JVM

Build environment

Intellij IDEA version: 2023.3.2 Android Build Tools version: 8.0.2 Gradle version: 8.2.1

rorbech commented 6 months ago

You shouldn't use set on the RealmSet-property. Instead you need to obtain a reference to the RealmSet of your newObject and add the elements to it with something like

getValueSet<String>("executablePaths").add(it)
p0058781 commented 6 months ago

Ah, ok, works like that. Thank you I was following Change an Object Model - Kotlin SDK That guide could probably also be updated to mention that