Exposed icon indicating copy to clipboard operation
Exposed copied to clipboard

0.39.1 breaks my application

Open pindab0ter opened this issue 3 years ago • 2 comments

When updating from 0.38.2 to 0.39.1 or .2 my application breaks.

When I'm trying to load one or more instances of the Farmer DAO from the Farmers DSL, this error occurs:

transaction {
    Farmer.findById("1")
}
java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at kotlin.reflect.jvm.internal.calls.CallerImpl$Constructor.call(CallerImpl.kt:41)
	at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108)
	at org.jetbrains.exposed.dao.EntityClass$entityCtor$1.invoke(EntityClass.kt:32)
	at org.jetbrains.exposed.dao.EntityClass$entityCtor$1.invoke(EntityClass.kt:32)
	at org.jetbrains.exposed.dao.EntityClass.createInstance(EntityClass.kt:229)
	at org.jetbrains.exposed.dao.EntityClass.wrap(EntityClass.kt:233)
	at org.jetbrains.exposed.dao.EntityClass.wrapRow(EntityClass.kt:138)
	at org.jetbrains.exposed.dao.EntityClass$wrapRows$1.invoke(EntityClass.kt:125)
	at org.jetbrains.exposed.dao.EntityClass$wrapRows$1.invoke(EntityClass.kt:124)
	at org.jetbrains.exposed.sql.IterableExKt$mapLazy$1$iterator$1.next(IterableEx.kt:131)
	at kotlin.collections.CollectionsKt___CollectionsKt.firstOrNull(_Collections.kt:272)
	at org.jetbrains.exposed.dao.EntityClass.findById(EntityClass.kt:56)
	at org.jetbrains.exposed.dao.EntityClass.findById(EntityClass.kt:47)
	at nl.pindab0ter.eggbot.utilities.ScratchKt$main$2$1$1$1$result$1.invoke(Scratch.kt:24)
	at nl.pindab0ter.eggbot.utilities.ScratchKt$main$2$1$1$1$result$1.invoke(Scratch.kt:23)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction$run(ThreadLocalTransactionManager.kt:189)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.access$inTopLevelTransaction$run(ThreadLocalTransactionManager.kt:1)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt$inTopLevelTransaction$1.invoke(ThreadLocalTransactionManager.kt:215)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.keepAndRestoreTransactionRefAfterRun(ThreadLocalTransactionManager.kt:223)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction(ThreadLocalTransactionManager.kt:214)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt$transaction$1.invoke(ThreadLocalTransactionManager.kt:165)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.keepAndRestoreTransactionRefAfterRun(ThreadLocalTransactionManager.kt:223)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:135)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:132)
	at nl.pindab0ter.eggbot.utilities.ScratchKt$main$2$1$1.invokeSuspend(Scratch.kt:23)
	at nl.pindab0ter.eggbot.utilities.ScratchKt$main$2$1$1.invoke(Scratch.kt)
	at nl.pindab0ter.eggbot.utilities.ScratchKt$main$2$1$1.invoke(Scratch.kt)
	at com.kotlindiscord.kord.extensions.builders.ExtensibleBotBuilder$HooksBuilder.runBeforeExtensionsAdded(ExtensibleBotBuilder.kt:942)
	at com.kotlindiscord.kord.extensions.builders.ExtensibleBotBuilder.build$suspendImpl(ExtensibleBotBuilder.kt:478)
	at com.kotlindiscord.kord.extensions.builders.ExtensibleBotBuilder$build$1.invokeSuspend(ExtensibleBotBuilder.kt)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:178)
	at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
	at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)
	at kotlinx.coroutines.ResumeOnCompletion.invoke(JobSupport.kt:1398)
	at kotlinx.coroutines.JobSupport.notifyCompletion(JobSupport.kt:1520)
	at kotlinx.coroutines.JobSupport.completeStateFinalization(JobSupport.kt:323)
	at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:240)
	at kotlinx.coroutines.JobSupport.continueCompleting(JobSupport.kt:935)
	at kotlinx.coroutines.JobSupport.access$continueCompleting(JobSupport.kt:27)
	at kotlinx.coroutines.JobSupport$ChildCompletion.invoke(JobSupport.kt:1155)
	at kotlinx.coroutines.JobSupport.notifyCompletion(JobSupport.kt:1520)
	at kotlinx.coroutines.JobSupport.completeStateFinalization(JobSupport.kt:323)
	at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:240)
	at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:906)
	at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:863)
	at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:828)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100)
	at kotlinx.coroutines.debug.internal.DebugProbesImpl$CoroutineOwner.resumeWith(DebugProbesImpl.kt:545)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Caused by: java.lang.ExceptionInInitializerError
	at nl.pindab0ter.eggbot.model.database.Coop$Companion.<init>(Coop.kt:40)
	at nl.pindab0ter.eggbot.model.database.Coop$Companion.<init>(Coop.kt)
	at nl.pindab0ter.eggbot.model.database.Coop.<clinit>(Coop.kt:38)
	at nl.pindab0ter.eggbot.model.database.Farmer.<init>(Farmer.kt:30)
	... 65 more
