Skip to content

Commit e117cb3

Browse files
committed
[jdbc-v2] ResultSetImpl.getObject() returns T[] instead of Object[] for array columns
1 parent fda4e4f commit e117cb3

File tree

2 files changed

+135
-87
lines changed

2 files changed

+135
-87
lines changed

jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java

Lines changed: 113 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,27 @@ public static Class<?> convertToJavaClass(ClickHouseDataType clickhouseType) {
217217
return DATA_TYPE_CLASS_MAP.get(clickhouseType);
218218
}
219219

220+
private static Class<?> unwrapPrimitiveType(Class<?> type) {
221+
if (type == int.class) {
222+
return Integer.class;
223+
} else if (type == long.class) {
224+
return Long.class;
225+
} else if (type == boolean.class) {
226+
return Boolean.class;
227+
} else if (type == float.class) {
228+
return Float.class;
229+
} else if (type == double.class) {
230+
return Double.class;
231+
} else if (type == char.class) {
232+
return Character.class;
233+
} else if (type == byte.class) {
234+
return Byte.class;
235+
} else if (type == short.class) {
236+
return Short.class;
237+
}
238+
return type;
239+
}
240+
220241
public static Object convert(Object value, Class<?> type) throws SQLException {
221242
return convert(value, type, null);
222243
}
@@ -225,74 +246,100 @@ public static Object convert(Object value, Class<?> type, ClickHouseColumn colum
225246
if (value == null || type == null) {
226247
return value;
227248
}
249+
250+
type = unwrapPrimitiveType(type);
251+
if (type.isInstance(value)) {
252+
return value;
253+
}
254+
255+
if (value instanceof List<?>) {
256+
List<?> listValue = (List<?>) value;
257+
if (type != java.sql.Array.class) {
258+
return convertList(listValue, type);
259+
}
260+
261+
if (column != null && column.getArrayBaseColumn() != null) {
262+
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
263+
Object[] convertedValues = convertList(listValue, convertToJavaClass(baseType));
264+
return new Array(column, convertedValues);
265+
}
266+
267+
// base type is unknown. all objects should be converted
268+
return new Array(column, listValue.toArray());
269+
}
270+
271+
if (value.getClass().isArray()) {
272+
if (type == java.sql.Array.class) {
273+
return new Array(column, arrayToObjectArray(value));
274+
} else if (type == Tuple.class) {
275+
return new Tuple(true, value);
276+
}
277+
}
278+
279+
if (type == java.sql.Array.class && value instanceof BinaryStreamReader.ArrayValue) {
280+
BinaryStreamReader.ArrayValue arrayValue = (BinaryStreamReader.ArrayValue) value;
281+
282+
if (column != null && column.getArrayBaseColumn() != null) {
283+
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
284+
Object[] convertedValues = convertArray(arrayValue.getArrayOfObjects(), convertToJavaClass(baseType));
285+
return new Array(column, convertedValues);
286+
}
287+
288+
return new Array(column, arrayValue.getArrayOfObjects());
289+
}
290+
291+
return convertObject(value, type);
292+
}
293+
294+
public static Object convertObject(Object value, Class<?> type) throws SQLException {
295+
if (value == null || type == null) {
296+
return value;
297+
}
228298
try {
229-
if (type.isInstance(value)) {
230-
return value;
231-
} else if (type != java.sql.Array.class && value instanceof List<?>) {
232-
return convertList((List<?>) value, type);
233-
} else if (type == String.class) {
299+
if (type == String.class) {
234300
return value.toString();
235-
} else if (type == Boolean.class || type == boolean.class) {
301+
} else if (type == Boolean.class) {
236302
String str = value.toString();
237303
return !("false".equalsIgnoreCase(str) || "0".equalsIgnoreCase(str));
238-
} else if (type == Byte.class || type == byte.class) {
304+
} else if (type == Byte.class) {
239305
return Byte.parseByte(value.toString());
240-
} else if (type == Short.class || type == short.class) {
306+
} else if (type == Short.class) {
241307
return Short.parseShort(value.toString());
242-
} else if (type == Integer.class || type == int.class) {
308+
} else if (type == Integer.class) {
243309
return Integer.parseInt(value.toString());
244-
} else if (type == Long.class || type == long.class) {
310+
} else if (type == Long.class) {
245311
return Long.parseLong(value.toString());
246-
} else if (type == Float.class || type == float.class) {
312+
} else if (type == Float.class) {
247313
return Float.parseFloat(value.toString());
248-
} else if (type == Double.class || type == double.class) {
314+
} else if (type == Double.class) {
249315
return Double.parseDouble(value.toString());
250316
} else if (type == java.math.BigDecimal.class) {
251317
return new java.math.BigDecimal(value.toString());
252-
} else if (type == byte[].class) {
253-
return value.toString().getBytes();
254-
} else if (type == LocalDate.class && value instanceof TemporalAccessor) {
255-
return LocalDate.from((TemporalAccessor) value);
256-
} else if (type == LocalDateTime.class && value instanceof TemporalAccessor) {
257-
return LocalDateTime.from((TemporalAccessor) value);
258-
} else if (type == OffsetDateTime.class && value instanceof TemporalAccessor) {
259-
return OffsetDateTime.from((TemporalAccessor) value);
260-
} else if (type == ZonedDateTime.class && value instanceof TemporalAccessor) {
261-
return ZonedDateTime.from((TemporalAccessor) value);
262-
} else if (type == Instant.class && value instanceof TemporalAccessor) {
263-
return Instant.from((TemporalAccessor) value);
264-
} else if (type == Date.class && value instanceof TemporalAccessor) {
265-
return Date.valueOf(LocalDate.from((TemporalAccessor) value));
266-
} else if (type == java.sql.Timestamp.class && value instanceof TemporalAccessor) {
267-
return java.sql.Timestamp.valueOf(LocalDateTime.from((TemporalAccessor) value));
268-
} else if (type == java.sql.Time.class && value instanceof TemporalAccessor) {
269-
return java.sql.Time.valueOf(LocalTime.from((TemporalAccessor) value));
270-
} else if (type == java.sql.Array.class && value instanceof BinaryStreamReader.ArrayValue) {//It's cleaner to use getList but this handles the more generic getObject
271-
BinaryStreamReader.ArrayValue arrayValue = (BinaryStreamReader.ArrayValue) value;
272-
if (column != null && column.getArrayBaseColumn() != null) {
273-
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
274-
Object[] convertedValues = convertArray(arrayValue.getArrayOfObjects(), JdbcUtils.convertToJavaClass(baseType));
275-
return new Array(column, convertedValues);
318+
} else if (value instanceof TemporalAccessor) {
319+
TemporalAccessor temporalValue = (TemporalAccessor) value;
320+
if (type == LocalDate.class) {
321+
return LocalDate.from(temporalValue);
322+
} else if (type == LocalDateTime.class) {
323+
return LocalDateTime.from(temporalValue);
324+
} else if (type == OffsetDateTime.class) {
325+
return OffsetDateTime.from(temporalValue);
326+
} else if (type == ZonedDateTime.class) {
327+
return ZonedDateTime.from(temporalValue);
328+
} else if (type == Instant.class) {
329+
return Instant.from(temporalValue);
330+
} else if (type == Date.class) {
331+
return Date.valueOf(LocalDate.from(temporalValue));
332+
} else if (type == java.sql.Timestamp.class) {
333+
return java.sql.Timestamp.valueOf(LocalDateTime.from(temporalValue));
334+
} else if (type == java.sql.Time.class) {
335+
return java.sql.Time.valueOf(LocalTime.from(temporalValue));
276336
}
277-
return new Array(column, arrayValue.getArrayOfObjects());
278-
} else if (type == java.sql.Array.class && value instanceof List<?>) {
279-
if (column != null && column.getArrayBaseColumn() != null) {
280-
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
281-
Object[] convertedValues = convertList((List<?>) value, JdbcUtils.convertToJavaClass(baseType));
282-
return new Array(column, convertedValues);
283-
}
284-
// base type is unknown. all objects should be converted
285-
return new Array(column, ((List<?>) value).toArray());
286-
} else if (type == java.sql.Array.class && value.getClass().isArray()) {
287-
return new Array(column, arrayToObjectArray(value));
288337
} else if (type == Inet4Address.class && value instanceof Inet6Address) {
289338
// Convert Inet6Address to Inet4Address
290339
return InetAddressConverter.convertToIpv4((InetAddress) value);
291340
} else if (type == Inet6Address.class && value instanceof Inet4Address) {
292341
// Convert Inet4Address to Inet6Address
293342
return InetAddressConverter.convertToIpv6((InetAddress) value);
294-
} else if (type == Tuple.class && value.getClass().isArray()) {
295-
return new Tuple(true, value);
296343
}
297344
} catch (Exception e) {
298345
throw new SQLException("Failed to convert from " + value.getClass().getName() + " to " + type.getName(), ExceptionUtils.SQL_STATE_DATA_EXCEPTION, e);
@@ -301,28 +348,27 @@ public static Object convert(Object value, Class<?> type, ClickHouseColumn colum
301348
throw new SQLException("Unsupported conversion from " + value.getClass().getName() + " to " + type.getName(), ExceptionUtils.SQL_STATE_DATA_EXCEPTION);
302349
}
303350

304-
public static Object[] convertList(List<?> values, Class<?> type) throws SQLException {
351+
public static <T> T[] convertList(List<?> values, Class<T> type) throws SQLException {
305352
if (values == null) {
306353
return null;
307354
}
308355
if (values.isEmpty()) {
309-
return new Object[0];
356+
return (T[]) java.lang.reflect.Array.newInstance(type, 0);
310357
}
311-
312-
Object[] convertedValues = new Object[values.size()];
358+
T[] convertedValues = (T[]) java.lang.reflect.Array.newInstance(type, values.size());
313359
for (int i = 0; i < values.size(); i++) {
314-
convertedValues[i] = convert(values.get(i), type);
360+
convertedValues[i] = (T) convert(values.get(i), type);
315361
}
316362
return convertedValues;
317363
}
318364

319-
public static Object[] convertArray(Object[] values, Class<?> type) throws SQLException {
320-
if (values == null || type == null) {
321-
return values;
365+
public static <T> T[] convertArray(Object[] values, Class<T> type) throws SQLException {
366+
if (values == null) {
367+
return null;
322368
}
323-
Object[] convertedValues = new Object[values.length];
369+
T[] convertedValues = (T[]) java.lang.reflect.Array.newInstance(type, values.length);
324370
for (int i = 0; i < values.length; i++) {
325-
convertedValues[i] = convert(values[i], type);
371+
convertedValues[i] = (T) convert(values[i], type);
326372
}
327373
return convertedValues;
328374
}
@@ -340,56 +386,56 @@ private static Object[] arrayToObjectArray(Object array) {
340386

341387
if (array instanceof byte[]) {
342388
byte[] src = (byte[]) array;
343-
Object[] dst = new Object[src.length];
389+
Byte[] dst = new Byte[src.length];
344390
for (int i = 0; i < src.length; i++) {
345391
dst[i] = src[i];
346392
}
347393
return dst;
348394
} else if (array instanceof short[]) {
349395
short[] src = (short[]) array;
350-
Object[] dst = new Object[src.length];
396+
Short[] dst = new Short[src.length];
351397
for (int i = 0; i < src.length; i++) {
352398
dst[i] = src[i];
353399
}
354400
return dst;
355401
} else if (array instanceof int[]) {
356402
int[] src = (int[]) array;
357-
Object[] dst = new Object[src.length];
403+
Integer[] dst = new Integer[src.length];
358404
for (int i = 0; i < src.length; i++) {
359405
dst[i] = src[i];
360406
}
361407
return dst;
362408
} else if (array instanceof long[]) {
363409
long[] src = (long[]) array;
364-
Object[] dst = new Object[src.length];
410+
Long[] dst = new Long[src.length];
365411
for (int i = 0; i < src.length; i++) {
366412
dst[i] = src[i];
367413
}
368414
return dst;
369415
} else if (array instanceof float[]) {
370416
float[] src = (float[]) array;
371-
Object[] dst = new Object[src.length];
417+
Float[] dst = new Float[src.length];
372418
for (int i = 0; i < src.length; i++) {
373419
dst[i] = src[i];
374420
}
375421
return dst;
376422
} else if (array instanceof double[]) {
377423
double[] src = (double[]) array;
378-
Object[] dst = new Object[src.length];
424+
Double[] dst = new Double[src.length];
379425
for (int i = 0; i < src.length; i++) {
380426
dst[i] = src[i];
381427
}
382428
return dst;
383429
} else if (array instanceof char[]) {
384430
char[] src = (char[]) array;
385-
Object[] dst = new Object[src.length];
431+
Character[] dst = new Character[src.length];
386432
for (int i = 0; i < src.length; i++) {
387433
dst[i] = src[i];
388434
}
389435
return dst;
390436
} else if (array instanceof boolean[]) {
391437
boolean[] src = (boolean[]) array;
392-
Object[] dst = new Object[src.length];
438+
Boolean[] dst = new Boolean[src.length];
393439
for (int i = 0; i < src.length; i++) {
394440
dst[i] = src[i];
395441
}

jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/JdbcUtilsTest.java

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ public class JdbcUtilsTest {
1919

2020
@Test(groups = {"unit"})
2121
public void testConvertPrimitiveTypes() throws SQLException {
22-
assertEquals(JdbcUtils.convert(1, int.class), 1);
23-
assertEquals(JdbcUtils.convert(1L, long.class), 1L);
22+
assertEquals(JdbcUtils.convert(1, int.class), Integer.valueOf(1));
23+
assertEquals(JdbcUtils.convert(1L, long.class), Long.valueOf(1));
2424
assertEquals(JdbcUtils.convert("1", String.class), "1");
25-
assertEquals(JdbcUtils.convert(1.0f, float.class), 1.0f);
26-
assertEquals(JdbcUtils.convert(1.0, double.class), 1.0);
27-
assertEquals(JdbcUtils.convert(true, boolean.class), true);
28-
assertEquals(JdbcUtils.convert((short) 1, short.class), (short) 1);
29-
assertEquals(JdbcUtils.convert((byte) 1, byte.class), (byte) 1);
30-
assertEquals(JdbcUtils.convert(1.0d, BigDecimal.class), BigDecimal.valueOf(1.0d));
25+
assertEquals(JdbcUtils.convert(1.0f, float.class), Float.valueOf(1));
26+
assertEquals(JdbcUtils.convert(1.0, double.class), Double.valueOf(1));
27+
assertEquals(JdbcUtils.convert(true, boolean.class), Boolean.TRUE);
28+
assertEquals(JdbcUtils.convert((short) 1, short.class), Short.valueOf("1"));
29+
assertEquals(JdbcUtils.convert((byte) 1, byte.class), Byte.valueOf("1"));
30+
assertEquals(JdbcUtils.convert(1.0d, BigDecimal.class), new BigDecimal("1.0"));
3131
}
3232

3333

@@ -40,32 +40,34 @@ public void testConvertToArray() throws Exception {
4040
java.sql.Array array = (java.sql.Array) JdbcUtils.convert(arrayValue, java.sql.Array.class, column);
4141
Object arr = array.getArray();
4242
assertEquals(array.getBaseTypeName(), "Int32");
43-
assertEquals(arr.getClass().getComponentType(), Object.class);
44-
Object[] arrs = (Object[]) arr;
43+
assertEquals(arr.getClass().getComponentType(), Integer.class);
44+
Integer[] arrs = (Integer[]) arr;
4545
assertEquals(arrs[0], 1);
4646
assertEquals(arrs[1], 2);
4747
}
4848

4949

5050
@Test(groups = {"unit"})
5151
public void testConvertArray() throws Exception {
52-
Object[] src = {1, 2, 3};
53-
Object[] dst = JdbcUtils.convertArray(src, int.class);
54-
assertEquals(dst.length, src.length);
55-
assertEquals(dst[0], src[0]);
56-
assertEquals(dst[1], src[1]);
57-
assertEquals(dst[2], src[2]);
58-
59-
assertNull(JdbcUtils.convertArray(null, int.class));
60-
assertEquals(JdbcUtils.convertArray(new Integer[] { 1, 2}, null), new Integer[] { 1, 2});
52+
// primitive classes are unwrapped
53+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Boolean.class), new Boolean[] { false, true });
54+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Byte.class), new Byte[] { 0, 1 });
55+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Short.class), new Short[] { 0, 1 });
56+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Integer.class), new Integer[] { 0, 1 });
57+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Long.class), new Long[] { 0L, 1L });
58+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Float.class), new Float[] { 0.0f, 1.0f });
59+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Double.class), new Double[] { 0.0, 1.0 });
60+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, String.class), new String[] { "0", "1" });
61+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, BigDecimal.class), new BigDecimal[] { BigDecimal.valueOf(0), BigDecimal.valueOf(1) });
62+
assertNull(JdbcUtils.convertArray(null, Integer.class));
6163
}
6264

6365

6466
@Test(groups = {"unit"})
6567
public void testConvertList() throws Exception {
6668
ClickHouseColumn column = ClickHouseColumn.of("arr", "Array(Int32)");
6769
List<Integer> src = Arrays.asList(1, 2, 3);
68-
Object[] dst = JdbcUtils.convertList(src, Integer.class);
70+
Integer[] dst = JdbcUtils.convertList(src, Integer.class);
6971
assertEquals(dst.length, src.size());
7072
assertEquals(dst[0], src.get(0));
7173
assertEquals(dst[1], src.get(1));

0 commit comments

Comments
 (0)