Skip to content

Commit 349d402

Browse files
authored
GH-964: Fix IndexOutOfBoundsException in Array.getResultSet() for JDBC clients (#965)
## What's Changed - Fixed JDBC specification in ArrowFlightJdbcArray.getResultSet() that caused IndexOutOfBoundsException in JDBC clients like DBeaver when reading array columns - The method returned a single-column ResultSet containing only array values, but JDBC spec requires a 2-column format - Not it returns two columns: - Column 1 (INDEX): 1-based element indices per JDBC specification - Column 2: The actual array element values Closes #964.
1 parent 9cdda52 commit 349d402

File tree

4 files changed

+31
-15
lines changed

4 files changed

+31
-15
lines changed

flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightJdbcArray.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.apache.arrow.driver.jdbc.utils.SqlTypes;
2727
import org.apache.arrow.memory.util.LargeMemoryUtil;
2828
import org.apache.arrow.vector.FieldVector;
29+
import org.apache.arrow.vector.IntVector;
2930
import org.apache.arrow.vector.ValueVector;
3031
import org.apache.arrow.vector.VectorSchemaRoot;
3132
import org.apache.arrow.vector.types.pojo.ArrowType;
@@ -135,12 +136,22 @@ public ResultSet getResultSet(long index, int count) throws SQLException {
135136

136137
private static ResultSet getResultSetNoBoundariesCheck(
137138
ValueVector dataVector, long start, long count) throws SQLException {
139+
int intStart = LargeMemoryUtil.checkedCastToInt(start);
140+
int intCount = LargeMemoryUtil.checkedCastToInt(count);
141+
142+
// Create an index vector with 1-based indices (per JDBC spec) to return with value vector
143+
IntVector indexVector = new IntVector("INDEX", dataVector.getAllocator());
144+
indexVector.allocateNew(intCount);
145+
for (int i = 0; i < intCount; i++) {
146+
indexVector.set(i, i + 1);
147+
}
148+
indexVector.setValueCount(intCount);
149+
138150
TransferPair transferPair = dataVector.getTransferPair(dataVector.getAllocator());
139-
transferPair.splitAndTransfer(
140-
LargeMemoryUtil.checkedCastToInt(start), LargeMemoryUtil.checkedCastToInt(count));
141-
FieldVector vectorSlice = (FieldVector) transferPair.getTo();
151+
transferPair.splitAndTransfer(intStart, intCount);
152+
FieldVector valueVector = (FieldVector) transferPair.getTo();
142153

143-
VectorSchemaRoot vectorSchemaRoot = VectorSchemaRoot.of(vectorSlice);
154+
VectorSchemaRoot vectorSchemaRoot = VectorSchemaRoot.of(indexVector, valueVector);
144155
return ArrowFlightJdbcVectorSchemaRootResultSet.fromVectorSchemaRoot(vectorSchemaRoot);
145156
}
146157

flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowFlightJdbcArrayTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public void testShouldGetResultSetReturnValidResultSet() throws SQLException {
129129
try (ResultSet resultSet = arrowFlightJdbcArray.getResultSet()) {
130130
int count = 0;
131131
while (resultSet.next()) {
132-
assertEquals((Object) resultSet.getInt(1), dataVector.getObject(count));
132+
assertEquals((Object) resultSet.getInt(2), dataVector.getObject(count));
133133
count++;
134134
}
135135
}
@@ -142,7 +142,7 @@ public void testShouldGetResultSetReturnValidResultSetWithOffsets() throws SQLEx
142142
try (ResultSet resultSet = arrowFlightJdbcArray.getResultSet(3, 5)) {
143143
int count = 0;
144144
while (resultSet.next()) {
145-
assertEquals((Object) resultSet.getInt(1), dataVector.getObject(count + 3));
145+
assertEquals((Object) resultSet.getInt(2), dataVector.getObject(count + 3));
146146
count++;
147147
}
148148
assertEquals(5, count);

flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/accessor/impl/complex/AbstractArrowFlightJdbcListAccessorTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,12 @@ public void testShouldGetArrayGetResultSetReturnValidResultSet(
191191
try (ResultSet rs = array.getResultSet()) {
192192
int count = 0;
193193
while (rs.next()) {
194-
final int value = rs.getInt(1);
194+
// Column 1: 1-based index (per JDBC spec)
195+
final int index = rs.getInt(1);
196+
assertThat(index, equalTo(count + 1));
197+
198+
// Column 2: actual value (per JDBC spec)
199+
final int value = rs.getInt(2);
195200
assertThat(value, equalTo(currentRow * count));
196201
count++;
197202
}

flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/accessor/impl/complex/ArrowFlightJdbcMapVectorAccessorTest.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,15 @@ public void testShouldGetArrayReturnValidArray() throws SQLException {
153153

154154
try (ResultSet resultSet = array.getResultSet()) {
155155
assertTrue(resultSet.next());
156-
Map<?, ?> entry = resultSet.getObject(1, Map.class);
156+
Map<?, ?> entry = resultSet.getObject(2, Map.class);
157157
assertEquals(1, entry.get("key"));
158158
assertEquals(11, entry.get("value"));
159159
assertTrue(resultSet.next());
160-
entry = resultSet.getObject(1, Map.class);
160+
entry = resultSet.getObject(2, Map.class);
161161
assertEquals(2, entry.get("key"));
162162
assertEquals(22, entry.get("value"));
163163
assertTrue(resultSet.next());
164-
entry = resultSet.getObject(1, Map.class);
164+
entry = resultSet.getObject(2, Map.class);
165165
assertEquals(3, entry.get("key"));
166166
assertEquals(33, entry.get("value"));
167167
assertFalse(resultSet.next());
@@ -173,7 +173,7 @@ public void testShouldGetArrayReturnValidArray() throws SQLException {
173173
assertFalse(accessor.wasNull());
174174
try (ResultSet resultSet = array.getResultSet()) {
175175
assertTrue(resultSet.next());
176-
Map<?, ?> entry = resultSet.getObject(1, Map.class);
176+
Map<?, ?> entry = resultSet.getObject(2, Map.class);
177177
assertEquals(2, entry.get("key"));
178178
assertNull(entry.get("value"));
179179
assertFalse(resultSet.next());
@@ -185,19 +185,19 @@ public void testShouldGetArrayReturnValidArray() throws SQLException {
185185
assertFalse(accessor.wasNull());
186186
try (ResultSet resultSet = array.getResultSet()) {
187187
assertTrue(resultSet.next());
188-
Map<?, ?> entry = resultSet.getObject(1, Map.class);
188+
Map<?, ?> entry = resultSet.getObject(2, Map.class);
189189
assertEquals(0, entry.get("key"));
190190
assertEquals(2000, entry.get("value"));
191191
assertTrue(resultSet.next());
192-
entry = resultSet.getObject(1, Map.class);
192+
entry = resultSet.getObject(2, Map.class);
193193
assertEquals(1, entry.get("key"));
194194
assertEquals(2001, entry.get("value"));
195195
assertTrue(resultSet.next());
196-
entry = resultSet.getObject(1, Map.class);
196+
entry = resultSet.getObject(2, Map.class);
197197
assertEquals(2, entry.get("key"));
198198
assertEquals(2002, entry.get("value"));
199199
assertTrue(resultSet.next());
200-
entry = resultSet.getObject(1, Map.class);
200+
entry = resultSet.getObject(2, Map.class);
201201
assertEquals(3, entry.get("key"));
202202
assertEquals(2003, entry.get("value"));
203203
assertFalse(resultSet.next());

0 commit comments

Comments
 (0)