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,12 @@ 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 ), column . getArrayNestedLevel () );
264271 return new Array (column , convertedValues );
265272 }
266273
@@ -348,17 +355,41 @@ public static Object convertObject(Object value, Class<?> type) throws SQLExcept
348355 throw new SQLException ("Unsupported conversion from " + value .getClass ().getName () + " to " + type .getName (), ExceptionUtils .SQL_STATE_DATA_EXCEPTION );
349356 }
350357
351- public static <T > T [] convertList (List <?> values , Class <T > type ) throws SQLException {
358+ public static <T > T [] convertList (List <?> values , Class <T > type , int dimensions ) throws SQLException {
352359 if (values == null ) {
353360 return null ;
354361 }
355362 if (values .isEmpty ()) {
356363 return (T []) java .lang .reflect .Array .newInstance (type , 0 );
357364 }
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 );
365+
366+
367+ int [] arrayDimensions = new int [dimensions ];
368+ arrayDimensions [0 ] = values .size ();
369+ T [] convertedValues = (T []) java .lang .reflect .Array .newInstance (type , arrayDimensions );
370+ Stack <ArrayProcessingCursor > stack = new Stack <>();
371+ stack .push (new ArrayProcessingCursor (convertedValues , values , 0 , values .size ()));
372+
373+ while (!stack .isEmpty ()) {
374+ ArrayProcessingCursor cursor = stack .pop ();
375+
376+ for (int i = 0 ; i < cursor .size ; i ++) {
377+ Object value = cursor .getValue (i );
378+ if (value == null ) {
379+ continue ; // no need to set null value
380+ } else if (value instanceof List <?>) {
381+ List <?> srcList = (List <?>) value ;
382+ arrayDimensions = new int [Math .max (dimensions - stack .size () - 1 , 1 )];
383+ arrayDimensions [0 ] = srcList .size ();
384+ T [] targetArray = (T []) java .lang .reflect .Array .newInstance (type , arrayDimensions );
385+ stack .push (new ArrayProcessingCursor (targetArray , value , 0 , srcList .size ()));
386+ java .lang .reflect .Array .set (cursor .targetArray , i , targetArray );
387+ } else {
388+ java .lang .reflect .Array .set (cursor .targetArray , i , convert (value , type ));
389+ }
390+ }
361391 }
392+
362393 return convertedValues ;
363394 }
364395
@@ -373,6 +404,31 @@ public static <T> T[] convertArray(Object[] values, Class<T> type) throws SQLExc
373404 return convertedValues ;
374405 }
375406
407+ private static final class ArrayProcessingCursor {
408+ private final Object targetArray ;
409+ private final Object srcArray ;
410+ private final int pos ;
411+ private final int size ;
412+ private final Function <Integer , Object > valueGetter ;
413+
414+ public ArrayProcessingCursor (Object targetArray , Object srcArray , int pos , int size ) {
415+ this .targetArray = targetArray ;
416+ this .srcArray = srcArray ;
417+ this .pos = pos ;
418+ this .size = size ;
419+ if (srcArray instanceof List <?>) {
420+ List <?> list = (List <?>) srcArray ;
421+ this .valueGetter = list ::get ;
422+ } else {
423+ this .valueGetter = (i ) -> java .lang .reflect .Array .get (srcArray , i );
424+ }
425+ }
426+
427+ public Object getValue (int i ) {
428+ return valueGetter .apply (i );
429+ }
430+ }
431+
376432 private static Object [] arrayToObjectArray (Object array ) {
377433 if (array == null ) {
378434 return null ;
0 commit comments