Skip to content

Commit e251400

Browse files
authored
Merge pull request #2667 from ClickHouse/fix_array_tests
Fix array tests
2 parents 235880b + d7856fd commit e251400

File tree

2 files changed

+110
-26
lines changed

2 files changed

+110
-26
lines changed

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

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.clickhouse.data.ClickHouseDataType;
77
import com.clickhouse.data.Tuple;
88
import com.clickhouse.data.format.BinaryStreamUtils;
9+
import com.clickhouse.jdbc.PreparedStatementImpl;
910
import com.clickhouse.jdbc.types.Array;
1011
import com.google.common.collect.ImmutableMap;
1112
import org.slf4j.Logger;
@@ -31,7 +32,9 @@
3132
import java.util.List;
3233
import java.util.Map;
3334
import java.util.Set;
35+
import java.util.Stack;
3436
import java.util.TreeMap;
37+
import java.util.function.Function;
3538
import java.util.stream.Collectors;
3639

3740
public class JdbcUtils {
@@ -85,6 +88,8 @@ private static Map<ClickHouseDataType, SQLType> generateTypeMap() {
8588
map.put(ClickHouseDataType.LineString, JDBCType.ARRAY);
8689
map.put(ClickHouseDataType.MultiPolygon, JDBCType.ARRAY);
8790
map.put(ClickHouseDataType.MultiLineString, JDBCType.ARRAY);
91+
map.put(ClickHouseDataType.Tuple, JDBCType.OTHER);
92+
map.put(ClickHouseDataType.Nothing, JDBCType.OTHER);
8893
return ImmutableMap.copyOf(map);
8994
}
9095

@@ -169,6 +174,8 @@ private static Map<ClickHouseDataType, Class<?>> getDataTypeClassMap() {
169174
default:
170175
map.put(e.getKey(), Object.class);
171176
}
177+
} else if (e.getValue().equals(JDBCType.STRUCT)) {
178+
map.put(e.getKey(), Object.class);
172179
} else {
173180
map.put(e.getKey(), SQL_TYPE_TO_CLASS_MAP.get(e.getValue()));
174181
}
@@ -255,12 +262,13 @@ public static Object convert(Object value, Class<?> type, ClickHouseColumn colum
255262
if (value instanceof List<?>) {
256263
List<?> listValue = (List<?>) value;
257264
if (type != java.sql.Array.class) {
258-
return convertList(listValue, type);
265+
return convertList(listValue, type, column.getArrayNestedLevel());
259266
}
260267

261268
if (column != null && column.getArrayBaseColumn() != null) {
262269
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
263-
Object[] convertedValues = convertList(listValue, convertToJavaClass(baseType));
270+
Object[] convertedValues = convertList(listValue, convertToJavaClass(baseType),
271+
column.getArrayNestedLevel());
264272
return new Array(column, convertedValues);
265273
}
266274

@@ -281,7 +289,8 @@ public static Object convert(Object value, Class<?> type, ClickHouseColumn colum
281289

282290
if (column != null && column.getArrayBaseColumn() != null) {
283291
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
284-
Object[] convertedValues = convertArray(arrayValue.getArrayOfObjects(), convertToJavaClass(baseType));
292+
Object[] convertedValues = convertArray(arrayValue.getArray(), convertToJavaClass(baseType),
293+
column.getArrayNestedLevel());
285294
return new Array(column, convertedValues);
286295
}
287296

@@ -348,31 +357,103 @@ public static Object convertObject(Object value, Class<?> type) throws SQLExcept
348357
throw new SQLException("Unsupported conversion from " + value.getClass().getName() + " to " + type.getName(), ExceptionUtils.SQL_STATE_DATA_EXCEPTION);
349358
}
350359

351-
public static <T> T[] convertList(List<?> values, Class<T> type) throws SQLException {
360+
public static <T> T[] convertList(List<?> values, Class<T> type, int dimensions) throws SQLException {
352361
if (values == null) {
353362
return null;
354363
}
355-
if (values.isEmpty()) {
356-
return (T[]) java.lang.reflect.Array.newInstance(type, 0);
357-
}
358-
T[] convertedValues = (T[]) java.lang.reflect.Array.newInstance(type, values.size());
359-
for (int i = 0; i < values.size(); i++) {
360-
convertedValues[i] = (T) convert(values.get(i), type);
364+
365+
int[] arrayDimensions = new int[dimensions];
366+
arrayDimensions[0] = values.size();
367+
T[] convertedValues = (T[]) java.lang.reflect.Array.newInstance(type, arrayDimensions);
368+
Stack<ArrayProcessingCursor> stack = new Stack<>();
369+
stack.push(new ArrayProcessingCursor(convertedValues, values, values.size()));
370+
371+
while (!stack.isEmpty()) {
372+
ArrayProcessingCursor cursor = stack.pop();
373+
374+
for (int i = 0; i < cursor.size; i++) {
375+
Object value = cursor.getValue(i);
376+
if (value == null) {
377+
continue; // no need to set null value
378+
} else if (value instanceof List<?>) {
379+
List<?> srcList = (List<?>) value;
380+
arrayDimensions = new int[Math.max(dimensions - stack.size() - 1, 1)];
381+
arrayDimensions[0] = srcList.size();
382+
T[] targetArray = (T[]) java.lang.reflect.Array.newInstance(type, arrayDimensions);
383+
stack.push(new ArrayProcessingCursor(targetArray, value, srcList.size()));
384+
java.lang.reflect.Array.set(cursor.targetArray, i, targetArray);
385+
} else {
386+
java.lang.reflect.Array.set(cursor.targetArray, i, convert(value, type));
387+
}
388+
}
361389
}
390+
362391
return convertedValues;
363392
}
364393

365-
public static <T> T[] convertArray(Object[] values, Class<T> type) throws SQLException {
394+
/**
395+
* Convert array to java array and all its elements
396+
* @param values
397+
* @param type
398+
* @param dimensions
399+
* @return
400+
* @param <T>
401+
* @throws SQLException
402+
*/
403+
public static <T> T[] convertArray(Object values, Class<T> type, int dimensions) throws SQLException {
366404
if (values == null) {
367405
return null;
368406
}
369-
T[] convertedValues = (T[]) java.lang.reflect.Array.newInstance(type, values.length);
370-
for (int i = 0; i < values.length; i++) {
371-
convertedValues[i] = (T) convert(values[i], type);
407+
408+
int[] arrayDimensions = new int[dimensions];
409+
arrayDimensions[0] = java.lang.reflect.Array.getLength(values);
410+
T[] convertedValues = (T[]) java.lang.reflect.Array.newInstance(type, arrayDimensions);
411+
Stack<ArrayProcessingCursor> stack = new Stack<>();
412+
stack.push(new ArrayProcessingCursor(convertedValues, values, arrayDimensions[0]));
413+
414+
while (!stack.isEmpty()) {
415+
ArrayProcessingCursor cursor = stack.pop();
416+
417+
for (int i = 0; i < cursor.size; i++) {
418+
Object value = cursor.getValue(i);
419+
if (value == null) {
420+
continue; // no need to set null value
421+
} else if (value.getClass().isArray()) {
422+
arrayDimensions = new int[Math.max(dimensions - stack.size() - 1, 1)];
423+
arrayDimensions[0] = java.lang.reflect.Array.getLength(value);
424+
T[] targetArray = (T[]) java.lang.reflect.Array.newInstance(type, arrayDimensions);
425+
stack.push(new ArrayProcessingCursor(targetArray, value, arrayDimensions[0]));
426+
java.lang.reflect.Array.set(cursor.targetArray, i, targetArray);
427+
} else {
428+
java.lang.reflect.Array.set(cursor.targetArray, i, convert(value, type));
429+
}
430+
}
372431
}
432+
373433
return convertedValues;
374434
}
375435

436+
private static final class ArrayProcessingCursor {
437+
private final Object targetArray;
438+
private final int size;
439+
private final Function<Integer, Object> valueGetter;
440+
441+
public ArrayProcessingCursor(Object targetArray, Object srcArray, int size) {
442+
this.targetArray = targetArray;
443+
this.size = size;
444+
if (srcArray instanceof List<?>) {
445+
List<?> list = (List<?>) srcArray;
446+
this.valueGetter = list::get;
447+
} else {
448+
this.valueGetter = (i) -> java.lang.reflect.Array.get(srcArray, i);
449+
}
450+
}
451+
452+
public Object getValue(int i) {
453+
return valueGetter.apply(i);
454+
}
455+
}
456+
376457
private static Object[] arrayToObjectArray(Object array) {
377458
if (array == null) {
378459
return null;

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

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,30 +50,33 @@ public void testConvertToArray() throws Exception {
5050
@Test(groups = {"unit"})
5151
public void testConvertArray() throws Exception {
5252
// 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));
53+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Boolean.class, 1), new Boolean[] { false, true });
54+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Byte.class, 1), new Byte[] { 0, 1 });
55+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Short.class, 1), new Short[] { 0, 1 });
56+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Integer.class, 1), new Integer[] { 0, 1 });
57+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Long.class, 1), new Long[] { 0L, 1L });
58+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Float.class, 1), new Float[] { 0.0f, 1.0f });
59+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Double.class, 1), new Double[] { 0.0, 1.0 });
60+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, String.class, 1), new String[] { "0", "1" });
61+
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, BigDecimal.class, 1), new BigDecimal[] { BigDecimal.valueOf(0), BigDecimal.valueOf(1) });
62+
assertEquals(JdbcUtils.convertArray(new Object[][] { new Object[] {1, 2, 3}, new Object[] { 4, 5, 6} }, String.class, 2),
63+
new String[][] { new String[] {"1", "2", "3"}, new String[] {"4", "5", "6"} });
64+
65+
assertNull(JdbcUtils.convertArray(null, Integer.class, 1));
6366
}
6467

6568

6669
@Test(groups = {"unit"})
6770
public void testConvertList() throws Exception {
6871
ClickHouseColumn column = ClickHouseColumn.of("arr", "Array(Int32)");
6972
List<Integer> src = Arrays.asList(1, 2, 3);
70-
Integer[] dst = JdbcUtils.convertList(src, Integer.class);
73+
Integer[] dst = JdbcUtils.convertList(src, Integer.class, 1);
7174
assertEquals(dst.length, src.size());
7275
assertEquals(dst[0], src.get(0));
7376
assertEquals(dst[1], src.get(1));
7477
assertEquals(dst[2], src.get(2));
7578

76-
assertNull(JdbcUtils.convertList(null, Integer.class));
79+
assertNull(JdbcUtils.convertList(null, Integer.class, 1));
7780
}
7881

7982

0 commit comments

Comments
 (0)