11package org.jetbrains.exposed.v1.tests.shared.entities
22
33import org.jetbrains.exposed.v1.core.SortOrder.DESC
4+ import org.jetbrains.exposed.v1.core.Transaction
45import org.jetbrains.exposed.v1.core.dao.id.EntityID
56import 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
69import org.jetbrains.exposed.v1.dao.IntEntity
710import org.jetbrains.exposed.v1.dao.IntEntityClass
11+ import org.jetbrains.exposed.v1.dao.entityCache
812import org.jetbrains.exposed.v1.jdbc.JdbcTransaction
913import org.jetbrains.exposed.v1.jdbc.insert
1014import org.jetbrains.exposed.v1.jdbc.insertAndGetId
@@ -15,6 +19,8 @@ import org.jetbrains.exposed.v1.tests.shared.assertEquals
1519import org.jetbrains.exposed.v1.tests.shared.assertTrue
1620import org.junit.jupiter.api.Tag
1721import org.junit.jupiter.api.Test
22+ import org.junit.jupiter.api.assertNotNull
23+ import kotlin.math.max
1824
1925@Tag(MISSING_R2DBC_TEST )
2026class 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