66import com .clickhouse .data .ClickHouseDataType ;
77import com .clickhouse .data .Tuple ;
88import com .clickhouse .data .format .BinaryStreamUtils ;
9+ import com .clickhouse .jdbc .PreparedStatementImpl ;
910import com .clickhouse .jdbc .types .Array ;
1011import com .google .common .collect .ImmutableMap ;
1112import org .slf4j .Logger ;
3132import java .util .List ;
3233import java .util .Map ;
3334import java .util .Set ;
35+ import java .util .Stack ;
3436import java .util .TreeMap ;
37+ import java .util .function .Function ;
3538import java .util .stream .Collectors ;
3639
3740public 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 ;
0 commit comments