From de1b90c774ed790d9e06d1dcc4658b4e0e2383dc Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 2 Oct 2024 09:25:43 +0200 Subject: [PATCH] Introduce `ObjectCodec` to support `bindNull(Object.class)` [resolves #664] --- .../r2dbc/postgresql/codec/DefaultCodecs.java | 6 +- .../r2dbc/postgresql/codec/ObjectCodec.java | 72 +++++++++++++++++++ .../codec/ObjectCodecIntegrationTests.java | 46 ++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/r2dbc/postgresql/codec/ObjectCodec.java create mode 100644 src/test/java/io/r2dbc/postgresql/codec/ObjectCodecIntegrationTests.java diff --git a/src/main/java/io/r2dbc/postgresql/codec/DefaultCodecs.java b/src/main/java/io/r2dbc/postgresql/codec/DefaultCodecs.java index 5e5d9473..cb007ad6 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/DefaultCodecs.java +++ b/src/main/java/io/r2dbc/postgresql/codec/DefaultCodecs.java @@ -268,7 +268,7 @@ EncodedParameter encodeParameterValue(Object value, @Nullable PostgresTypeIdenti if (dataType == null) { if (parameterValue == null) { - throw new IllegalArgumentException(String.format("Cannot encode null value %s using type inference", value)); + return ObjectCodec.INSTANCE.encodeNull(); } Codec codec = this.codecLookup.findEncodeCodec(parameterValue); @@ -294,6 +294,10 @@ EncodedParameter encodeParameterValue(Object value, @Nullable PostgresTypeIdenti public EncodedParameter encodeNull(Class type) { Assert.requireNonNull(type, "type must not be null"); + if (type == Object.class) { + return ObjectCodec.INSTANCE.encodeNull(); + } + Codec codec = this.codecLookup.findEncodeNullCodec(type); if (codec != null) { return codec.encodeNull(); diff --git a/src/main/java/io/r2dbc/postgresql/codec/ObjectCodec.java b/src/main/java/io/r2dbc/postgresql/codec/ObjectCodec.java new file mode 100644 index 00000000..1b5c4d6f --- /dev/null +++ b/src/main/java/io/r2dbc/postgresql/codec/ObjectCodec.java @@ -0,0 +1,72 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.r2dbc.postgresql.codec; + +import io.netty.buffer.ByteBuf; +import io.r2dbc.postgresql.client.EncodedParameter; +import io.r2dbc.postgresql.message.Format; + +import static io.r2dbc.postgresql.codec.PostgresqlObjectId.UNSPECIFIED; +import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT; + +final class ObjectCodec implements Codec { + + static final ObjectCodec INSTANCE = new ObjectCodec(); + + private ObjectCodec() { + } + + @Override + public EncodedParameter encodeNull() { + return AbstractCodec.createNull(FORMAT_TEXT, UNSPECIFIED); + } + + public EncodedParameter encodeNull(int dataType) { + return new EncodedParameter(FORMAT_TEXT, dataType, EncodedParameter.NULL_VALUE); + } + + @Override + public boolean canDecode(int dataType, Format format, Class type) { + return false; + } + + @Override + public boolean canEncode(Object value) { + return false; + } + + @Override + public boolean canEncodeNull(Class type) { + return Object.class.equals(type); + } + + @Override + public Object decode(ByteBuf buffer, int dataType, Format format, Class type) { + throw new UnsupportedOperationException(); + } + + @Override + public EncodedParameter encode(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public EncodedParameter encode(Object value, int dataType) { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/test/java/io/r2dbc/postgresql/codec/ObjectCodecIntegrationTests.java b/src/test/java/io/r2dbc/postgresql/codec/ObjectCodecIntegrationTests.java new file mode 100644 index 00000000..4a71650f --- /dev/null +++ b/src/test/java/io/r2dbc/postgresql/codec/ObjectCodecIntegrationTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.r2dbc.postgresql.codec; + +import io.r2dbc.postgresql.AbstractIntegrationTests; +import org.junit.jupiter.api.Test; +import reactor.test.StepVerifier; + +/** + * Integration tests for {@link ObjectCodec} usage. + */ +class ObjectCodecIntegrationTests extends AbstractIntegrationTests { + + @Test + void shouldEncodeNull() { + + SERVER.getJdbcOperations().execute("DROP TABLE IF EXISTS test"); + SERVER.getJdbcOperations().execute("CREATE TABLE test (ci INT4, cs VARCHAR)"); + + this.connection.createStatement("INSERT INTO test VALUES($1, $2)") + .bindNull("$1", Object.class) + .bindNull("$2", Object.class) + .execute() + .flatMap(it -> it.getRowsUpdated()) + .as(StepVerifier::create) + .expectNext(1L) + .verifyComplete(); + + SERVER.getJdbcOperations().execute("DROP TABLE test"); + } + +}