Skip to content

Commit 1c27930

Browse files
committed
GH-734: Improve CompositeJdbcConsumer error handling and add unit tests
1 parent 17f85a1 commit 1c27930

File tree

2 files changed

+112
-3
lines changed

2 files changed

+112
-3
lines changed

adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/consumer/CompositeJdbcConsumer.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,24 @@ public void consume(ResultSet rs) throws SQLException, IOException {
4444
} catch (Exception e) {
4545
if (consumers[i] instanceof BaseConsumer) {
4646
BaseConsumer consumer = (BaseConsumer) consumers[i];
47-
JdbcFieldInfo fieldInfo =
48-
new JdbcFieldInfo(rs.getMetaData(), consumer.columnIndexInResultSet);
49-
ArrowType arrowType = consumer.vector.getMinorType().getType();
47+
JdbcFieldInfo fieldInfo = null;
48+
ArrowType arrowType = null;
49+
try {
50+
if (rs != null) {
51+
fieldInfo = new JdbcFieldInfo(rs.getMetaData(), consumer.columnIndexInResultSet);
52+
}
53+
} catch (Exception metaEx) {
54+
// doesn't do anything if ResultSet is null
55+
// to return the JdbcConsumerException
56+
}
57+
try {
58+
if (consumer.vector.getMinorType() != null){
59+
arrowType = consumer.vector.getMinorType().getType();
60+
}
61+
} catch (Exception typeEx) {
62+
// doesn't do anything if there is an error when getting null with getMinorType()
63+
// to return the JdbcConsumerException
64+
}
5065
throw new JdbcConsumerException(
5166
"Exception while consuming JDBC value", e, fieldInfo, arrowType);
5267
} else {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.arrow.adapter.jdbc.consumer;
18+
19+
import org.apache.arrow.adapter.jdbc.consumer.exceptions.JdbcConsumerException;
20+
import org.apache.arrow.memory.BufferAllocator;
21+
import org.apache.arrow.memory.RootAllocator;
22+
import org.apache.arrow.vector.IntVector;
23+
import org.apache.arrow.vector.ValueVector;
24+
import java.sql.ResultSet;
25+
import java.sql.SQLException;
26+
27+
import static org.junit.jupiter.api.Assertions.*;
28+
29+
import org.junit.jupiter.api.Test;
30+
31+
public class CompositeJdbcConsumerTest {
32+
// Faulty consumer that simulates a runtime exception during consume()
33+
// This is used to test that the CompositeJdbcConsumer wraps it properly
34+
static class FaultyConsumer extends BaseConsumer {
35+
public FaultyConsumer(ValueVector vector, int index) {
36+
super(vector, index);
37+
}
38+
39+
@Override
40+
public void consume(ResultSet rs) throws SQLException {
41+
throw new NullPointerException("Simulating NPE");
42+
}
43+
}
44+
45+
@Test
46+
public void testHandlesJdbcConsumerExceptionGracefully() throws SQLException {
47+
// Setup: create an IntVector to simulate a consumer with a valid vector
48+
BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE);
49+
IntVector intVector = new IntVector("int", allocator);
50+
intVector.allocateNew();
51+
52+
// Simulate a failing consumer with valid vector (to test arrowType extraction)
53+
JdbcConsumer mockConsumer = new FaultyConsumer(intVector, 1);
54+
CompositeJdbcConsumer composite = new CompositeJdbcConsumer(new JdbcConsumer[]{mockConsumer});
55+
56+
// Use null ResultSet to simulate failure scenario
57+
ResultSet dummyRs = null;
58+
59+
// Verify: the failure is caught and wrapped in JdbcConsumerException
60+
JdbcConsumerException thrownEx = assertThrows(JdbcConsumerException.class, () -> {
61+
composite.consume(dummyRs);
62+
});
63+
assertTrue(thrownEx.getMessage().contains("Exception while consuming JDBC value"));
64+
assertNull(thrownEx.getFieldInfo());
65+
assertNotNull(thrownEx.getArrowType()); // Should be non-null since vector was valid
66+
}
67+
68+
// Faulty consumer that has a null ValueVector (to test arrowType = null)
69+
public static class FaultyConsumerWIthNullVector extends BaseConsumer {
70+
public FaultyConsumerWIthNullVector(int index) {
71+
super(null, index);
72+
}
73+
74+
@Override
75+
public void consume(ResultSet rs) throws SQLException {
76+
throw new NullPointerException("Simulating NPE");
77+
}
78+
}
79+
80+
@Test
81+
public void testJdbcConsumerExceptionWhenArrowTypeIsNull() throws SQLException {
82+
// Setup: consumer with null vector
83+
JdbcConsumer mockConsumer = new FaultyConsumerWIthNullVector(1);
84+
CompositeJdbcConsumer composite = new CompositeJdbcConsumer(new JdbcConsumer[]{mockConsumer});
85+
ResultSet dummyRs = null;
86+
87+
// Verify: when the consumer fails and the vector is null,
88+
// arrowType in JdbcConsumerException should also be null
89+
JdbcConsumerException thrownEx = assertThrows(JdbcConsumerException.class, () -> {
90+
composite.consume(dummyRs);
91+
});
92+
assertNull(thrownEx.getArrowType());
93+
}
94+
}

0 commit comments

Comments
 (0)