From 132d511739481a1d48c6314e51dad404493b3dca Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Fri, 18 Sep 2026 23:27:50 +0200 Subject: [PATCH] Fix ArrayResultSet after-last cursor state --- CHANGELOG.md | 2 ++ .../com/clickhouse/jdbc/types/ArrayResultSet.java | 5 +++-- .../clickhouse/jdbc/types/ArrayResultSetTest.java | 12 ++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 206ff3cc8..f9c2829e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -171,6 +171,8 @@ ### Bug Fixes +- **[jdbc-v2]** Fixed `ArrayResultSet#next()` leaving the cursor before-first for empty arrays or on the last row for + non-empty arrays after exhaustion. The cursor now moves to the after-last state when `next()` returns `false`. - **[jdbc-v2]** Added the non-reserved keywords `AGGREGATE`, `BOUNDED`, `EXTEND`, `HANDLER`, `IDLE`, `PROTOCOL`, `RECENT`, `TIMEOUT` and `UNORDERED` (ClickHouse `26.8+`; `IDLE`, `TIMEOUT` and `RECENT` come from the multi-word keywords `IDLE TIMEOUT` and `RECENT SAMPLES`) to the list of keywords allowed in identifier positions. The server diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/ArrayResultSet.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/ArrayResultSet.java index 97da74a06..1f63ad3c8 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/ArrayResultSet.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/ArrayResultSet.java @@ -98,7 +98,8 @@ private static ClickHouseColumn elementColumn(ClickHouseColumn column) { @Override public boolean next() throws SQLException { - if (pos + 1 >= length || length == 0) { + if (pos + 1 >= length) { + pos = length; return false; } pos++; @@ -478,7 +479,7 @@ public boolean isAfterLast() throws SQLException { @Override public boolean isFirst() throws SQLException { - return pos == 0; + return length > 0 && pos == 0; } @Override diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayResultSetTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayResultSetTest.java index e4a417201..cbae5d169 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayResultSetTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/types/ArrayResultSetTest.java @@ -308,10 +308,11 @@ void testEmptyArray() throws SQLException { assertFalse(rs.next()); - Assert.assertTrue(rs.isBeforeFirst()); - Assert.assertFalse(rs.isAfterLast()); + Assert.assertFalse(rs.isBeforeFirst()); + Assert.assertTrue(rs.isAfterLast()); Assert.assertFalse(rs.isLast()); Assert.assertFalse(rs.isFirst()); + Assert.assertEquals(rs.getRow(), 0); Assert.assertThrows(SQLException.class, () -> rs.getString("col1")); Assert.assertThrows(SQLException.class, () -> rs.getObject("col1")); @@ -573,7 +574,9 @@ void testNextEmptyArray() throws SQLException { ArrayResultSet rs = new ArrayResultSet(array, ClickHouseColumn.parse("v Array(Int32)").get(0)); assertFalse(rs.next(), "next() should return false immediately for an empty array"); + assertTrue(rs.isAfterLast(), "next() should move an empty array after-last"); assertFalse(rs.next(), "next() should keep returning false on subsequent calls"); + assertTrue(rs.isAfterLast(), "next() should keep the cursor after-last"); } @Test @@ -584,6 +587,11 @@ void testNextSingleElement() throws SQLException { assertTrue(rs.next()); assertEquals(rs.getInt(2), 42); assertFalse(rs.next(), "next() should return false after the only element"); + assertTrue(rs.isAfterLast()); + assertFalse(rs.isLast()); + assertEquals(rs.getRow(), 0); + assertTrue(rs.previous()); + assertEquals(rs.getInt(2), 42); } @Test