From facff596db85cf3117d2950bed99d76ae7f8d8a5 Mon Sep 17 00:00:00 2001 From: 014-code <2402143478@qq.com> Date: Sat, 8 Aug 2026 20:13:52 +0800 Subject: [PATCH] fix(jdbc-v2): render array contents in toString Fixes #3045. Render nested JDBC Array values with deep formatting so array-of-tuple results are readable to clients that use ResultSet#getObject(). --- CHANGELOG.md | 2 + .../java/com/clickhouse/jdbc/types/Array.java | 11 +++++ .../jdbc/ArrayTupleIntegrationTest.java | 43 +++++++++++++++++++ .../com/clickhouse/jdbc/types/ArrayTest.java | 22 ++++++++++ 4 files changed, 78 insertions(+) create mode 100644 jdbc-v2/src/test/java/com/clickhouse/jdbc/ArrayTupleIntegrationTest.java create mode 100644 jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 52c92a57a..dee575de9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ ### Bug Fixes +- **[jdbc-v2]** Made JDBC `Array` values render their nested contents through `toString()`, including arrays of named tuples, so clients such as IDE database viewers no longer display the wrapper class name and identity hash. (https://github.com/ClickHouse/clickhouse-java/issues/3045) + - **[client-v2]** Fixed LZ4 input streams not closing their underlying HTTP response stream. Closing an LZ4 stream returned by `QueryResponse.getInputStream()` now releases the wrapped transport stream, including after a partial read. (https://github.com/ClickHouse/clickhouse-java/issues/2985) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java index b7b23cc7f..b1daaaaac 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java @@ -11,6 +11,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; +import java.util.Arrays; import java.util.Map; public class Array implements java.sql.Array { @@ -127,6 +128,16 @@ private void ensureValid() throws SQLException { } } + /** + * Returns a readable representation of the array contents. JDBC clients such as IDE database viewers + * commonly use {@code toString()} when rendering values returned by {@link java.sql.ResultSet#getObject}. + * Deep formatting is required for arrays of tuples and nested arrays. + */ + @Override + public String toString() { + return Arrays.deepToString(array); + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ArrayTupleIntegrationTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ArrayTupleIntegrationTest.java new file mode 100644 index 000000000..8d2d784d6 --- /dev/null +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ArrayTupleIntegrationTest.java @@ -0,0 +1,43 @@ +package com.clickhouse.jdbc; + +import org.testng.annotations.Test; + +import java.sql.Array; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +public class ArrayTupleIntegrationTest extends JdbcIntegrationTest { + + @Test(groups = {"integration"}) + public void testArrayOfNamedAndUnnamedTuplesToString() throws Exception { + String uuid = "550e8400-e29b-41d4-a716-446655440000"; + String expected = "[[" + uuid + "]]"; + String query = "SELECT [('" + uuid + "')::Tuple(id UUID)] AS named, " + + "[('" + uuid + "')::Tuple(UUID)] AS unnamed"; + + try (Connection connection = getJdbcConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(query)) { + assertTrue(resultSet.next()); + + assertArrayValue(resultSet.getObject("named"), expected); + assertArrayValue(resultSet.getObject("unnamed"), expected); + + assertFalse(resultSet.next()); + } + } + + private static void assertArrayValue(Object value, String expected) throws SQLException { + assertTrue(value instanceof Array); + Array array = (Array) value; + assertEquals(value.toString(), expected); + assertEquals(Arrays.deepToString((Object[]) array.getArray()), expected); + } +} diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayTest.java new file mode 100644 index 000000000..78028b550 --- /dev/null +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayTest.java @@ -0,0 +1,22 @@ +package com.clickhouse.jdbc.types; + +import com.clickhouse.data.ClickHouseColumn; +import org.testng.annotations.Test; + +import java.sql.SQLException; +import java.util.UUID; + +import static org.testng.Assert.assertEquals; + +@Test(groups = {"unit"}) +public class ArrayTest { + + @Test + public void testToStringForArrayOfNamedTuples() throws SQLException { + ClickHouseColumn column = ClickHouseColumn.of("value", "Array(Tuple(id UUID))"); + UUID id = UUID.fromString("550e8400-e29b-41d4-a716-446655440000"); + Array array = new Array(column, new Object[] {new Object[] {id}}); + + assertEquals(array.toString(), "[[550e8400-e29b-41d4-a716-446655440000]]"); + } +}