Skip to content

Commit 70b40d3

Browse files
committed
fix: provide default getObject(Class) fallback in QueryJDBCAccessor
QueryJDBCAccessor.getObject(Class) threw "Operation not supported" no matter what. That's a problem: JDBC says ResultSet.getObject(int, Class<T>) is supposed to return the value as T when the conversion is trivial, but every accessor that didn't override it would throw, even on the identity case like getObject(col, String.class) against a VARCHAR. Callers had to either know which accessors implement typed conversion or wrap calls in catch-and-retry. The new default does the obvious thing: reject null type with SQLState 22023, fetch the raw object, return null if it's null, return it cast to T if type.isInstance(raw), otherwise throw a typed conversion error. Accessors with richer needs (timestamp accessors return Instant, OffsetDateTime, etc.) keep their overrides. TimeStampVectorAccessor and TimeStampTZVectorAccessor are the two that have them today. Tests: getObjectWithClassUsesAccessorBaseFallback in StreamingResultSetMethodTest exercises the inherited path on a VARCHAR column. The existing QueryJDBCAccessorTest still passes since it doesn't touch getObject(Class). Null-type rejection is already pinned by TimeStampVectorAccessorTest and TimeZoneIntegrationTest.testGetObjectWithNullTypeThrows.
1 parent 092ac4d commit 70b40d3

2 files changed

Lines changed: 34 additions & 3 deletions

File tree

jdbc-core/src/main/java/com/salesforce/datacloud/jdbc/core/accessor/QueryJDBCAccessor.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,28 @@ public Reader getNCharacterStream() throws SQLException {
193193
throw getOperationNotSupported(this.getClass());
194194
}
195195

196-
@Override
197-
public <T> T getObject(Class<T> aClass) throws SQLException {
198-
throw getOperationNotSupported(aClass.getClass());
196+
/**
197+
* Default {@code getObject(Class)} implementation: take the raw object produced by
198+
* {@link #getObject()} and return it if {@code type.isInstance(raw)}, otherwise raise a
199+
* conversion error. Accessors that need richer conversion (e.g. an Arrow {@code TIMESTAMP}
200+
* vector returning {@link java.time.Instant} / {@link java.time.OffsetDateTime}) override
201+
* this; everyone else inherits this generic path so callers do not need to special-case
202+
* "accessor implements typed getObject" vs "accessor does not".
203+
*/
204+
@Override
205+
public <T> T getObject(Class<T> type) throws SQLException {
206+
if (type == null) {
207+
throw new SQLException("type parameter must not be null", "22023");
208+
}
209+
Object raw = getObject();
210+
if (raw == null) {
211+
return null;
212+
}
213+
if (type.isInstance(raw)) {
214+
return type.cast(raw);
215+
}
216+
throw new SQLException("Cannot convert column value to " + type.getName() + "; actual type is "
217+
+ raw.getClass().getName());
199218
}
200219

201220
private static SQLException getOperationNotSupported(final Class<?> type) {

jdbc-core/src/test/java/com/salesforce/datacloud/jdbc/core/StreamingResultSetMethodTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@ void methodsThrowAfterClose() throws Exception {
143143
.hasMessageContaining("closed");
144144
}
145145

146+
@Test
147+
void getObjectWithClassUsesAccessorBaseFallback() throws Exception {
148+
// VarCharVectorAccessor does not override getObject(Class); it inherits the default in
149+
// QueryJDBCAccessor that does raw + isInstance. Pin that this delivers a String for a
150+
// VARCHAR column — regressing the base-class fallback breaks every accessor that does
151+
// not implement typed conversion of its own.
152+
try (val rs = createResultSet()) {
153+
rs.next();
154+
assertThat(rs.getObject(1, String.class)).isEqualTo("hello");
155+
}
156+
}
157+
146158
@Test
147159
void queryId() throws Exception {
148160
try (val rs = createResultSet()) {

0 commit comments

Comments
 (0)