pgjdbc / r2dbc-postgresql

Postgresql R2DBC Driver
https://r2dbc.io
Apache License 2.0
1.01k stars 177 forks source link

Add the ability to register EnumCodec when enum data type is not in the Database yet. #660

Open man85 opened 3 weeks ago

man85 commented 3 weeks ago

DDL and DML migrations fails with enum data types. The application executes migrations that adding enum data type into Database, but Enum codecs tried to register during connection creation before this moment and they haven't been registered, because there weren't enum types in the Database yet. Than some code try to save or retrieve Enum data from the Database and will fail, because there is no Enum codec registered for connections. As workarkaround I tried to register fake codec. When app try to save or retrieve data some of methods canDecode, canEncode, canEncodeNull, getDataTypes of this fake codec will be invoked. After that the fake codec checks whether enum type exists in the Database or not and if it does the fake codec tries to register enum codec with

EnumCodec.builder()
                    .withEnum(databaseTypeName, kotlinClazz.java)
                    .withRegistrationPriority(EnumCodec.Builder.RegistrationPriority.LAST)
                    .build()
                    .register(connection, allocator, registry)

, but codec won't be found found in current canEncode method. canEncode' https://github.com/pgjdbc/r2dbc-postgresql/blob/main/src/main/java/io/r2dbc/postgresql/codec/CachedCodecLookup.java#L115 invocation occurs after cache lookup https://github.com/pgjdbc/r2dbc-postgresql/blob/main/src/main/java/io/r2dbc/postgresql/codec/CachedCodecLookup.java#L151. So if launch registiring new codec it will appear in codec cache, but it won't be found beacuse there will no cache lookup in the currentcanEncode' already. Possible solutions:

  1. Add the feature for registering new codec on the fly for connections that already created.
  2. Add the ability to close all connections (or does it already exist?), so new connections will be created with Enum codec successfully registered.
  3. Add the ability to register EnumCodec when enum data type is not in the Database yet.
mp911de commented 3 weeks ago

EnumCodec requires an OID. We cannot perform OID lookups during encoding or decoding values. You would need to run your migration scripts first and then obtain a new connection that is associated with the EnumCodec.

man85 commented 3 weeks ago

Why EnumCodec can't load OID lazyly? When I created codecs for user-defined types I did this trick

mp911de commented 3 weeks ago

Because R2DBC's bind-methods and the codec API uses synchronous methods. Lazym OID retrieval requires I/O that we cannot do in a synchronous flow.

man85 commented 3 weeks ago

What about adding codecs to CodecLookup on the fly?

man85 commented 3 weeks ago

You would need to run your migration scripts first and then obtain a new connection that is associated with the EnumCodec.

There is no control over all connections that have been created before, just over ConnectionFactory