spring-projects / spring-data-relational

Spring Data Relational. Home of Spring Data JDBC and Spring Data R2DBC.
https://spring.io/projects/spring-data-jdbc
Apache License 2.0
753 stars 345 forks source link

Regression in Enum Converter Behaviour with Postgres Enums #1705

Closed micahsmith closed 1 month ago

micahsmith commented 8 months ago

Upgrading 3.1.7 -> 3.2.1 Corretto 17 Kotlin 1.9.20 Postgres 13

I've been using Spring Data JDBC for some time in a project with Postgres's native enums without issue. Typically the setup would look something like this (using Kotlin in the project):

// Email.kt
data class Email(
  @Id val id: Long?,
  val publicId: UUID,
  val queueState: QueueState,
  ...
)
// QueueState.kt
enum class QueueState {
  PENDING,
  SUBMITTED,
  ERROR,
  CANCELLED,
}
// EmailRepository.kt
@Repository
interface EmailRepository : CrudRepository<Email, Int> {
  fun findAllByQueueState(queueState: QueueState): List<Email>
}
// QueueStateWritingConverter.kt
@WritingConverter
internal class QueueStateConverter : Converter<QueueState, JdbcValue> {
  override fun convert(source: EmailQueueState): JdbcValue {
    return JdbcValue.of(source.name, JDBCType.OTHER)
  }
}
// DataConfig.kt
@Configuration
class DataConfig : AbstractJdbcConfiguration() {
  override fun userConverters(): List<*> {
    return listOf(
      QueueStateConverter()
    )
  }
}

The behaviour that has been exhibited up until upgrading to 3.2.x (and what I hope is expected behaviour) would be that the call to findAllByQueueState succeeds without issue. Since upgrading the 3.2.x, it is now failing with the following error:

Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: queue_state = character varying

    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:190) ~[postgresql-42.6.0.jar:42.6.0]
    at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:134) ~[postgresql-42.6.0.jar:42.6.0]
    at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-5.0.1.jar:na]
    at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java) ~[HikariCP-5.0.1.jar:na]
    at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:732) ~[spring-jdbc-6.1.2.jar:6.1.2]
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:658) ~[spring-jdbc-6.1.2.jar:6.1.2]
    ... 77 common frames omitted

Now, I should note that, as far as I can tell, this sort of converter setup still works just fine when serializing/deserializing between Java/Kotlin enum fields and Postgres's native enums when the enum is not one of the query parameters. It is only in this particular case, where the QueueState enum is passed to the query that the query generation produces this error.

Right now there are only two workable solutions: (1) downgrade to 3.1.x, or (2) use @Query and cast the parameter to the enum in the manual query. In my circumstances, either works, but (2) is not a particularly scalable solution, and mostly works for me because the program effected is itself quite small.

schauder commented 8 months ago

Please provide a Minimimal Reproducable Example, preferable as a Github repository. Make sure to include the database, either as an in memory database or if that is not possible using Testcontainers.

Please also check against the most recent Snapshot since it contains a couple of bugfixes that might resolve the issue.

micahsmith commented 8 months ago

Here is the repo: https://github.com/micahsmith/spring-data-enum-replication. It should be sufficient to just ./gradlew :test to get it to run. The test named cannotQueryWithEnum is the one that is of interest. The repo is running the 3.2.2 snapshot and the error still gets thrown. However, I tried it out with the 3.3.0 snapshot and it seems to be working fine.

schauder commented 2 months ago

I'm not able to run your example with Spring Boot 3.3.x Looks like the datasource is not ready??? Although that part works in 3.2.x. Could that a Kotlin/lazy issue?

spring-projects-issues commented 1 month ago

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-projects-issues commented 1 month ago

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.