Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: EXPOSED-382 ClassCastException when uuid().references() is used with EntityID column #2079

Merged
merged 1 commit into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Column<T>(
val referee: Column<*>?
get() = foreignKey?.targetOf(this)

/** Returns the column that this column references, casted as a column of type [S], or `null` if the cast fails. */
/** Returns the column that this column references, cast as a column of type [S], or `null` if the cast fails. */
@Suppress("UNCHECKED_CAST")
fun <S : T> referee(): Column<S>? = referee as? Column<S>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,15 @@ open class Entity<ID : Comparable<ID>>(val id: EntityID<ID>) {
}
}
else -> {
// @formatter:off
val castReferee = reference.referee<REF>()!!
val baseReferee = (castReferee.columnType as? EntityIDColumnType<REF>)?.idColumn ?: castReferee
factory.findWithCacheCondition({
reference.referee!!.getValue(this, desc) == refValue
}) {
reference.referee<REF>()!! eq refValue
baseReferee eq refValue
Comment on lines +128 to +133
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This forces the query clause to use the underlying/wrapped type of EntityIDColumnType when setting statement parameters.

The elvis op is necessary in case the cast fails, which covers cases when an entity may have a foreign key on a target refColumn that is not actually an entityID column.

}.singleOrNull()?.also {
storeReferenceInCache(reference, it)
}
// @formatter:on
}
} ?: error("Cannot find ${factory.table.tableName} WHERE id=$refValue")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import org.jetbrains.exposed.dao.LongEntity
import org.jetbrains.exposed.dao.LongEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.LongIdTable
import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.exists
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.tests.DatabaseTestsBase
import org.jetbrains.exposed.sql.tests.shared.assertEquals
import org.junit.Test
Expand All @@ -13,30 +16,43 @@ object LongIdTables {
object Cities : LongIdTable() {
val name = varchar("name", 50)
}

class City(id: EntityID<Long>) : LongEntity(id) {
companion object : LongEntityClass<City>(Cities)
var name by Cities.name
}

object People : LongIdTable() {
val name = varchar("name", 80)
val cityId = reference("city_id", Cities)
}

class Person(id: EntityID<Long>) : LongEntity(id) {
companion object : LongEntityClass<Person>(People)
var name by People.name
var city by City referencedOn People.cityId
}

object Towns : LongIdTable("towns") {
val cityId: Column<Long> = long("city_id").references(Cities.id)
}

class Town(id: EntityID<Long>) : LongEntity(id) {
companion object : LongEntityClass<Town>(Towns)
var city by City referencedOn Towns.cityId
}
}
class LongIdTableEntityTest : DatabaseTestsBase() {

@Test fun `create tables`() {
@Test
fun `create tables`() {
withTables(LongIdTables.Cities, LongIdTables.People) {
assertEquals(true, LongIdTables.Cities.exists())
assertEquals(true, LongIdTables.People.exists())
}
}

@Test fun `create records`() {
@Test
fun `create records`() {
withTables(LongIdTables.Cities, LongIdTables.People) {
val mumbai = LongIdTables.City.new { name = "Mumbai" }
val pune = LongIdTables.City.new { name = "Pune" }
Expand Down Expand Up @@ -64,7 +80,8 @@ class LongIdTableEntityTest : DatabaseTestsBase() {
}
}

@Test fun `update and delete records`() {
@Test
fun `update and delete records`() {
withTables(LongIdTables.Cities, LongIdTables.People) {
val mumbai = LongIdTables.City.new { name = "Mumbai" }
val pune = LongIdTables.City.new { name = "Pune" }
Expand Down Expand Up @@ -93,4 +110,19 @@ class LongIdTableEntityTest : DatabaseTestsBase() {
assertEquals(false, allPeople.contains(Pair("Tanu Arora", "Pune")))
}
}

@Test
fun testForeignKeyBetweenLongAndEntityIDColumns() {
withTables(LongIdTables.Cities, LongIdTables.Towns) {
val cId = LongIdTables.Cities.insertAndGetId {
it[name] = "City A"
}
LongIdTables.Towns.insert {
it[cityId] = cId.value
}

val town1 = LongIdTables.Town.all().single()
assertEquals(cId, town1.city.id)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import org.jetbrains.exposed.dao.UUIDEntity
import org.jetbrains.exposed.dao.UUIDEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.UUIDTable
import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.exists
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.tests.DatabaseTestsBase
import org.jetbrains.exposed.sql.tests.shared.assertEquals
import org.junit.Test
Expand Down Expand Up @@ -47,10 +50,18 @@ object UUIDTables {
var city by City.referencedOn(Addresses.city)
var address by Addresses.address
}

object Towns : UUIDTable("towns") {
val cityId: Column<UUID> = uuid("city_id").references(Cities.id)
}

class Town(id: EntityID<UUID>) : UUIDEntity(id) {
companion object : UUIDEntityClass<Town>(Towns)
var city by City referencedOn Towns.cityId
}
}

class UUIDTableEntityTest : DatabaseTestsBase() {

@Test
fun `create tables`() {
withTables(UUIDTables.Cities, UUIDTables.People) {
Expand Down Expand Up @@ -149,4 +160,19 @@ class UUIDTableEntityTest : DatabaseTestsBase() {
assertEquals("address2", address2.address)
}
}

@Test
fun testForeignKeyBetweenUUIDAndEntityIDColumns() {
withTables(UUIDTables.Cities, UUIDTables.Towns) {
val cId = UUIDTables.Cities.insertAndGetId {
it[name] = "City A"
}
UUIDTables.Towns.insert {
it[cityId] = cId.value
}

val town1 = UUIDTables.Town.all().single()
assertEquals(cId, town1.city.id)
}
}
}
Loading