Caused by: java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.text.StringsKt__StringsKt.substringAfter, parameter <this>
	at kotlin.text.StringsKt__StringsKt.substringAfter(Strings.kt)
	at kotlin.text.StringsKt__StringsKt.substringAfter$default(Strings.kt:456)
	at org.jetbrains.exposed.sql.Table.getTableNameWithoutScheme$exposed_core(Table.kt:334)
	at org.jetbrains.exposed.sql.Table$PrimaryKey.<init>(Table.kt:424)
	at org.jetbrains.exposed.dao.id.IntIdTable.<init>(IdTable.kt:43)
	at org.jetbrains.exposed.dao.id.IntIdTable.<init>(IdTable.kt:41)
	at nl.pindab0ter.eggbot.model.database.Coops.<init>(Coops.kt:8)
	at nl.pindab0ter.eggbot.model.database.Coops.<clinit>(Coops.kt)
	... 69 more

The error message doesn't tell me what went wrong specifically. It's not clear to me, at least.

Even though I'm not loading or using this relationship (no eager loading or calling it), it seems to originate in the Coops DSL, with the relation defined like this:

var coops by Coop via CoopFarmers

Unfortunately I can't open source the code. So please let me know what more details I should provide.

The error occurs even when the coops table is completely empty. No non-nullable fields in Farmer are null.

With 3.8.2 this worked without a problem.

pindab0ter avatar Sep 05 '22 09:09 pindab0ter

Seems like Exposed is not able to figure out the name of the table from your table class. Not sure why it only happens in 0.39, as this code wasn't changed in a year: https://github.com/JetBrains/Exposed/blob/2feb5505bb02ef75bf5ddb32395b5fa4c8c7f001/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt#L334

Can you please rename your Coop to CoopTable or try and specify the table explicitly?

AlexeySoshin avatar Sep 05 '22 10:09 AlexeySoshin

@pindab0ter , can you share your Farmers table declaration and what Java version do you use?

Tapac avatar Sep 07 '22 09:09 Tapac

Seems like Exposed is not able to figure out the name of the table from your table class. Not sure why it only happens in 0.39, as this code wasn't changed in a year:

https://github.com/JetBrains/Exposed/blob/2feb5505bb02ef75bf5ddb32395b5fa4c8c7f001/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt#L334

Can you please rename your Coop to CoopTable or try and specify the table explicitly?

The table name was already explicitly defined:

object Coops : IntIdTable() {
    override val tableName = "coops"

@pindab0ter , can you share your Farmers table declaration and what Java version do you use?

The project uses Java version 11 (Azul Zulu 11.0.16, specifically), defined through kotlinOptions.jvmTarget and as the Project SDK in IntelliJ.

This is what Farmers.kt looks like:

import org.jetbrains.exposed.dao.id.IdTable
import org.jetbrains.exposed.sql.ReferenceOption.CASCADE
import org.jetbrains.exposed.sql.jodatime.datetime
import org.joda.time.DateTime.now

object Farmers : IdTable<String>() {
    override val tableName = "farmers"
    override val id = text("id").entityId()
    override val primaryKey = PrimaryKey(id)
    val userId = reference("id", Users, CASCADE, CASCADE)
    // text, integer, double and long fields
    val createdAt = datetime("created_at").clientDefault { now() }
    val updatedAt = datetime("updated_at").clientDefault { now() }
}

I would like to reiterate that version 0.38.2 doesn't have this problem.

The earlier mentioned Coop and Farmer DAOs do have many-to-many relations defined:

// Farmer.kt (DAO)
// ...
    var coops by Coop via CoopFarmers
// ...

// Coop.kt (DAO)
// ...
    var farmers by Farmer via CoopFarmers
// ...

// CoopFarmers.kt (DSL)
object CoopFarmers : Table() {
    override val tableName = "coop_farmers"
    val farmer = reference("farmer", Farmers, CASCADE, CASCADE)
    val coop = reference("coop", Coops, CASCADE, CASCADE)

    init {
        index(true, farmer, coop)
    }
}

pindab0ter avatar Sep 28 '22 08:09 pindab0ter

I was able to reproduce the bug, will fix it with next release. As a workaround you can move table name into table declaration like:

object Farmers : IdTable<String>{"farmers") { ... }
object CoopFarmers : Table("coop_farmers")  { ... }

It should help

Tapac avatar Sep 28 '22 17:09 Tapac

Awesome! Glad to hear you were able to reproduce the problem.

Thank you for providing a workaround as well. I will give that a go.

Do you want to leave this issue open until there’s a commit/PR/release with a fix, or should I close it?

pindab0ter avatar Sep 28 '22 19:09 pindab0ter

I'll close it just after all tests will pass on CI. The fix is on the way.

Tapac avatar Sep 28 '22 19:09 Tapac