|
6 | 6 | import com.clickhouse.client.api.data_formats.internal.SerializerUtils; |
7 | 7 | import com.clickhouse.client.api.metadata.TableSchema; |
8 | 8 | import com.clickhouse.client.api.query.QuerySettings; |
| 9 | +import com.clickhouse.client.api.serde.POJOFieldDeserializer; |
9 | 10 | import com.clickhouse.data.ClickHouseColumn; |
10 | 11 | import com.clickhouse.data.format.BinaryStreamUtils; |
11 | 12 | import org.testng.Assert; |
|
15 | 16 | import java.io.ByteArrayInputStream; |
16 | 17 | import java.io.ByteArrayOutputStream; |
17 | 18 | import java.io.IOException; |
| 19 | +import java.lang.reflect.Method; |
18 | 20 | import java.nio.ByteBuffer; |
19 | 21 | import java.nio.charset.Charset; |
20 | 22 | import java.nio.charset.StandardCharsets; |
@@ -286,6 +288,83 @@ public void testDefaultBehaviorReturnsString() throws IOException { |
286 | 288 | Assert.assertEquals(read, "still a string"); |
287 | 289 | } |
288 | 290 |
|
| 291 | + // ---- POJO binding (queryAll/readToPOJO) over String columns with the feature enabled ---- |
| 292 | + |
| 293 | + /** |
| 294 | + * Minimal POJO with the only two field representations supported for top-level String/FixedString |
| 295 | + * columns: {@link String} and {@code byte[]}. {@link StringValue} is a read-time holder and is not a |
| 296 | + * supported POJO field type. |
| 297 | + */ |
| 298 | + public static class StringPojo { |
| 299 | + private String asString; |
| 300 | + private byte[] asBytes; |
| 301 | + |
| 302 | + public void setAsString(String asString) { this.asString = asString; } |
| 303 | + public void setAsBytes(byte[] asBytes) { this.asBytes = asBytes; } |
| 304 | + } |
| 305 | + |
| 306 | + private static byte[] stringWire(byte[] value) throws IOException { |
| 307 | + ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| 308 | + BinaryStreamUtils.writeString(baos, value); |
| 309 | + return baos.toByteArray(); |
| 310 | + } |
| 311 | + |
| 312 | + private static POJOFieldDeserializer setterFor(String name, ClickHouseColumn column) throws Exception { |
| 313 | + Method setter = StringPojo.class.getMethod(name, |
| 314 | + name.equals("setAsString") ? String.class : byte[].class); |
| 315 | + return SerializerUtils.compilePOJOSetter(setter, column); |
| 316 | + } |
| 317 | + |
| 318 | + @Test |
| 319 | + public void testPojoSetterStringFieldDecodesWhenFeatureEnabled() throws Exception { |
| 320 | + // Regression: with the feature enabled the reader produces StringValue, but a String setter must |
| 321 | + // still receive a decoded String (the compiled setter casts the readValue result to String). |
| 322 | + ClickHouseColumn column = ClickHouseColumn.of("s", "String"); |
| 323 | + byte[] wire = stringWire("hello".getBytes(StandardCharsets.UTF_8)); |
| 324 | + |
| 325 | + StringPojo pojo = new StringPojo(); |
| 326 | + setterFor("setAsString", column).setValue(pojo, reader(wire, true), column); |
| 327 | + Assert.assertEquals(pojo.asString, "hello"); |
| 328 | + } |
| 329 | + |
| 330 | + @Test |
| 331 | + public void testPojoSetterByteArrayFieldReceivesRawBytesWhenFeatureEnabled() throws Exception { |
| 332 | + // A byte[] setter must receive the raw bytes (preserving non-UTF-8 content) instead of a StringValue. |
| 333 | + ClickHouseColumn column = ClickHouseColumn.of("s", "String"); |
| 334 | + byte[] binary = {(byte) 0xDE, (byte) 0xAD, (byte) 0x00, (byte) 0xBE, (byte) 0xEF}; |
| 335 | + byte[] wire = stringWire(binary); |
| 336 | + |
| 337 | + StringPojo pojo = new StringPojo(); |
| 338 | + setterFor("setAsBytes", column).setValue(pojo, reader(wire, true), column); |
| 339 | + Assert.assertEquals(pojo.asBytes, binary); |
| 340 | + } |
| 341 | + |
| 342 | + @Test |
| 343 | + public void testPojoSetterFixedStringByteArrayFieldWhenFeatureEnabled() throws Exception { |
| 344 | + // A byte[] setter over a FixedString column must receive the raw bytes, preserving binary content. |
| 345 | + ClickHouseColumn column = ClickHouseColumn.of("s", "FixedString(3)"); |
| 346 | + byte[] binary = {(byte) 0xAA, (byte) 0xBB, (byte) 0xCC}; |
| 347 | + ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| 348 | + baos.write(binary); // FixedString(3) is exactly 3 raw bytes on the wire |
| 349 | + |
| 350 | + StringPojo pojo = new StringPojo(); |
| 351 | + setterFor("setAsBytes", column).setValue(pojo, reader(baos.toByteArray(), true), column); |
| 352 | + Assert.assertEquals(pojo.asBytes, binary); |
| 353 | + } |
| 354 | + |
| 355 | + @Test |
| 356 | + public void testPojoSetterFixedStringStringFieldWhenFeatureEnabled() throws Exception { |
| 357 | + // A String setter over a FixedString column must receive the decoded String. |
| 358 | + ClickHouseColumn column = ClickHouseColumn.of("s", "FixedString(3)"); |
| 359 | + byte[] bytes = "abc".getBytes(StandardCharsets.UTF_8); |
| 360 | + ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| 361 | + baos.write(bytes); |
| 362 | + |
| 363 | + StringPojo pojo = new StringPojo(); |
| 364 | + setterFor("setAsString", column).setValue(pojo, reader(baos.toByteArray(), true), column); |
| 365 | + Assert.assertEquals(pojo.asString, "abc"); |
| 366 | + } |
| 367 | + |
289 | 368 | // ---- Writing binary String values ---- |
290 | 369 |
|
291 | 370 | @Test |
|
0 commit comments