Skip to content

Commit 507be2b

Browse files
fix(client-v2): reject null in non-nullable Enum with clear error
serializeEnumData dereferenced the value before the null check, so inserting null into a non-nullable Enum8/Enum16 column failed the RowBinary serialization path with a confusing NullPointerException. Add a null guard that throws IllegalArgumentException naming the column, consistent with how other unsupported enum values are rejected. Nullable enum columns are unaffected. Fixes: #2931
1 parent fbe31cd commit 507be2b

3 files changed

Lines changed: 43 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
### Bug Fixes
66

7+
- **[client-v2]** Fixed a `NullPointerException` when serializing a `null` value into a non-nullable
8+
`Enum8`/`Enum16` column. `SerializerUtils.serializeEnumData` dereferenced the value before the `null`
9+
check, so a `null` in a non-nullable enum column failed the RowBinary insert path with a confusing NPE
10+
instead of a clear error. It now throws `IllegalArgumentException` naming the column, consistent with the
11+
existing `IllegalArgumentException` for other unsupported enum values. Nullable enum columns are unaffected.
12+
(https://github.com/ClickHouse/clickhouse-java/issues/2931)
713
- **[client-v2]** Fixed POJO insert error classification so transport write failures such as java.net.SocketException:
814
Broken pipe (Write failed) are now surfaced as transfer/network errors instead of being wrapped as
915
DataSerializationException. This only changes the exception type reported for request-body transport failures during

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/SerializerUtils.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,10 @@ private static void serializeTime64(OutputStream stream, Object value) throws IO
706706
}
707707

708708
public static void serializeEnumData(OutputStream stream, ClickHouseColumn column, Object value) throws IOException {
709+
if (value == null) {
710+
throw new IllegalArgumentException("Cannot insert null into non-nullable column " + column.getColumnName()
711+
+ " of type " + column.getOriginalTypeName());
712+
}
709713
int enumValue = -1;
710714
if (value instanceof String) {
711715
enumValue = column.getEnumConstants().value((String) value);

client-v2/src/test/java/com/clickhouse/client/api/data_formats/internal/SerializerUtilsTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,39 @@ public void testGeometrySerializationRejectsMalformedList() {
143143
ClickHouseColumn.of("v", "Geometry")));
144144
}
145145

146+
@Test(dataProvider = "nonNullableEnumTypes")
147+
public void testNullIntoNonNullableEnumThrowsIllegalArgument(String typeName) {
148+
ClickHouseColumn column = ClickHouseColumn.of("v", typeName);
149+
150+
IllegalArgumentException ex = Assert.expectThrows(IllegalArgumentException.class,
151+
() -> SerializerUtils.serializeData(new ByteArrayOutputStream(), null, column));
152+
Assert.assertTrue(ex.getMessage().contains("Cannot insert null into non-nullable column"),
153+
"Unexpected message: " + ex.getMessage());
154+
}
155+
156+
@DataProvider(name = "nonNullableEnumTypes")
157+
private Object[][] nonNullableEnumTypes() {
158+
return new Object[][] {
159+
{"Enum8('B' = 1, 'S' = 2)"},
160+
{"Enum16('B' = 1, 'S' = 2)"},
161+
};
162+
}
163+
164+
@Test
165+
public void testEnumSerializationUnaffectedByNullGuard() throws Exception {
166+
// A Nullable(Enum) with null still takes the early null-marker path and never reaches
167+
// enum serialization, so a single null-marker byte is written.
168+
ByteArrayOutputStream nullableOut = new ByteArrayOutputStream();
169+
SerializerUtils.serializeData(nullableOut, null,
170+
ClickHouseColumn.of("v", "Nullable(Enum8('B' = 1, 'S' = 2))"));
171+
Assert.assertEquals(nullableOut.toByteArray(), new byte[] {1});
172+
173+
// A present value in a non-nullable Enum column still serializes to its mapped numeric value.
174+
ByteArrayOutputStream valueOut = new ByteArrayOutputStream();
175+
SerializerUtils.serializeData(valueOut, "S", ClickHouseColumn.of("v", "Enum8('B' = 1, 'S' = 2)"));
176+
Assert.assertEquals(valueOut.toByteArray(), new byte[] {2});
177+
}
178+
146179
@Test(dataProvider = "nestedNullableData")
147180
public void testNestedNullableRoundTrip(String typeName, Object value) throws Exception {
148181
ClickHouseColumn column = ClickHouseColumn.of("v", typeName);

0 commit comments

Comments
 (0)