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 1/2] 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]]"); + } +} From dcc728fd5ecbff1d284fa444e710d27b5d583fa5 Mon Sep 17 00:00:00 2001 From: 014-code <2402143478@qq.com> Date: Tue, 18 Aug 2026 21:35:16 +0800 Subject: [PATCH 2/2] revert(jdbc-v2): defer array display formatting Remove the deep Array.toString() traversal and its tests pending a defined JDBC display contract. --- 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 deletions(-) delete mode 100644 jdbc-v2/src/test/java/com/clickhouse/jdbc/ArrayTupleIntegrationTest.java delete mode 100644 jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index dee575de9..52c92a57a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,8 +44,6 @@ ### 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 b1daaaaac..b7b23cc7f 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,7 +11,6 @@ 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 { @@ -128,16 +127,6 @@ 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 deleted file mode 100644 index 8d2d784d6..000000000 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ArrayTupleIntegrationTest.java +++ /dev/null @@ -1,43 +0,0 @@ -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 deleted file mode 100644 index 78028b550..000000000 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayTest.java +++ /dev/null @@ -1,22 +0,0 @@ -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]]"); - } -}