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

Vertx client doesn't deal correctly with a byte array set to null #1477

Open
wjglerum opened this issue Dec 3, 2024 · 4 comments
Open

Vertx client doesn't deal correctly with a byte array set to null #1477

wjglerum opened this issue Dec 3, 2024 · 4 comments
Assignees
Milestone

Comments

@wjglerum
Copy link

wjglerum commented Dec 3, 2024

Questions

After updating our application to Quarkus 3.16.4 see https://github.com/quarkusio/quarkus/releases/tag/3.16.4 we noticed that we got a test failure for the case when we try to persist a Hibernate entity that has a byte array field set to null. This used to work before upgrading to Quarkus 3.16.4 which also upgrades Vert.x to 4.5.11

We get the following exception:

java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "value" is null
	at io.vertx.sqlclient.impl.ErrorMessageFactory.buildWhenArgumentsTypeNotMatched(ErrorMessageFactory.java:23)
	at io.vertx.pgclient.impl.codec.PgParamDesc.prepare(PgParamDesc.java:57)
	at io.vertx.pgclient.impl.codec.PgPreparedStatement.prepare(PgPreparedStatement.java:57)
	at io.vertx.sqlclient.impl.command.ExtendedQueryCommand.prepare(ExtendedQueryCommand.java:122)
	at io.vertx.sqlclient.impl.SocketConnectionBase.lambda$prepareCommand$4(SocketConnectionBase.java:295)
	at io.vertx.sqlclient.impl.command.CommandResponse.fire(CommandResponse.java:46)
	at io.vertx.sqlclient.impl.SocketConnectionBase.handleMessage(SocketConnectionBase.java:324)
	at io.vertx.pgclient.impl.PgSocketConnection.handleMessage(PgSocketConnection.java:114)
	at io.vertx.sqlclient.impl.SocketConnectionBase.lambda$init$0(SocketConnectionBase.java:137)
	at io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:342)
	at io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:335)
	at io.vertx.core.net.impl.NetSocketImpl.handleMessage(NetSocketImpl.java:389)
	at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:159)
	at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:153)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
	at io.vertx.pgclient.impl.codec.PgDecoder.fireCommandResponse(PgDecoder.java:52)
	at io.vertx.pgclient.impl.codec.PgCommandCodec.handleReadyForQuery(PgCommandCodec.java:133)
	at io.vertx.pgclient.impl.codec.PrepareStatementCommandCodec.handleReadyForQuery(PrepareStatementCommandCodec.java:96)
	at io.vertx.pgclient.impl.codec.PgDecoder.decodeReadyForQuery(PgDecoder.java:248)
	at io.vertx.pgclient.impl.codec.PgDecoder.channelRead(PgDecoder.java:107)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:1583)

Which can be resolved to setting the entity field to an empty byte array instead of null. Then it is able to persist the entity.

Version

Vert.x 4.5.11

Context

Quarkus app with Hibernate Reactive and Reactive PostgreSQL Client

Do you have a reproducer?

no yet

But can be reproduced by creating a Hibernate entity with a field like this:

    @Lob
    @Column(name = "data")
    public byte[] data;

And set the field to null, then it doesn't work. If you set it to an empty byte array it will work again.

Extra

Looks like this was introduced by this PR here #1464

@wjglerum wjglerum added the bug label Dec 3, 2024
@vietj vietj added this to the 4.5.12 milestone Dec 4, 2024
@ProgMaq
Copy link

ProgMaq commented Dec 5, 2024

Same error trace, but using io.vertx.pgclient.data.Point.

I changed Tuple.addValue(point) to Tuple.addValue(point != null ? point : NullValue.of(Point.class)) but the error still appears.

@tsegismont tsegismont self-assigned this Jan 3, 2025
@tsegismont
Copy link
Contributor

Thank you for the report @wjglerum and for the additional details @ProgMaq

Can you please share the definition of the database table that you have used in these cases ? Thank you

@unlazy
Copy link

unlazy commented Jan 13, 2025

Hi there!

I have the same error, but for io.vertx.pgclient.data.Interval. This is because of io.vertx.pgclient.impl.codec.DataType.INTERVAL definition:

INTERVAL(1186, true, Interval.class, JDBCType.DATE),

There is no custom ParamExtractor<T> for this type which leads to default DefaultParamExtractor<T>:

    @Override
    public T get(TupleInternal tuple, int idx) {
      Object value = tuple.getValue(idx);
      if (value != null && encodingType.isAssignableFrom(value.getClass())) {
        return encodingType.cast(value);
      }
      throw FAILURE;
    }

In case of the real null value this extractor throws an exception above. Same for io.vertx.pgclient.data.Point.

Such behaviour was definitely introduced in #1464

@wjglerum
Copy link
Author

wjglerum commented Jan 13, 2025

Thank you for the report @wjglerum and for the additional details @ProgMaq

Can you please share the definition of the database table that you have used in these cases ? Thank you

We have this entity:

@Entity
@Table(name = "personal_data")
public class PersonalDataEntity extends PanacheEntityBase {

    @Id
    @Column(name = "person_id")
    public String personId;

    @Column(name = "updated")
    public LocalDateTime updated;

    @Column(name = "photo_content_type")
    public String photoContentType;

    @Lob
    @Column(name = "photo_data")
    public byte[] photoData;
}

Hibernate generated the following schema for that entity:

    create table personal_data (
        person_id varchar(255) not null,
        photo_content_type varchar(255),
        photo_data oid,
        updated timestamp(6),
        primary key (person_id)
    );

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

No branches or pull requests

5 participants