diff --git a/src/main/java/io/r2dbc/postgresql/PostgresqlRow.java b/src/main/java/io/r2dbc/postgresql/PostgresqlRow.java index 867db1b5..d1d7f40a 100644 --- a/src/main/java/io/r2dbc/postgresql/PostgresqlRow.java +++ b/src/main/java/io/r2dbc/postgresql/PostgresqlRow.java @@ -83,7 +83,7 @@ public T get(int index, Class type) { Assert.requireNonNull(type, "type must not be null"); requireNotReleased(); - return decode(getColumn(index), type); + return decode(getColumn(index), null, type); } @Nullable @@ -93,7 +93,7 @@ public T get(String name, Class type) { Assert.requireNonNull(type, "type must not be null"); requireNotReleased(); - return decode(getColumn(name), type); + return decode(getColumn(name), name, type); } @Override @@ -102,9 +102,23 @@ public io.r2dbc.postgresql.api.PostgresqlRowMetadata getMetadata() { } @Nullable - private T decode(int index, Class type) { + @SuppressWarnings("unchecked") + private T decode(int index, @Nullable String name, Class type) { ByteBuf data = this.data[index]; if (data == null) { + if (type.isPrimitive()) { + + String message; + if (name != null) { + message = String.format("Value at column '%s' is null. Cannot return value for primitive '%s'", name, + type.getName()); + } else { + message = String.format("Value at column index %d is null. Cannot return value for primitive '%s'", index, + type.getName()); + } + + throw new NullPointerException(message); + } return null; } @@ -114,7 +128,8 @@ private T decode(int index, Class type) { T decoded = this.context.getCodecs().decode(data, field.getDataType(), field.getFormat(), type); - return type.cast(postProcessResult(decoded)); + Object result = postProcessResult(decoded); + return type.isPrimitive() ? (T) result : type.cast(result); } finally { data.readerIndex(readerIndex); diff --git a/src/main/java/io/r2dbc/postgresql/codec/BooleanCodec.java b/src/main/java/io/r2dbc/postgresql/codec/BooleanCodec.java index 5cdfb4b8..9334646a 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/BooleanCodec.java +++ b/src/main/java/io/r2dbc/postgresql/codec/BooleanCodec.java @@ -26,12 +26,17 @@ import static io.r2dbc.postgresql.codec.PostgresqlObjectId.BOOL; import static io.r2dbc.postgresql.codec.PostgresqlObjectId.BOOL_ARRAY; -final class BooleanCodec extends BuiltinCodecSupport { +final class BooleanCodec extends BuiltinCodecSupport implements PrimitiveWrapperCodecProvider { BooleanCodec(ByteBufAllocator byteBufAllocator) { super(Boolean.class, byteBufAllocator, BOOL, BOOL_ARRAY, it -> it ? "t" : "f"); } + @Override + public PrimitiveCodec getPrimitiveCodec() { + return new PrimitiveCodec<>(Boolean.TYPE, Boolean.class, this); + } + @Override Boolean doDecode(ByteBuf buffer, PostgresTypeIdentifier dataType, @Nullable Format format, @Nullable Class type) { Assert.requireNonNull(buffer, "byteBuf must not be null"); diff --git a/src/main/java/io/r2dbc/postgresql/codec/ByteCodec.java b/src/main/java/io/r2dbc/postgresql/codec/ByteCodec.java index daa74e17..65230331 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/ByteCodec.java +++ b/src/main/java/io/r2dbc/postgresql/codec/ByteCodec.java @@ -23,7 +23,7 @@ import io.r2dbc.postgresql.util.Assert; import reactor.util.annotation.Nullable; -final class ByteCodec extends AbstractCodec implements ArrayCodecDelegate { +final class ByteCodec extends AbstractCodec implements ArrayCodecDelegate, PrimitiveWrapperCodecProvider { private final ShortCodec delegate; @@ -34,6 +34,11 @@ final class ByteCodec extends AbstractCodec implements ArrayCodecDelegate< this.delegate = new ShortCodec(byteBufAllocator); } + @Override + public PrimitiveCodec getPrimitiveCodec() { + return new PrimitiveCodec<>(Byte.TYPE, Byte.class, this); + } + @Override public EncodedParameter encodeNull() { return this.delegate.encodeNull(); diff --git a/src/main/java/io/r2dbc/postgresql/codec/CharacterCodec.java b/src/main/java/io/r2dbc/postgresql/codec/CharacterCodecProvider.java similarity index 88% rename from src/main/java/io/r2dbc/postgresql/codec/CharacterCodec.java rename to src/main/java/io/r2dbc/postgresql/codec/CharacterCodecProvider.java index dfd51783..efb3eba5 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/CharacterCodec.java +++ b/src/main/java/io/r2dbc/postgresql/codec/CharacterCodecProvider.java @@ -23,17 +23,22 @@ import io.r2dbc.postgresql.util.Assert; import reactor.util.annotation.Nullable; -final class CharacterCodec extends AbstractCodec implements ArrayCodecDelegate { +final class CharacterCodecProvider extends AbstractCodec implements ArrayCodecDelegate, PrimitiveWrapperCodecProvider { private final StringCodec delegate; - CharacterCodec(ByteBufAllocator byteBufAllocator) { + CharacterCodecProvider(ByteBufAllocator byteBufAllocator) { super(Character.class); Assert.requireNonNull(byteBufAllocator, "byteBufAllocator must not be null"); this.delegate = new StringCodec(byteBufAllocator); } + @Override + public PrimitiveCodec getPrimitiveCodec() { + return new PrimitiveCodec<>(Character.TYPE, Character.class, this); + } + @Override public EncodedParameter encodeNull() { return this.delegate.encodeNull(); diff --git a/src/main/java/io/r2dbc/postgresql/codec/DefaultCodecs.java b/src/main/java/io/r2dbc/postgresql/codec/DefaultCodecs.java index cb007ad6..3b8e51c6 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/DefaultCodecs.java +++ b/src/main/java/io/r2dbc/postgresql/codec/DefaultCodecs.java @@ -111,7 +111,7 @@ private static List> getDefaultCodecs(ByteBufAllocator byteBufAllocator new BigDecimalCodec(byteBufAllocator), new BigIntegerCodec(byteBufAllocator), new BooleanCodec(byteBufAllocator), - new CharacterCodec(byteBufAllocator), + new CharacterCodecProvider(byteBufAllocator), new DoubleCodec(byteBufAllocator), new FloatCodec(byteBufAllocator), new InetAddressCodec(byteBufAllocator), @@ -159,7 +159,7 @@ private static List> getDefaultCodecs(ByteBufAllocator byteBufAllocator new PolygonCodec(byteBufAllocator) )); - List> defaultArrayCodecs = new ArrayList<>(); + List> additionalCodecs = new ArrayList<>(); for (Codec codec : codecs) { @@ -171,18 +171,22 @@ private static List> getDefaultCodecs(ByteBufAllocator byteBufAllocator if (codec instanceof BoxCodec) { // BOX[] uses a ';' as a delimiter (i.e. "{(3.7,4.6),(1.9,2.8);(5,7),(1.5,3.3)}") - defaultArrayCodecs.add(new ArrayCodec(byteBufAllocator, delegate.getArrayDataType(), delegate, componentType, (byte) ';')); + additionalCodecs.add(new ArrayCodec(byteBufAllocator, delegate.getArrayDataType(), delegate, componentType, (byte) ';')); } else if (codec instanceof AbstractNumericCodec) { - defaultArrayCodecs.add(new ConvertingArrayCodec(byteBufAllocator, delegate, componentType, ConvertingArrayCodec.NUMERIC_ARRAY_TYPES)); + additionalCodecs.add(new ConvertingArrayCodec(byteBufAllocator, delegate, componentType, ConvertingArrayCodec.NUMERIC_ARRAY_TYPES)); } else if (codec instanceof AbstractTemporalCodec) { - defaultArrayCodecs.add(new ConvertingArrayCodec(byteBufAllocator, delegate, componentType, ConvertingArrayCodec.DATE_ARRAY_TYPES)); + additionalCodecs.add(new ConvertingArrayCodec(byteBufAllocator, delegate, componentType, ConvertingArrayCodec.DATE_ARRAY_TYPES)); } else { - defaultArrayCodecs.add(new ArrayCodec(byteBufAllocator, delegate, componentType)); + additionalCodecs.add(new ArrayCodec(byteBufAllocator, delegate, componentType)); } } + + if (codec instanceof PrimitiveWrapperCodecProvider) { + additionalCodecs.add(((PrimitiveWrapperCodecProvider) codec).getPrimitiveCodec()); + } } - codecs.addAll(defaultArrayCodecs); + codecs.addAll(additionalCodecs); return codecs; } diff --git a/src/main/java/io/r2dbc/postgresql/codec/DoubleCodec.java b/src/main/java/io/r2dbc/postgresql/codec/DoubleCodec.java index 81b972ad..607e7a6c 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/DoubleCodec.java +++ b/src/main/java/io/r2dbc/postgresql/codec/DoubleCodec.java @@ -25,12 +25,17 @@ import static io.r2dbc.postgresql.codec.PostgresqlObjectId.FLOAT8; import static io.r2dbc.postgresql.codec.PostgresqlObjectId.FLOAT8_ARRAY; -final class DoubleCodec extends AbstractNumericCodec { +final class DoubleCodec extends AbstractNumericCodec implements PrimitiveWrapperCodecProvider { DoubleCodec(ByteBufAllocator byteBufAllocator) { super(Double.class, byteBufAllocator); } + @Override + public PrimitiveCodec getPrimitiveCodec() { + return new PrimitiveCodec<>(Double.TYPE, Double.class, this); + } + @Override Double doDecode(ByteBuf buffer, PostgresTypeIdentifier dataType, Format format, @Nullable Class type) { Assert.requireNonNull(buffer, "byteBuf must not be null"); diff --git a/src/main/java/io/r2dbc/postgresql/codec/FloatCodec.java b/src/main/java/io/r2dbc/postgresql/codec/FloatCodec.java index de3f88ef..8511b761 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/FloatCodec.java +++ b/src/main/java/io/r2dbc/postgresql/codec/FloatCodec.java @@ -25,12 +25,17 @@ import static io.r2dbc.postgresql.codec.PostgresqlObjectId.FLOAT4; import static io.r2dbc.postgresql.codec.PostgresqlObjectId.FLOAT4_ARRAY; -final class FloatCodec extends AbstractNumericCodec { +final class FloatCodec extends AbstractNumericCodec implements PrimitiveWrapperCodecProvider { FloatCodec(ByteBufAllocator byteBufAllocator) { super(Float.class, byteBufAllocator); } + @Override + public PrimitiveCodec getPrimitiveCodec() { + return new PrimitiveCodec<>(Float.TYPE, Float.class, this); + } + @Override Float doDecode(ByteBuf buffer, PostgresTypeIdentifier dataType, Format format, @Nullable Class type) { Assert.requireNonNull(buffer, "byteBuf must not be null"); diff --git a/src/main/java/io/r2dbc/postgresql/codec/IntegerCodec.java b/src/main/java/io/r2dbc/postgresql/codec/IntegerCodec.java index 79f7d67f..15974af9 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/IntegerCodec.java +++ b/src/main/java/io/r2dbc/postgresql/codec/IntegerCodec.java @@ -25,12 +25,17 @@ import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT4; import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT4_ARRAY; -final class IntegerCodec extends AbstractNumericCodec { +final class IntegerCodec extends AbstractNumericCodec implements PrimitiveWrapperCodecProvider { IntegerCodec(ByteBufAllocator byteBufAllocator) { super(Integer.class, byteBufAllocator); } + @Override + public PrimitiveCodec getPrimitiveCodec() { + return new PrimitiveCodec<>(Integer.TYPE, Integer.class, this); + } + @Override Integer doDecode(ByteBuf buffer, PostgresTypeIdentifier dataType, @Nullable Format format, @Nullable Class type) { Assert.requireNonNull(buffer, "byteBuf must not be null"); diff --git a/src/main/java/io/r2dbc/postgresql/codec/LongCodec.java b/src/main/java/io/r2dbc/postgresql/codec/LongCodec.java index de415921..5c916e63 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/LongCodec.java +++ b/src/main/java/io/r2dbc/postgresql/codec/LongCodec.java @@ -25,12 +25,17 @@ import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT8; import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT8_ARRAY; -final class LongCodec extends AbstractNumericCodec { +final class LongCodec extends AbstractNumericCodec implements PrimitiveWrapperCodecProvider { LongCodec(ByteBufAllocator byteBufAllocator) { super(Long.class, byteBufAllocator); } + @Override + public PrimitiveCodec getPrimitiveCodec() { + return new PrimitiveCodec<>(Long.TYPE, Long.class, this); + } + @Override Long doDecode(ByteBuf buffer, PostgresTypeIdentifier dataType, Format format, @Nullable Class type) { Assert.requireNonNull(buffer, "byteBuf must not be null"); diff --git a/src/main/java/io/r2dbc/postgresql/codec/PrimitiveCodec.java b/src/main/java/io/r2dbc/postgresql/codec/PrimitiveCodec.java new file mode 100644 index 00000000..ffc376b6 --- /dev/null +++ b/src/main/java/io/r2dbc/postgresql/codec/PrimitiveCodec.java @@ -0,0 +1,85 @@ +/* + * 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 io.r2dbc.postgresql.util.Assert; + +/** + * @since 1.0.6 + */ +final class PrimitiveCodec implements Codec { + + private final Class primitiveType; + + private final Class wrapperType; + + private final Codec boxedCodec; + + public PrimitiveCodec(Class primitiveType, Class wrapperType, Codec boxedCodec) { + + this.primitiveType = Assert.requireNonNull(primitiveType, "primitiveType must not be null"); + this.wrapperType = Assert.requireNonNull(wrapperType, "wrapperType must not be null"); + this.boxedCodec = Assert.requireNonNull(boxedCodec, "boxedCodec must not be null"); + + Assert.isTrue(primitiveType.isPrimitive(), "primitiveType must be a primitive type"); + } + + @Override + public boolean canDecode(int dataType, Format format, Class type) { + return this.primitiveType.equals(type) && this.boxedCodec.canDecode(dataType, format, this.wrapperType); + } + + @Override + public boolean canEncode(Object value) { + return this.boxedCodec.canEncode(value); + } + + @Override + public boolean canEncodeNull(Class type) { + return this.primitiveType.equals(type) && this.boxedCodec.canEncodeNull(this.wrapperType); + } + + @Override + public Object decode(ByteBuf buffer, int dataType, Format format, Class type) { + + T value = this.boxedCodec.decode(buffer, dataType, format, this.wrapperType); + + if (value == null) { + throw new NullPointerException("value for primitive type " + this.primitiveType.getName() + " is null"); + } + + return value; + } + + @Override + public EncodedParameter encode(Object value) { + return this.boxedCodec.encode(value); + } + + @Override + public EncodedParameter encode(Object value, int dataType) { + return this.boxedCodec.encode(value, dataType); + } + + @Override + public EncodedParameter encodeNull() { + return this.boxedCodec.encodeNull(); + } +} diff --git a/src/main/java/io/r2dbc/postgresql/codec/PrimitiveWrapperCodecProvider.java b/src/main/java/io/r2dbc/postgresql/codec/PrimitiveWrapperCodecProvider.java new file mode 100644 index 00000000..a44da805 --- /dev/null +++ b/src/main/java/io/r2dbc/postgresql/codec/PrimitiveWrapperCodecProvider.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * Codec for a primitive wrapper such as {@link DoubleCodec} for {@link Double}. {@link #getPrimitiveCodec()} is expected to return a {@code double} codec. + * + * @since 1.0.6 + */ +interface PrimitiveWrapperCodecProvider { + + /** + * Return the codec for its primitive type. + * + * @return the codec for its primitive type + */ + PrimitiveCodec getPrimitiveCodec(); +} diff --git a/src/main/java/io/r2dbc/postgresql/codec/ShortCodec.java b/src/main/java/io/r2dbc/postgresql/codec/ShortCodec.java index 2a8218d8..d0ffe97a 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/ShortCodec.java +++ b/src/main/java/io/r2dbc/postgresql/codec/ShortCodec.java @@ -25,12 +25,17 @@ import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT2; import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT2_ARRAY; -final class ShortCodec extends AbstractNumericCodec { +final class ShortCodec extends AbstractNumericCodec implements PrimitiveWrapperCodecProvider { ShortCodec(ByteBufAllocator byteBufAllocator) { super(Short.class, byteBufAllocator); } + @Override + public PrimitiveCodec getPrimitiveCodec() { + return new PrimitiveCodec<>(Short.TYPE, Short.class, this); + } + @Override Short doDecode(ByteBuf buffer, PostgresTypeIdentifier dataType, Format format, @Nullable Class type) { Assert.requireNonNull(buffer, "byteBuf must not be null"); diff --git a/src/test/java/io/r2dbc/postgresql/AbstractCodecIntegrationTests.java b/src/test/java/io/r2dbc/postgresql/AbstractCodecIntegrationTests.java index be231a8b..19305403 100644 --- a/src/test/java/io/r2dbc/postgresql/AbstractCodecIntegrationTests.java +++ b/src/test/java/io/r2dbc/postgresql/AbstractCodecIntegrationTests.java @@ -145,6 +145,7 @@ void booleanArray() { @Test void booleanPrimitive() { testCodec(Boolean.class, true, "BOOL"); + testCodec(Boolean.TYPE, true, "BOOL"); } @Test @@ -161,12 +162,14 @@ void binary() { @Test void bytePrimitive() { testCodec(Byte.class, (byte) 10, "INT2"); + testCodec(Byte.TYPE, (byte) 10, "INT2"); } @Test void charPrimitive() { testCodec(Character.class, 'a', "BPCHAR(1)"); testCodec(Character.class, 'a', "VARCHAR(1)"); + testCodec(Character.TYPE, 'a', "VARCHAR(1)"); } @Test @@ -261,6 +264,7 @@ void doublePrimitive() { testCodec(Double.class, 100.1, "DECIMAL"); testCodec(Double.class, 100.1, "FLOAT4"); testCodec(Double.class, 100.1, "FLOAT8"); + testCodec(Double.TYPE, 100.1, "FLOAT8"); testCodec(Double.class, 100.1, "DECIMAL", R2dbcType.DECIMAL); testCodec(Double.class, 100.1, "FLOAT4", R2dbcType.FLOAT); @@ -295,6 +299,7 @@ void floatPrimitive() { testCodec(Float.class, 100.1f, "DECIMAL"); testCodec(Float.class, 100.1f, "FLOAT4"); testCodec(Float.class, 100.1f, "FLOAT8"); + testCodec(Float.TYPE, 100.1f, "FLOAT8"); testCodec(Float.class, 100.1f, "DECIMAL", R2dbcType.DECIMAL); testCodec(Float.class, 100.1f, "FLOAT4", R2dbcType.FLOAT); @@ -346,6 +351,7 @@ void intArray() { void intPrimitive() { testCodec(Integer.class, 100, "INT2"); testCodec(Integer.class, 100, "INT4"); + testCodec(Integer.TYPE, 100, "INT4"); testCodec(Integer.class, 100, "INT8"); testCodec(Integer.class, 100, "OID"); testCodec(Integer.class, 100, "NUMERIC"); @@ -488,6 +494,7 @@ void longPrimitive() { testCodec(Long.class, 100L, "INT2"); testCodec(Long.class, 100L, "INT4"); testCodec(Long.class, 100L, "INT8"); + testCodec(Long.TYPE, 100L, "INT8"); testCodec(Long.class, 2314556683L, "OID"); testCodec(Long.class, 100L, "NUMERIC"); testCodec(Long.class, 100L, "FLOAT4"); @@ -529,6 +536,7 @@ void shortArray() { @Test void shortPrimitive() { testCodec(Short.class, (short) 100, "INT2"); + testCodec(Short.TYPE, (short) 100, "INT2"); } @Test diff --git a/src/test/java/io/r2dbc/postgresql/codec/CharacterCodecUnitTests.java b/src/test/java/io/r2dbc/postgresql/codec/CharacterCodecUnitTests.java index 26b6ecba..c8e43e77 100644 --- a/src/test/java/io/r2dbc/postgresql/codec/CharacterCodecUnitTests.java +++ b/src/test/java/io/r2dbc/postgresql/codec/CharacterCodecUnitTests.java @@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; /** - * Unit tests for {@link CharacterCodec}. + * Unit tests for {@link CharacterCodecProvider}. */ final class CharacterCodecUnitTests { @@ -40,24 +40,24 @@ final class CharacterCodecUnitTests { @Test void constructorNoByteBufAllocator() { - assertThatIllegalArgumentException().isThrownBy(() -> new CharacterCodec(null)) + assertThatIllegalArgumentException().isThrownBy(() -> new CharacterCodecProvider(null)) .withMessage("byteBufAllocator must not be null"); } @Test void decode() { - assertThat(new CharacterCodec(TEST).decode(encode(TEST, "A"), dataType, FORMAT_TEXT, Character.class)) + assertThat(new CharacterCodecProvider(TEST).decode(encode(TEST, "A"), dataType, FORMAT_TEXT, Character.class)) .isEqualTo('A'); } @Test void decodeNoByteBuf() { - assertThat(new CharacterCodec(TEST).decode(null, dataType, FORMAT_TEXT, Character.class)).isNull(); + assertThat(new CharacterCodecProvider(TEST).decode(null, dataType, FORMAT_TEXT, Character.class)).isNull(); } @Test void doCanDecode() { - CharacterCodec codec = new CharacterCodec(TEST); + CharacterCodecProvider codec = new CharacterCodecProvider(TEST); assertThat(codec.doCanDecode(VARCHAR, FORMAT_BINARY)).isTrue(); assertThat(codec.doCanDecode(MONEY, FORMAT_TEXT)).isFalse(); @@ -67,19 +67,19 @@ void doCanDecode() { @Test void doCanDecodeNoFormat() { - assertThatIllegalArgumentException().isThrownBy(() -> new CharacterCodec(TEST).doCanDecode(VARCHAR, null)) + assertThatIllegalArgumentException().isThrownBy(() -> new CharacterCodecProvider(TEST).doCanDecode(VARCHAR, null)) .withMessage("format must not be null"); } @Test void doCanDecodeNoType() { - assertThatIllegalArgumentException().isThrownBy(() -> new CharacterCodec(TEST).doCanDecode(null, FORMAT_TEXT)) + assertThatIllegalArgumentException().isThrownBy(() -> new CharacterCodecProvider(TEST).doCanDecode(null, FORMAT_TEXT)) .withMessage("type must not be null"); } @Test void doEncode() { - assertThat(new CharacterCodec(TEST).doEncode('A')) + assertThat(new CharacterCodecProvider(TEST).doEncode('A')) .hasFormat(FORMAT_TEXT) .hasType(VARCHAR.getObjectId()) .hasValue(encode(TEST, "A")); @@ -87,13 +87,13 @@ void doEncode() { @Test void doEncodeNoValue() { - assertThatIllegalArgumentException().isThrownBy(() -> new CharacterCodec(TEST).doEncode(null)) + assertThatIllegalArgumentException().isThrownBy(() -> new CharacterCodecProvider(TEST).doEncode(null)) .withMessage("value must not be null"); } @Test void encodeNull() { - assertThat(new CharacterCodec(TEST).encodeNull()) + assertThat(new CharacterCodecProvider(TEST).encodeNull()) .isEqualTo(new EncodedParameter(FORMAT_TEXT, VARCHAR.getObjectId(), NULL_VALUE)); } diff --git a/src/test/java/io/r2dbc/postgresql/codec/PrimitiveCodecIntegrationTests.java b/src/test/java/io/r2dbc/postgresql/codec/PrimitiveCodecIntegrationTests.java new file mode 100644 index 00000000..c42f8269 --- /dev/null +++ b/src/test/java/io/r2dbc/postgresql/codec/PrimitiveCodecIntegrationTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2024 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 PrimitiveCodec} usage. + */ +class PrimitiveCodecIntegrationTests extends AbstractIntegrationTests { + + @Test + void shouldReturnValues() { + + SERVER.getJdbcOperations().execute("DROP TABLE IF EXISTS test"); + SERVER.getJdbcOperations().execute("CREATE TABLE test (the_int INT2, the_bool BOOL)"); + SERVER.getJdbcOperations().execute("INSERT INTO test VALUES(1, true)"); + + this.connection.createStatement("SELECT the_int FROM test") + .execute() + .flatMap(it -> it.map(r -> r.get("the_int", int.class))) + .as(StepVerifier::create) + .expectNext(1) + .verifyComplete(); + + this.connection.createStatement("SELECT the_bool FROM test") + .execute() + .flatMap(it -> it.map(r -> r.get("the_bool", boolean.class))) + .as(StepVerifier::create) + .expectNext(true) + .verifyComplete(); + + SERVER.getJdbcOperations().execute("DROP TABLE test"); + } + + @Test + void nullRetrievalShouldFail() { + + SERVER.getJdbcOperations().execute("DROP TABLE IF EXISTS test"); + SERVER.getJdbcOperations().execute("CREATE TABLE test (the_int INT2, the_bool BOOL)"); + SERVER.getJdbcOperations().execute("INSERT INTO test VALUES(null, null)"); + + this.connection.createStatement("SELECT the_int FROM test") + .execute() + .flatMap(it -> it.map(r -> r.get("the_int", int.class))) + .as(StepVerifier::create) + .verifyError(NullPointerException.class); + + this.connection.createStatement("SELECT the_int FROM test") + .execute() + .flatMap(it -> it.map(r -> r.get(0, int.class))) + .as(StepVerifier::create) + .verifyError(NullPointerException.class); + + this.connection.createStatement("SELECT the_bool FROM test") + .execute() + .flatMap(it -> it.map(r -> r.get("the_bool", boolean.class))) + .as(StepVerifier::create) + .verifyError(NullPointerException.class); + + SERVER.getJdbcOperations().execute("DROP TABLE test"); + } + +}