3838import java .util .Map ;
3939import java .util .Objects ;
4040import java .util .Set ;
41+ import java .util .Stack ;
4142import java .util .StringTokenizer ;
4243import java .util .TimeZone ;
4344import java .util .UUID ;
@@ -154,10 +155,6 @@ private static Map<Class<?>, ClickHouseColumn> getPredefinedTypeColumnsMap() {
154155 map .put (double [][].class , ClickHouseColumn .of ("v" , "Array(Array(Float64))" ));
155156 map .put (double [][][].class , ClickHouseColumn .of ("v" , "Array(Array(Array(Float64)))" ));
156157
157- map .put (String [].class , ClickHouseColumn .of ("v" , "Array(String)" ));
158- map .put (String [][].class , ClickHouseColumn .of ("v" , "Array(Array(String))" ));
159- map .put (String [][][].class , ClickHouseColumn .of ("v" , "Array(Array(Array(String)))" ));
160-
161158 return Collections .unmodifiableMap (map );
162159 }
163160
@@ -194,7 +191,7 @@ public static ClickHouseColumn valueToColumnForDynamicType(Object value) {
194191 column = ClickHouseColumn .of ("v" , "Map(" + keyInfo .getOriginalTypeName () + ", " + valueInfo .getOriginalTypeName () + ")" );
195192 } else if (value instanceof Enum <?>) {
196193 column = enumValue2Column ((Enum )value );
197- } else if (value instanceof List <?>) {
194+ } else if (value instanceof List <?> || ( value != null && value . getClass (). isArray ()) ) {
198195 column = listValue2Column (value );
199196 } else if (value == null ) {
200197 column = PREDEFINED_TYPE_COLUMNS .get (Void .class );
@@ -219,28 +216,57 @@ public static ClickHouseColumn valueToColumnForDynamicType(Object value) {
219216 // In this case we need to find max depth.
220217
221218 private static ClickHouseColumn listValue2Column (Object value ) {
222- ClickHouseColumn column ;
223- if (value instanceof List <?>) {
224- List <?> list = (List <?>) value ;
225- StringBuilder type = new StringBuilder ("Array()" );
226- int insertPos = type .length () - 1 ;
227- while (!list .isEmpty () && list .get (0 ) instanceof List <?>) {
228- type .insert (insertPos , "Array()" );
229- insertPos += 6 ; // add len of 'Array(' string
230- list = (List <?>) list .get (0 );
231- }
232- if (list .isEmpty () || list .get (0 ) == null ) {
233- type .insert (insertPos , "Nothing" );
234- column = ClickHouseColumn .of ("v" , type .toString ());
235- } else {
236- ClickHouseColumn arrayBaseColumn = PREDEFINED_TYPE_COLUMNS .get (list .get (0 ).getClass ());
219+
220+ ClickHouseColumn column = PREDEFINED_TYPE_COLUMNS .get (value .getClass ());
221+ if (column != null ) {
222+ return column ;
223+ }
224+
225+ if (value instanceof List <?> || (value .getClass ().isArray ())) {
226+ Stack <Object []> arrays = new Stack <>();
227+ arrays .push (new Object []{value , 1 });
228+ int maxDepth = 0 ;
229+ boolean hasNulls = false ;
230+ ClickHouseColumn arrayBaseColumn = null ;
231+ StringBuilder typeStr = new StringBuilder ();
232+ int insertPos = 0 ;
233+ while (!arrays .isEmpty ()) {
234+ Object [] arr = arrays .pop ();
235+ int depth = (Integer ) arr [1 ];
236+ if (depth > maxDepth ) {
237+ maxDepth = depth ;
238+ typeStr .insert (insertPos , "Array()" );
239+ insertPos += 6 ;
240+ }
241+
242+ boolean isArray = arr [0 ].getClass ().isArray ();
243+ List <?> list = isArray ? null : ((List <?>) arr [0 ]);
244+ int len = isArray ? Array .getLength (arr [0 ]) : list .size ();
245+ for (int i = 0 ; i < len ; i ++) {
246+ Object item = isArray ? Array .get (arr [0 ], i ) : list .get (i );
247+ if (!hasNulls && item == null ) {
248+ hasNulls = true ;
249+ } else if (item != null && (item instanceof List <?> || item .getClass ().isArray ())) {
250+ arrays .push (new Object []{item , depth + 1 });
251+ } else if (arrayBaseColumn == null && item != null ) {
252+ arrayBaseColumn = PREDEFINED_TYPE_COLUMNS .get (item .getClass ());
253+ if (arrayBaseColumn == null ) {
254+ throw new ClientException ("Cannot serialize " + item .getClass () + " as array element" );
255+ }
256+ }
257+ }
258+
237259 if (arrayBaseColumn != null ) {
238- type .insert (insertPos , arrayBaseColumn .getOriginalTypeName ());
239- column = ClickHouseColumn .of ("v" , type .toString ());
240- } else {
241- column = null ;
260+ if (hasNulls ) {
261+ typeStr .insert (insertPos , "Nullable()" );
262+ insertPos += 9 ;
263+ }
264+ typeStr .insert (insertPos , arrayBaseColumn .getOriginalTypeName ());
265+ break ;
242266 }
243267 }
268+
269+ column = ClickHouseColumn .of ("v" , typeStr .toString ());
244270 } else {
245271 column = null ;
246272 }
@@ -370,27 +396,20 @@ public static void writeDynamicTypeTag(OutputStream stream, ClickHouseColumn typ
370396 }
371397
372398 public static void serializeArrayData (OutputStream stream , Object value , ClickHouseColumn column ) throws IOException {
399+ if (value == null ) {
400+ writeVarInt (stream , 0 );
401+ return ;
402+ }
373403
374- if (value instanceof List <?>) {
375- //Serialize the array to the stream
376- //The array is a list of values
377- List <?> values = (List <?>) value ;
378- writeVarInt (stream , values .size ());
379- for (Object val : values ) {
380- if (column .getArrayBaseColumn ().isNullable ()) {
381- if (val == null ) {
382- writeNull (stream );
383- continue ;
384- }
385- writeNonNull (stream );
386- }
387- serializeData (stream , val , column .getNestedColumns ().get (0 ));
388- }
389- } else if (value .getClass ().isArray ()) {
390- writeVarInt (stream , Array .getLength (value ));
391- for (int i = 0 ; i < Array .getLength (value ); i ++) {
392- Object val = Array .get (value , i );
393- if (column .getArrayBaseColumn ().isNullable ()) {
404+ boolean isArray = value .getClass ().isArray ();
405+ if (value instanceof List <?> || isArray ) {
406+ List <?> list = isArray ? null : (List <?>)value ;
407+ int len = isArray ? Array .getLength (value ) : list .size ();
408+
409+ writeVarInt (stream , len );
410+ for (int i = 0 ; i < len ; i ++) {
411+ Object val = isArray ? Array .get (value , i ) : list .get (i );
412+ if (column .getArrayNestedLevel () == 1 && column .getArrayBaseColumn ().isNullable ()) {
394413 if (val == null ) {
395414 writeNull (stream );
396415 continue ;
@@ -515,12 +534,6 @@ private static void serializePrimitiveData(OutputStream stream, Object value, Cl
515534 case UUID :
516535 BinaryStreamUtils .writeUuid (stream , (UUID ) value );
517536 break ;
518- // case Enum8:
519- // BinaryStreamUtils.writeEnum8(stream, (Byte) value);
520- // break;
521- // case Enum16:
522- // BinaryStreamUtils.writeEnum16(stream, convertToInteger(value));
523- // break;
524537 case Enum8 :
525538 case Enum16 :
526539 serializeEnumData (stream , column , value );
0 commit comments