Skip to content

Commit f419693

Browse files
committed
fix: Exposed-950 Order by clause is repeated hundredfold
1 parent f824d9e commit f419693

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-2
lines changed

exposed-dao/src/main/kotlin/org/jetbrains/exposed/v1/dao/References.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ open class Referrers<ParentID : Any, in Parent : Entity<ParentID>, ChildID : Any
110110
val cache: Boolean,
111111
references: Map<Column<*>, Column<*>>? = null
112112
) : ReadOnlyProperty<Parent, SizedIterable<Child>> {
113-
/** The list of columns and their [SortOrder] for ordering referred entities in one-to-many relationship. */
114-
private val orderByExpressions: MutableList<Pair<Expression<*>, SortOrder>> = mutableListOf()
113+
/** The set of columns and their [SortOrder] for ordering referred entities in one-to-many relationship. */
114+
private val orderByExpressions = linkedSetOf<Pair<Expression<*>, SortOrder>>()
115115

116116
val allReferences = references ?: run {
117117
reference.referee ?: error("Column $reference is not a reference")

exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/OrderedReferenceTest.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package org.jetbrains.exposed.v1.tests.shared.entities
22

33
import org.jetbrains.exposed.v1.core.SortOrder.DESC
4+
import org.jetbrains.exposed.v1.core.Transaction
45
import org.jetbrains.exposed.v1.core.dao.id.EntityID
56
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
7+
import org.jetbrains.exposed.v1.core.statements.StatementContext
8+
import org.jetbrains.exposed.v1.core.statements.StatementInterceptor
69
import org.jetbrains.exposed.v1.dao.IntEntity
710
import org.jetbrains.exposed.v1.dao.IntEntityClass
11+
import org.jetbrains.exposed.v1.dao.entityCache
812
import org.jetbrains.exposed.v1.jdbc.JdbcTransaction
913
import org.jetbrains.exposed.v1.jdbc.insert
1014
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
@@ -15,6 +19,8 @@ import org.jetbrains.exposed.v1.tests.shared.assertEquals
1519
import org.jetbrains.exposed.v1.tests.shared.assertTrue
1620
import org.junit.jupiter.api.Tag
1721
import org.junit.jupiter.api.Test
22+
import org.junit.jupiter.api.assertNotNull
23+
import kotlin.math.max
1824

1925
@Tag(MISSING_R2DBC_TEST)
2026
class OrderedReferenceTest : DatabaseTestsBase() {
@@ -65,6 +71,50 @@ class OrderedReferenceTest : DatabaseTestsBase() {
6571
}
6672
}
6773

74+
@Test
75+
fun testNoDuplicatedOrderByPartsInQuery() {
76+
// This interceptor counts duplicated ORDER BY parts in the sql sent to database.
77+
// We want to be sure that DAO doesn't create duplicated parts.
78+
val interceptor = object : StatementInterceptor {
79+
var maxDuplicates = 0
80+
override fun beforeExecution(transaction: Transaction, context: StatementContext) {
81+
val duplicatedPartsAmount = context.statement.prepareSQL(transaction)
82+
// Get all the parts from order by section
83+
.lowercase()
84+
.substringAfter("order by")
85+
.split(",")
86+
.map { it.trim() }
87+
// Count the occurrences of each part and take maximum
88+
.groupBy { it }
89+
.mapValues { (_, list) -> list.size }
90+
.maxByOrNull { it.value }
91+
?.value ?: 0
92+
93+
maxDuplicates = max(maxDuplicates, duplicatedPartsAmount)
94+
}
95+
}
96+
97+
withOrderedReferenceTestTables {
98+
registerInterceptor(interceptor)
99+
// `orderBy` on references in DAO Entity classes could collect duplicated parts.
100+
// That method is executed on every access to the field, so every query has
101+
// one more duplicated part
102+
// It's mentioned in the original issue
103+
// 'EXPOSED-950 Order by clause is repeated hundredfold'
104+
repeat(5) {
105+
val user = UserDefaultOrder.all().first()
106+
entityCache.clear()
107+
108+
// This sections needs only to force DAO fetch the data to execute SQL queries
109+
user.ratings.forEach { rating ->
110+
assertNotNull(rating.value)
111+
}
112+
113+
assertEquals(1, interceptor.maxDuplicates)
114+
}
115+
}
116+
}
117+
68118
class UserRatingMultiColumn(id: EntityID<Int>) : IntEntity(id) {
69119
companion object : IntEntityClass<UserRatingMultiColumn>(UserRatings)
70120

0 commit comments

Comments
 (0)