Skip to content

Conversation

@ntkoopman
Copy link
Contributor

Description

Check types for QueryAlias.get() at runtime.

Relying purely on compile time checks causes issues when trying to dynamically select fields. E.g.:

object Tasks : UUIDTable("tasks") {
    val weight = integer("weight").nullable()
}

fun main() {
    val expression = Tasks.weight.sum()
    val alias = expression.alias("task_weight")
    val query = Tasks.select(Tasks.weight, alias).alias("all_weight")
    println(query[Tasks.weight]) // works
    println(query[alias]) // works
    val type = if (Random.nextInt(0..2) == 0) Tasks.weight else alias
    println(query[type]) // doesn't work half of the time
}

Type of Change

Please mark the relevant options with an "X":

  • Bug fix
  • New feature
  • Documentation update

Updates/remove existing public API methods:

  • Is breaking change

Affected databases:

  • MariaDB
  • Mysql5
  • Mysql8
  • Oracle
  • Postgres
  • SqlServer
  • H2
  • SQLite

Checklist

  • Unit tests are in place
  • The build is green (including the Detekt check)
  • All public methods affected by my PR has up to date API docs
  • Documentation for my change is up to date

Related Issues

?: error("Column not found in original table")

operator fun <T : Any?> get(original: Expression<T>): Expression<T> {
if (original is Column<T>) return get(original)
Copy link
Collaborator

Choose a reason for hiding this comment

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

As far as I know Column extends ExpressionWithColumnType, so these 2 checks could be joined

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, you are right. I've updated it to also handle Columns that are casted to Expression or ExpressionWithColumnType

operator fun <T : Any?> get(original: Expression<T>): Expression<T> {
if (original is Column<T>) return get(original)
if (original is ExpressionWithColumnType<T>) return get(original)
val aliases = query.set.fields.filterIsInstance<ExpressionAlias<T>>()
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be nice also to filter it for IExpressionAlias

@obabichevjb obabichevjb self-requested a review June 27, 2025 09:31
@obabichevjb
Copy link
Collaborator

It looks like there is a problem with PR. The test test union with expression and alias in org.jetbrains.exposed.v1.r2dbc.sql.tests.shared.dml.UnionTests fails with java.lang.IllegalStateException: Column not found in original table. Could you check if it's the result of the changes in the PR?

@ntkoopman
Copy link
Contributor Author

Fixed by inverting the if. This did make me realise you can't get a literal on an alias, and I'm not sure how it should be fixed (probably not in this PR anyway).

select my_alias.??? from (select 10 from some_table) my_alias

The union also generates

SELECT unionAlias.id, exp1, exp2 FROM (SELECT users.id, 10 exp1, 'aaa' exp2 FROM users WHERE users.id = 'andrey' UNION ALL SELECT users.id, 100, 'bbb' FROM users WHERE users.id = 'andrey') unionAlias

while it should probably reference unionAlias.exp1 and unionAlias.exp2

fun Expression<*>.toExpressionString() = QueryBuilder(true).also { this.toQueryBuilder(it) }.toString()

withTables(tester) {
assertEquals("all_weight.weight", query[column].toExpressionString())
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be nice to replace it with (added lowercase()):

assertEquals("all_weight.weight", query[column].toExpressionString().lowercase())
            assertEquals("all_weight.weight", query[column as Expression<Int>].toExpressionString().lowercase())
            assertEquals("all_weight.weight", query[column as ExpressionWithColumnType<Int>].toExpressionString().lowercase())

otherwise it fails on some dialects like H2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, fixed, my apologies

@obabichevjb
Copy link
Collaborator

In general PR looks good, the added test fails for some databases but it fixable. I'd merge the PR if you fix the test to make it working for all the dialects.

@obabichevjb obabichevjb merged commit 0f2bdf8 into JetBrains:main Jul 22, 2025
3 of 5 checks passed
@obabichevjb
Copy link
Collaborator

Thank you for contributing, it would be delivered in the next version (should be 1.0.0-beta-5)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants