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-474 Unexpected value of type when using a ColumnTransfor… #2191

Merged

Conversation

obabichevjb
Copy link
Collaborator

Description

An interesting issue with the new column conversion feature was found.

Transformed value actually cannot be read from the insert statement directly and via DAO entity.

The reason is that statements and entities work via ResultRow that has mappting of expressions to the values. And ResultRow works in the way that it accepts as values both user values and db values. It works because internally ResultRow calls ColumnType::valueFromDB() for all the values, and ColumnTypes should convert the values into correct kotlin types.

And the column types are implemented in the way that if they receive already valid value, they just return it. For example IntegerColumnType in this context looks like this:

override fun valueFromDB(value: Any): Int = when (value) {
    is Int -> value
    is Number -> value.toInt()
    is String -> value.toInt()
    else -> error("Unexpected value of type Int: $value of ${value::class.qualifiedName}")
}

So it was not a problem to recall valueFromDB several times for the value.

But with introduced transforms this logic breaks, because every try to call valueFromDB method leads to the transformation process.

So the flow was looking like:

  • statement = table.insert { it[field] = CustomData() } // insert value, so ResultRow will contain CustomData object
  • statement[field] fails, because it tries to call expression.columnType.valueFromDB(raw) internally, where raw is CustomData object. It leads to class cast exception.

Solution

At the current I found a way to unwrap (recursively for chained transforms) the values before setting them to ResultRow. It will allow keeping only original column type values inside ResultRow as before.

Alternatively, I tried two other options.

  1. Store in the ResultRow only really raw values (the values produced by ColumnType::valueToDB method). But it causes other problems, the main reason, as I understand, is that the values which we put to the database and the values we get back from the database are not always of the same data types, and not all column types are ready for it. And potentially it adds lots of unnecessary conversions.

  2. Store in the ResultRow only wrapped values, so no conversion is necessary. It can be an interesting solution, but it would require much more refactoring (as I can see now, but not sure), because many places of creating ResultRow should be extended. The positive side here is that the user will get the ResultRow object with final data, and no extra conversion magic would be applied on reading the data. I have a feeling that I will face many obstacles in this way, so I didn't invest more time here, but if you see that it's a good way in general, I could try to do a separate refactoring.


Type of Change

  • Bug fix

Affected databases:

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

Related Issues

EXPOSED-474 Unexpected value of type when using a ColumnTransformer in a DAO object

@obabichevjb obabichevjb merged commit ca45468 into main Aug 7, 2024
5 checks passed
@obabichevjb obabichevjb deleted the obabichev/exposed-474-column-transformer-unexprected-value branch August 7, 2024 13:14
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.

3 participants