From 545893d720c1520abad0b96c9cb1479b4a9b9995 Mon Sep 17 00:00:00 2001 From: Oleg Babichev Date: Fri, 27 Jun 2025 10:36:43 +0200 Subject: [PATCH 1/3] fix: EXPOSED-815 Resolving an aliased field on a query alias doesn't work --- .../org/jetbrains/exposed/v1/core/Alias.kt | 2 +- .../exposed/v1/tests/shared/AliasesTests.kt | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt index 721258d6c2..4bc1c321b4 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt @@ -194,7 +194,7 @@ class QueryAlias(val query: AbstractQuery<*>, val alias: String) : ColumnSet() { ?: error("Column not found in original table") operator fun get(original: Expression): Expression { - val aliases = query.set.fields.filterIsInstance>() + val aliases = query.set.fields.filterIsInstance>() return aliases.find { it == original }?.let { it.delegate.alias("$alias.${it.alias}").aliasOnlyExpression() } ?: aliases.find { it.delegate == original }?.aliasOnlyExpression() diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt index c780811e87..c8e358f3fd 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt @@ -342,4 +342,23 @@ class AliasesTests : DatabaseTestsBase() { ) } } + + @Test + fun testAliasCastedToExpression() { + val tester = object : IntIdTable("Test") { + val weight = integer("weight") + } + + val expression = tester.weight.sum() + val alias: Expression = expression.alias("task_weight") + val query: QueryAlias = tester.select(alias).alias("all_weight") + + // EXPOSED-815 Resolving an aliased field on a query alias doesn't work + // Reading `query[alias]` crashed before the fix + val aliasExpressionString = QueryBuilder(true).also { + query[alias].toQueryBuilder(it) + }.toString() + + assertEquals("all_weight.task_weight", aliasExpressionString) + } } From 35e3b6b9b78deb6c0b839951961cf0e3bf82ffac Mon Sep 17 00:00:00 2001 From: Tim Koopman Date: Thu, 3 Jul 2025 17:06:31 +0800 Subject: [PATCH 2/3] fix: EXPOSED-815 Check types for QueryAlias.get() at runtime --- .../org/jetbrains/exposed/v1/core/Alias.kt | 28 +++++++++---------- .../exposed/v1/tests/shared/AliasesTests.kt | 13 +++++++++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt index 4bc1c321b4..8fe364b783 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt @@ -188,27 +188,25 @@ class QueryAlias(val query: AbstractQuery<*>, val alias: String) : ColumnSet() { override val columns: List> = fields.filterIsInstance>() - @Suppress("UNCHECKED_CAST") operator fun get(original: Column): Column = - query.set.source.columns.find { it == original }?.clone() as? Column - ?: error("Column not found in original table") + get(original as Expression) as Column operator fun get(original: Expression): Expression { - val aliases = query.set.fields.filterIsInstance>() - return aliases.find { it == original }?.let { - it.delegate.alias("$alias.${it.alias}").aliasOnlyExpression() - } ?: aliases.find { it.delegate == original }?.aliasOnlyExpression() - ?: error("Field not found in original table fields") + if (original is IExpressionAlias<*>) { + val aliases = query.set.fields.filterIsInstance>() + return aliases.find { it == original }?.let { + it.delegate.alias("$alias.${it.alias}").aliasOnlyExpression() + } ?: aliases.find { it.delegate == original }?.aliasOnlyExpression() + ?: error("Field not found in original table fields") + } else { + @Suppress("UNCHECKED_CAST") + return query.set.source.columns.find { it == original }?.clone() as? Column + ?: error("Column not found in original table") + } } operator fun get(original: ExpressionWithColumnType): ExpressionWithColumnType { - val aliases = query.set.fields.filterIsInstance>() - return ( - aliases.find { it == original }?.let { - it.delegate.alias("$alias.${it.alias}").aliasOnlyExpression() - } ?: aliases.find { it.delegate == original }?.aliasOnlyExpression() - ) as? ExpressionWithColumnType - ?: error("Field not found in original table fields") + return get(original as Expression) as ExpressionWithColumnType } override fun join( diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt index c8e358f3fd..57a61ea768 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt @@ -361,4 +361,17 @@ class AliasesTests : DatabaseTestsBase() { assertEquals("all_weight.task_weight", aliasExpressionString) } + + @Test + fun testColumnCastedToExpression() { + val tester = object : IntIdTable("Test") { + val weight = integer("weight") + } + + val column = tester.weight + val query: QueryAlias = tester.select(column).alias("all_weight") + + assertNotNull(query[column as Expression]) + assertNotNull(query[column as ExpressionWithColumnType]) + } } From 789e489e598dbe41b0cb77b44d18963d68b356a0 Mon Sep 17 00:00:00 2001 From: Tim Koopman Date: Thu, 17 Jul 2025 09:46:15 +0800 Subject: [PATCH 3/3] Fix usage for literals --- .../org/jetbrains/exposed/v1/core/Alias.kt | 19 +++++++++---------- .../exposed/v1/tests/shared/AliasesTests.kt | 11 +++++++++-- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt index 8fe364b783..dc51c41e67 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Alias.kt @@ -192,17 +192,16 @@ class QueryAlias(val query: AbstractQuery<*>, val alias: String) : ColumnSet() { get(original as Expression) as Column operator fun get(original: Expression): Expression { - if (original is IExpressionAlias<*>) { - val aliases = query.set.fields.filterIsInstance>() - return aliases.find { it == original }?.let { - it.delegate.alias("$alias.${it.alias}").aliasOnlyExpression() - } ?: aliases.find { it.delegate == original }?.aliasOnlyExpression() - ?: error("Field not found in original table fields") + @Suppress("UNCHECKED_CAST") + return if (original is Column<*>) { + query.set.source.columns.find { it == original }?.clone() ?: error("Column not found in original table") } else { - @Suppress("UNCHECKED_CAST") - return query.set.source.columns.find { it == original }?.clone() as? Column - ?: error("Column not found in original table") - } + val aliases = query.set.fields.filterIsInstance>() + return aliases.find { it == original } + ?.let { it.delegate.alias("$alias.${it.alias}").aliasOnlyExpression() } + ?: aliases.find { it.delegate == original }?.aliasOnlyExpression() + ?: error("Field not found in original table fields") + } as Expression } operator fun get(original: ExpressionWithColumnType): ExpressionWithColumnType { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt index 57a61ea768..564314102c 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/AliasesTests.kt @@ -371,7 +371,14 @@ class AliasesTests : DatabaseTestsBase() { val column = tester.weight val query: QueryAlias = tester.select(column).alias("all_weight") - assertNotNull(query[column as Expression]) - assertNotNull(query[column as ExpressionWithColumnType]) + fun Expression<*>.toExpressionString() = QueryBuilder(true) + .also { this.toQueryBuilder(it) } + .toString().lowercase() + + withTables(tester) { + assertEquals("all_weight.weight", query[column].toExpressionString()) + assertEquals("all_weight.weight", query[column as Expression].toExpressionString()) + assertEquals("all_weight.weight", query[column as ExpressionWithColumnType].toExpressionString()) + } } }