Skip to content

Commit 4999fdd

Browse files
committed
Added tests for empty arrays and fixed issue with that. Also fixed issue with double[] and float[]
1 parent 9d5d0f0 commit 4999fdd

2 files changed

Lines changed: 278 additions & 5 deletions

File tree

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReader.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -604,15 +604,14 @@ public static byte[] readNBytesLE(InputStream input, byte[] buffer, int offset,
604604
*/
605605
public ArrayValue readArray(ClickHouseColumn column) throws IOException {
606606
int len = readVarInt(input);
607-
if (len == 0) {
608-
return new ArrayValue(Object.class, 0);
609-
}
610607

611608
ArrayValue array;
612609
ClickHouseColumn itemTypeColumn = column.getNestedColumns().get(0);
613-
if (column.getArrayNestedLevel() == 1) {
610+
if (len == 0) {
611+
Class<?> itemClass = itemTypeColumn.getDataType().getPrimitiveClass();
612+
array = new ArrayValue(itemClass == null ? Object.class : itemClass, 0);
613+
} else if (column.getArrayNestedLevel() == 1) {
614614
array = readArrayItem(itemTypeColumn, len);
615-
616615
} else {
617616
array = new ArrayValue(ArrayValue.class, len);
618617
for (int i = 0; i < len; i++) {
@@ -645,6 +644,10 @@ public ArrayValue readArrayItem(ClickHouseColumn itemTypeColumn, int len) throws
645644
itemClass = long.class;
646645
} else if (firstValue instanceof Boolean) {
647646
itemClass = boolean.class;
647+
} else if (firstValue instanceof Float) {
648+
itemClass = float.class;
649+
} else if (firstValue instanceof Double) {
650+
itemClass = double.class;
648651
}
649652

650653
array = new ArrayValue(itemClass, len);

client-v2/src/test/java/com/clickhouse/client/api/data_formats/ClickHouseBinaryFormatReaderTest.java

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.time.LocalDateTime;
1616
import java.time.ZonedDateTime;
1717
import java.util.Arrays;
18+
import java.util.List;
1819
import java.util.TimeZone;
1920
import java.util.function.Consumer;
2021

@@ -576,4 +577,273 @@ public void testGetObjectArrayPrimitiveTypes() throws Exception {
576577
Assert.assertEquals(floatResult[0], 1.5);
577578
Assert.assertEquals(floatResult[1], 2.5);
578579
}
580+
581+
@Test
582+
public void testReadingArraysByIndex() throws Exception {
583+
ByteArrayOutputStream out = new ByteArrayOutputStream();
584+
String[] names = new String[]{"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8"};
585+
String[] types = new String[]{"Array(Int8)", "Array(String)", "Array(Int16)", "Array(Int32)",
586+
"Array(Int64)", "Array(Float32)", "Array(Float64)", "Array(Bool)"};
587+
588+
BinaryStreamUtils.writeVarInt(out, names.length);
589+
for (String name : names) {
590+
BinaryStreamUtils.writeString(out, name);
591+
}
592+
for (String type : types) {
593+
BinaryStreamUtils.writeString(out, type);
594+
}
595+
596+
// write data
597+
BinaryStreamUtils.writeVarInt(out, 2);
598+
BinaryStreamUtils.writeInt8(out, (byte) 1);
599+
BinaryStreamUtils.writeInt8(out, (byte) 2);
600+
601+
BinaryStreamUtils.writeVarInt(out, 2);
602+
BinaryStreamUtils.writeString(out, "a");
603+
BinaryStreamUtils.writeString(out, "b");
604+
605+
BinaryStreamUtils.writeVarInt(out, 2);
606+
BinaryStreamUtils.writeInt16(out, (short) 1);
607+
BinaryStreamUtils.writeInt16(out, (short) 2);
608+
609+
BinaryStreamUtils.writeVarInt(out, 2);
610+
BinaryStreamUtils.writeInt32(out, (int) 1);
611+
BinaryStreamUtils.writeInt32(out, (int) 2);
612+
613+
BinaryStreamUtils.writeVarInt(out, 2);
614+
BinaryStreamUtils.writeInt64(out, (long) 1);
615+
BinaryStreamUtils.writeInt64(out, (long) 2);
616+
617+
BinaryStreamUtils.writeVarInt(out, 2);
618+
BinaryStreamUtils.writeFloat32(out, 1.5f);
619+
BinaryStreamUtils.writeFloat32(out, 2.5f);
620+
621+
BinaryStreamUtils.writeVarInt(out, 2);
622+
BinaryStreamUtils.writeFloat64(out, 1.5);
623+
BinaryStreamUtils.writeFloat64(out, 2.5);
624+
625+
BinaryStreamUtils.writeVarInt(out, 2);
626+
BinaryStreamUtils.writeBoolean(out, true);
627+
BinaryStreamUtils.writeBoolean(out, false);
628+
629+
InputStream in = new ByteArrayInputStream(out.toByteArray());
630+
QuerySettings querySettings = new QuerySettings().setUseTimeZone(TimeZone.getTimeZone("UTC").toZoneId().getId());
631+
RowBinaryWithNamesAndTypesFormatReader reader =
632+
new RowBinaryWithNamesAndTypesFormatReader(in, querySettings, new BinaryStreamReader.CachingByteBufferAllocator());
633+
634+
reader.next();
635+
636+
// Test all array methods with index parameters
637+
Assert.assertEquals(reader.getByteArray(1), new byte[] {(byte) 1, (byte) 2});
638+
Assert.assertEquals(reader.getStringArray(2), new String[] {"a", "b"});
639+
Assert.assertEquals(reader.getShortArray(3), new short[] {(short) 1, (short) 2});
640+
Assert.assertEquals(reader.getIntArray(4), new int[] {1, 2});
641+
Assert.assertEquals(reader.getLongArray(5), new long[] {1L, 2L});
642+
Assert.assertEquals(reader.getFloatArray(6), new float[] {1.5f, 2.5f});
643+
Assert.assertEquals(reader.getDoubleArray(7), new double[] {1.5, 2.5}, 0.001);
644+
Assert.assertEquals(reader.getBooleanArray(8), new boolean[] {true, false});
645+
}
646+
647+
@Test
648+
public void testGetObjectArrayByIndex() throws Exception {
649+
ByteArrayOutputStream out = new ByteArrayOutputStream();
650+
651+
String[] names = new String[]{"uint64_arr", "enum_arr", "dt_arr", "str_arr"};
652+
String[] types = new String[]{
653+
"Array(UInt64)",
654+
"Array(Enum8('abc' = 1, 'cde' = 2))",
655+
"Array(DateTime('UTC'))",
656+
"Array(String)"
657+
};
658+
659+
BinaryStreamUtils.writeVarInt(out, names.length);
660+
for (String name : names) {
661+
BinaryStreamUtils.writeString(out, name);
662+
}
663+
for (String type : types) {
664+
BinaryStreamUtils.writeString(out, type);
665+
}
666+
667+
// Array(UInt64): [100, 200]
668+
BinaryStreamUtils.writeVarInt(out, 2);
669+
BinaryStreamUtils.writeUnsignedInt64(out, BigInteger.valueOf(100));
670+
BinaryStreamUtils.writeUnsignedInt64(out, BigInteger.valueOf(200));
671+
672+
// Array(Enum8('abc' = 1, 'cde' = 2)): [1, 2]
673+
BinaryStreamUtils.writeVarInt(out, 2);
674+
BinaryStreamUtils.writeEnum8(out, (byte) 1);
675+
BinaryStreamUtils.writeEnum8(out, (byte) 2);
676+
677+
// Array(DateTime('UTC')): two timestamps
678+
LocalDateTime dt1 = LocalDateTime.of(2030, 10, 9, 8, 7, 6);
679+
LocalDateTime dt2 = LocalDateTime.of(2031, 10, 9, 8, 7, 6);
680+
BinaryStreamUtils.writeVarInt(out, 2);
681+
BinaryStreamUtils.writeDateTime32(out, dt1, TimeZone.getTimeZone("UTC"));
682+
BinaryStreamUtils.writeDateTime32(out, dt2, TimeZone.getTimeZone("UTC"));
683+
684+
// Array(String): ["hello", "world"]
685+
BinaryStreamUtils.writeVarInt(out, 2);
686+
BinaryStreamUtils.writeString(out, "hello");
687+
BinaryStreamUtils.writeString(out, "world");
688+
689+
InputStream in = new ByteArrayInputStream(out.toByteArray());
690+
QuerySettings querySettings = new QuerySettings().setUseTimeZone("UTC");
691+
RowBinaryWithNamesAndTypesFormatReader reader =
692+
new RowBinaryWithNamesAndTypesFormatReader(in, querySettings, new BinaryStreamReader.CachingByteBufferAllocator());
693+
reader.next();
694+
695+
// Test getObjectArray with index parameters
696+
Object[] uint64Result = reader.getObjectArray(1);
697+
Assert.assertNotNull(uint64Result);
698+
Assert.assertEquals(uint64Result.length, 2);
699+
Assert.assertEquals(uint64Result[0], BigInteger.valueOf(100));
700+
Assert.assertEquals(uint64Result[1], BigInteger.valueOf(200));
701+
702+
Object[] enumResult = reader.getObjectArray(2);
703+
Assert.assertNotNull(enumResult);
704+
Assert.assertEquals(enumResult.length, 2);
705+
Assert.assertTrue(enumResult[0] instanceof BinaryStreamReader.EnumValue);
706+
Assert.assertEquals(enumResult[0].toString(), "abc");
707+
Assert.assertEquals(enumResult[1].toString(), "cde");
708+
709+
Object[] dtResult = reader.getObjectArray(3);
710+
Assert.assertNotNull(dtResult);
711+
Assert.assertEquals(dtResult.length, 2);
712+
Assert.assertTrue(dtResult[0] instanceof ZonedDateTime);
713+
ZonedDateTime zdt1 = (ZonedDateTime) dtResult[0];
714+
ZonedDateTime zdt2 = (ZonedDateTime) dtResult[1];
715+
Assert.assertEquals(zdt1.toLocalDateTime(), dt1);
716+
Assert.assertEquals(zdt2.toLocalDateTime(), dt2);
717+
718+
Object[] strResult = reader.getObjectArray(4);
719+
Assert.assertNotNull(strResult);
720+
Assert.assertEquals(strResult.length, 2);
721+
Assert.assertEquals(strResult[0], "hello");
722+
Assert.assertEquals(strResult[1], "world");
723+
}
724+
725+
@Test
726+
public void testGetListByIndex() throws Exception {
727+
ByteArrayOutputStream out = new ByteArrayOutputStream();
728+
729+
String[] names = new String[]{"int_list", "string_list", "nested_list"};
730+
String[] types = new String[]{"Array(Int32)", "Array(String)", "Array(Array(Int64))"};
731+
732+
BinaryStreamUtils.writeVarInt(out, names.length);
733+
for (String name : names) {
734+
BinaryStreamUtils.writeString(out, name);
735+
}
736+
for (String type : types) {
737+
BinaryStreamUtils.writeString(out, type);
738+
}
739+
740+
// Array(Int32): [10, 20, 30]
741+
BinaryStreamUtils.writeVarInt(out, 3);
742+
BinaryStreamUtils.writeInt32(out, 10);
743+
BinaryStreamUtils.writeInt32(out, 20);
744+
BinaryStreamUtils.writeInt32(out, 30);
745+
746+
// Array(String): ["a", "b", "c"]
747+
BinaryStreamUtils.writeVarInt(out, 3);
748+
BinaryStreamUtils.writeString(out, "a");
749+
BinaryStreamUtils.writeString(out, "b");
750+
BinaryStreamUtils.writeString(out, "c");
751+
752+
// Array(Array(Int64)): [[1, 2], [3, 4]]
753+
BinaryStreamUtils.writeVarInt(out, 2); // outer array length
754+
BinaryStreamUtils.writeVarInt(out, 2); // inner[0] length
755+
BinaryStreamUtils.writeInt64(out, 1L);
756+
BinaryStreamUtils.writeInt64(out, 2L);
757+
BinaryStreamUtils.writeVarInt(out, 2); // inner[1] length
758+
BinaryStreamUtils.writeInt64(out, 3L);
759+
BinaryStreamUtils.writeInt64(out, 4L);
760+
761+
InputStream in = new ByteArrayInputStream(out.toByteArray());
762+
QuerySettings querySettings = new QuerySettings().setUseTimeZone("UTC");
763+
RowBinaryWithNamesAndTypesFormatReader reader =
764+
new RowBinaryWithNamesAndTypesFormatReader(in, querySettings, new BinaryStreamReader.CachingByteBufferAllocator());
765+
reader.next();
766+
767+
// Test getList with index parameters
768+
List<Integer> intList = reader.getList(1);
769+
Assert.assertEquals(intList, Arrays.asList(10, 20, 30));
770+
771+
List<String> stringList = reader.getList(2);
772+
Assert.assertEquals(stringList, Arrays.asList("a", "b", "c"));
773+
774+
List<List<Long>> nestedList = reader.getList(3);
775+
Assert.assertEquals(nestedList.size(), 2);
776+
Assert.assertEquals(nestedList.get(0), Arrays.asList(1L, 2L));
777+
Assert.assertEquals(nestedList.get(1), Arrays.asList(3L, 4L));
778+
}
779+
780+
@Test
781+
public void testArrayMethodsErrorConditions() throws Exception {
782+
ByteArrayOutputStream out = new ByteArrayOutputStream();
783+
String[] names = new String[]{"int_col", "string_col"};
784+
String[] types = new String[]{"Int32", "String"};
785+
786+
BinaryStreamUtils.writeVarInt(out, names.length);
787+
for (String name : names) {
788+
BinaryStreamUtils.writeString(out, name);
789+
}
790+
for (String type : types) {
791+
BinaryStreamUtils.writeString(out, type);
792+
}
793+
794+
BinaryStreamUtils.writeInt32(out, 42);
795+
BinaryStreamUtils.writeString(out, "test");
796+
797+
InputStream in = new ByteArrayInputStream(out.toByteArray());
798+
QuerySettings querySettings = new QuerySettings().setUseTimeZone("UTC");
799+
RowBinaryWithNamesAndTypesFormatReader reader =
800+
new RowBinaryWithNamesAndTypesFormatReader(in, querySettings, new BinaryStreamReader.CachingByteBufferAllocator());
801+
reader.next();
802+
803+
// Test that calling array methods on non-array columns throws exceptions
804+
Assert.assertThrows(Exception.class, () -> reader.getByteArray(1));
805+
Assert.assertThrows(Exception.class, () -> reader.getStringArray(1));
806+
Assert.assertThrows(Exception.class, () -> reader.getShortArray(1));
807+
Assert.assertThrows(Exception.class, () -> reader.getIntArray(1));
808+
Assert.assertThrows(Exception.class, () -> reader.getLongArray(1));
809+
Assert.assertThrows(Exception.class, () -> reader.getFloatArray(1));
810+
Assert.assertThrows(Exception.class, () -> reader.getDoubleArray(1));
811+
Assert.assertThrows(Exception.class, () -> reader.getBooleanArray(1));
812+
Assert.assertThrows(Exception.class, () -> reader.getObjectArray(1));
813+
Assert.assertThrows(Exception.class, () -> reader.getList(1));
814+
}
815+
816+
@Test
817+
public void testEmptyArraysByIndex() throws Exception {
818+
ByteArrayOutputStream out = new ByteArrayOutputStream();
819+
String[] names = new String[]{"empty_int_arr", "empty_str_arr", "empty_obj_arr"};
820+
String[] types = new String[]{"Array(Int32)", "Array(String)", "Array(Array(Int64))"};
821+
822+
BinaryStreamUtils.writeVarInt(out, names.length);
823+
for (String name : names) {
824+
BinaryStreamUtils.writeString(out, name);
825+
}
826+
for (String type : types) {
827+
BinaryStreamUtils.writeString(out, type);
828+
}
829+
830+
// Empty arrays
831+
BinaryStreamUtils.writeVarInt(out, 0); // empty_int_arr
832+
BinaryStreamUtils.writeVarInt(out, 0); // empty_str_arr
833+
BinaryStreamUtils.writeVarInt(out, 0); // empty_obj_arr
834+
835+
InputStream in = new ByteArrayInputStream(out.toByteArray());
836+
QuerySettings querySettings = new QuerySettings().setUseTimeZone("UTC");
837+
RowBinaryWithNamesAndTypesFormatReader reader =
838+
new RowBinaryWithNamesAndTypesFormatReader(in, querySettings, new BinaryStreamReader.CachingByteBufferAllocator());
839+
reader.next();
840+
841+
// Test empty arrays
842+
Assert.assertEquals(reader.getIntArray(1).length, 0);
843+
Assert.assertEquals(reader.getStringArray(2).length, 0);
844+
Assert.assertEquals(reader.getObjectArray(3).length, 0);
845+
Assert.assertEquals(reader.getList(1).size(), 0);
846+
Assert.assertEquals(reader.getList(2).size(), 0);
847+
Assert.assertEquals(reader.getList(3).size(), 0);
848+
}
579849
}

0 commit comments

Comments
 (0)