From 07befae6215db56445b7cc352228fc61314af820 Mon Sep 17 00:00:00 2001 From: Zhichun Wu Date: Fri, 3 Feb 2023 22:11:02 +0800 Subject: [PATCH] fix incorrect nested array value --- CHANGELOG.md | 5 + .../data/ClickHouseArraySequence.java | 8 +- .../clickhouse/data/ClickHouseDataType.java | 5 +- .../data/ClickHouseDeserializer.java | 13 +++ .../format/ClickHouseRowBinaryProcessor.java | 62 +++++++----- .../value/array/ClickHouseLongArrayValue.java | 2 +- .../clickhouse/data/ClickHouseColumnTest.java | 15 +++ .../ClickHouseRowBinaryProcessorTest.java | 28 ++++++ .../ClickHouseTabSeparatedProcessorTest.java | 3 +- .../data/value/BaseDataProcessorTest.java | 99 ++++++++++++++++++- 10 files changed, 199 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d63ef9fc2..fc3d38800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.1 + +### Bug Fixes +* incorrect nested array value. [#1221](https://github.com/ClickHouse/clickhouse-java/issues/1221) + ## 0.4.0, 2023-01-19 ### Breaking changes * refactored `JdbcTypeMapping` to make it extensible. [#1075](https://github.com/ClickHouse/clickhouse-java/pull/1075) diff --git a/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseArraySequence.java b/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseArraySequence.java index 29cd4dd3e..b6b5d6e7c 100644 --- a/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseArraySequence.java +++ b/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseArraySequence.java @@ -5,7 +5,7 @@ */ public interface ClickHouseArraySequence extends ClickHouseValue { /** - * Allocate an array according to given length. Same as + * Allocates an array according to given length. Same as * {@code allocate(length, Object.class, 1)}. * * @param length length of the array @@ -16,7 +16,7 @@ default ClickHouseArraySequence allocate(int length) { } /** - * Allocate an array according to given arguments. Same as + * Allocates an array according to given arguments. Same as * {@code allocate(length, clazz, 1)}. * * @param length length of the array @@ -28,7 +28,9 @@ default ClickHouseArraySequence allocate(int length, Class clazz) { } /** - * Allocate an array according to given arguments. + * Allocates an array according to given arguments. Pay attention that this will + * will not create new array but reuse existing one, when {@code length} are + * {@code clazz} not changed. * * @param length length of the array * @param clazz optional value type, null means {@code Object.class} diff --git a/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseDataType.java b/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseDataType.java index dc43b0103..9731b7326 100644 --- a/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseDataType.java +++ b/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseDataType.java @@ -341,10 +341,9 @@ public static Class toWiderPrimitiveType(Class javaClass) { javaClass = boolean.class; } else if (Byte.class == javaClass || UnsignedByte.class == javaClass || byte.class == javaClass) { javaClass = short.class; - } else if (Integer.class == javaClass || UnsignedInteger.class == javaClass || int.class == javaClass) { + } else if (Integer.class == javaClass || UnsignedInteger.class == javaClass || int.class == javaClass + || Long.class == javaClass || UnsignedLong.class == javaClass) { javaClass = long.class; - } else if (Long.class == javaClass || long.class == javaClass) { - javaClass = UnsignedLong.class; } else if (Short.class == javaClass || UnsignedShort.class == javaClass || short.class == javaClass || Character.class == javaClass || char.class == javaClass) { javaClass = int.class; diff --git a/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseDeserializer.java b/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseDeserializer.java index 0482184df..111e9f4ae 100644 --- a/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseDeserializer.java +++ b/clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseDeserializer.java @@ -12,6 +12,19 @@ */ @FunctionalInterface public interface ClickHouseDeserializer { + static final class ResetValueDeserializer implements ClickHouseDeserializer { + private final ClickHouseDeserializer deserializer; + + public ResetValueDeserializer(ClickHouseDeserializer deserializer) { + this.deserializer = deserializer; + } + + @Override + public ClickHouseValue deserialize(ClickHouseValue ref, ClickHouseInputStream input) throws IOException { + return deserializer.deserialize(ref.resetToDefault(), input); + } + } + static class CompositeDeserializer implements ClickHouseDeserializer { protected final ClickHouseDeserializer[] deserializers; diff --git a/clickhouse-data/src/main/java/com/clickhouse/data/format/ClickHouseRowBinaryProcessor.java b/clickhouse-data/src/main/java/com/clickhouse/data/format/ClickHouseRowBinaryProcessor.java index 53eed4059..e4a3780eb 100644 --- a/clickhouse-data/src/main/java/com/clickhouse/data/format/ClickHouseRowBinaryProcessor.java +++ b/clickhouse-data/src/main/java/com/clickhouse/data/format/ClickHouseRowBinaryProcessor.java @@ -311,23 +311,25 @@ public ClickHouseRowBinaryProcessor(ClickHouseDataConfig config, ClickHouseInput protected ClickHouseDeserializer[] getArrayDeserializers(ClickHouseDataConfig config, List columns) { - List list = new ArrayList<>(columns.size()); + ClickHouseDeserializer[] array = new ClickHouseDeserializer[columns.size()]; ClickHouseDataConfig modifiedConfig = new UseObjectConfig(config); + int i = 0; for (ClickHouseColumn column : columns) { - list.add(getDeserializer(modifiedConfig, - ClickHouseColumn.of(column.getColumnName(), ClickHouseDataType.Array, false, column))); + array[i++] = getDeserializer(modifiedConfig, + ClickHouseColumn.of(column.getColumnName(), ClickHouseDataType.Array, false, column)); } - return list.toArray(new ClickHouseDeserializer[0]); + return array; } protected ClickHouseSerializer[] getArraySerializers(ClickHouseDataConfig config, List columns) { - List list = new ArrayList<>(columns.size()); + ClickHouseSerializer[] array = new ClickHouseSerializer[columns.size()]; ClickHouseDataConfig modifiedConfig = new UseObjectConfig(config); + int i = 0; for (ClickHouseColumn column : columns) { - list.add(getSerializer(modifiedConfig, - ClickHouseColumn.of(column.getColumnName(), ClickHouseDataType.Array, false, column))); + array[i++] = getSerializer(modifiedConfig, + ClickHouseColumn.of(column.getColumnName(), ClickHouseDataType.Array, false, column)); } - return list.toArray(new ClickHouseSerializer[0]); + return array; } @Override @@ -462,29 +464,35 @@ public ClickHouseDeserializer getDeserializer(ClickHouseDataConfig config, Click break; // nested case Array: { - ClickHouseColumn baseColumn = column.getArrayBaseColumn(); - Class javaClass = baseColumn.getObjectClassForArray(config); - if (column.getArrayNestedLevel() == 1 && !baseColumn.isNullable() && javaClass.isPrimitive()) { - int byteLength = baseColumn.getDataType().getByteLength(); - if (byteLength == Byte.BYTES) { // Bool, *Int8 - deserializer = BinaryDataProcessor::readByteArray; - } else if (byteLength == Short.BYTES) { // *Int16 - deserializer = BinaryDataProcessor::readShortArray; - } else if (int.class == javaClass) { // Int32 - deserializer = BinaryDataProcessor::readIntegerArray; - } else if (long.class == javaClass) { // UInt32, *Int64 - deserializer = byteLength == Long.BYTES ? BinaryDataProcessor::readLongArray - : BinaryDataProcessor::readIntegerArray; - } else if (float.class == javaClass) { // Float32 - deserializer = BinaryDataProcessor::readFloatArray; - } else if (double.class == javaClass) { // Float64 - deserializer = BinaryDataProcessor::readDoubleArray; + if (column.getArrayNestedLevel() == 1) { + ClickHouseColumn baseColumn = column.getArrayBaseColumn(); + Class javaClass = baseColumn.getObjectClassForArray(config); + if (!baseColumn.isNullable() && javaClass.isPrimitive()) { + int byteLength = baseColumn.getDataType().getByteLength(); + if (byteLength == Byte.BYTES) { // Bool, *Int8 + deserializer = BinaryDataProcessor::readByteArray; + } else if (byteLength == Short.BYTES) { // *Int16 + deserializer = BinaryDataProcessor::readShortArray; + } else if (int.class == javaClass) { // Int32 + deserializer = BinaryDataProcessor::readIntegerArray; + } else if (long.class == javaClass) { // UInt32, *Int64 + deserializer = byteLength == Long.BYTES ? BinaryDataProcessor::readLongArray + : BinaryDataProcessor::readIntegerArray; + } else if (float.class == javaClass) { // Float32 + deserializer = BinaryDataProcessor::readFloatArray; + } else if (double.class == javaClass) { // Float64 + deserializer = BinaryDataProcessor::readDoubleArray; + } else { + throw new IllegalArgumentException("Unsupported primitive type: " + javaClass); + } } else { - throw new IllegalArgumentException("Unsupported primitive type: " + javaClass); + deserializer = new BinaryDataProcessor.ArrayDeserializer(config, column, true, + getDeserializer(config, column.getNestedColumns().get(0))); } } else { deserializer = new BinaryDataProcessor.ArrayDeserializer(config, column, true, - getDeserializer(config, column.getNestedColumns().get(0))); + new ClickHouseDeserializer.ResetValueDeserializer( + getDeserializer(config, column.getNestedColumns().get(0)))); } break; } diff --git a/clickhouse-data/src/main/java/com/clickhouse/data/value/array/ClickHouseLongArrayValue.java b/clickhouse-data/src/main/java/com/clickhouse/data/value/array/ClickHouseLongArrayValue.java index 6b1c5193c..d476633d2 100644 --- a/clickhouse-data/src/main/java/com/clickhouse/data/value/array/ClickHouseLongArrayValue.java +++ b/clickhouse-data/src/main/java/com/clickhouse/data/value/array/ClickHouseLongArrayValue.java @@ -77,7 +77,7 @@ public UnsignedLongArrayValue update(String value) { long[] arr = new long[list.size()]; int index = 0; for (String v : list) { - arr[index++] = v == null ? 0 : Integer.parseUnsignedInt(v); + arr[index++] = v == null ? 0L : Long.parseUnsignedLong(v); } set(arr); } diff --git a/clickhouse-data/src/test/java/com/clickhouse/data/ClickHouseColumnTest.java b/clickhouse-data/src/test/java/com/clickhouse/data/ClickHouseColumnTest.java index 99d805665..5b0d61429 100644 --- a/clickhouse-data/src/test/java/com/clickhouse/data/ClickHouseColumnTest.java +++ b/clickhouse-data/src/test/java/com/clickhouse/data/ClickHouseColumnTest.java @@ -9,6 +9,11 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import com.clickhouse.data.value.ClickHouseArrayValue; +import com.clickhouse.data.value.ClickHouseLongValue; +import com.clickhouse.data.value.UnsignedLong; +import com.clickhouse.data.value.array.ClickHouseLongArrayValue; + public class ClickHouseColumnTest { @DataProvider(name = "enumTypesProvider") private Object[][] getEnumTypes() { @@ -257,6 +262,16 @@ public boolean isWidenUnsignedTypes() { Assert.assertEquals(v.update(new Long[] { 1L }).asObject(), new Long[] { 1L }); v = ClickHouseColumn.of("a", "Array(Array(UInt16))").newValue(config); Assert.assertEquals(v.asObject(), new int[0][]); + v = ClickHouseColumn.of("a", "Array(UInt64)").newValue(config); + Assert.assertEquals(v.asObject(), new UnsignedLong[0]); + Assert.assertEquals(((ClickHouseLongArrayValue) v).allocate(1) + .setValue(0, ClickHouseLongValue.of(1L)).asObject(), new long[] { 1L }); + Assert.assertEquals(((ClickHouseLongArrayValue) v).allocate(1) + .setValue(0, ClickHouseLongValue.ofUnsigned(1L)).asObject(), new long[] { 1L }); + v = ClickHouseColumn.of("a", "Array(Array(UInt64))").newValue(config); + Assert.assertEquals(v.asObject(), new long[0][]); + Assert.assertEquals(((ClickHouseArrayValue) v).allocate(1) + .setValue(0, ClickHouseLongArrayValue.of(new long[] { 1L })).asObject(), new long[][] { { 1L } }); v = ClickHouseColumn.of("a", "Array(Array(Array(UInt8)))").newValue(config); Assert.assertEquals(v.asObject(), new short[0][][]); v = ClickHouseColumn.of("a", "Array(Array(Array(Nullable(UInt8))))").newValue(config); diff --git a/clickhouse-data/src/test/java/com/clickhouse/data/format/ClickHouseRowBinaryProcessorTest.java b/clickhouse-data/src/test/java/com/clickhouse/data/format/ClickHouseRowBinaryProcessorTest.java index 29299be08..eca8b2aad 100644 --- a/clickhouse-data/src/test/java/com/clickhouse/data/format/ClickHouseRowBinaryProcessorTest.java +++ b/clickhouse-data/src/test/java/com/clickhouse/data/format/ClickHouseRowBinaryProcessorTest.java @@ -156,6 +156,34 @@ protected byte[] getRawData(String typeName, String key) { data = toBytes(4, 0, 1, 66, 2, 66, 67, 3, 66, 67, 68); } break; + case "Array(Array(Array(UInt8)))": + if ("[[3]],[[1,2],[2,1]],[[4,5],[5,4]]".equals(key)) { + data = toBytes(3, 1, 1, 3, 2, 2, 1, 2, 2, 2, 1, 2, 2, 4, 5, 2, 5, 4); + } + break; + case "Array(Array(UInt64))": + if ("[1,2,3],[3,2,1],[4,5]".equals(key)) { + data = toBytes(3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, + 0, 5, 0, 0, 0, 0, 0, 0, 0); + } + break; + case "Map(String,Array(UInt8))": + if ("[1,2,3],[3,2,1],[4,5]".equals(key)) { + data = toBytes(3, 1, 0x61, 3, 1, 2, 3, 1, 0x62, 3, 3, 2, 1, 1, 0x63, 2, 4, 5); + } + break; + case "Tuple(Array(UInt8),Array(UInt8),Array(UInt8))": + if ("[1,2,3],[3,2,1],[4,5]".equals(key)) { + data = toBytes(3, 1, 2, 3, 3, 3, 2, 1, 2, 4, 5); + } + break; + case "Array(Array(String))": + if ("[foo,bar],[qaz,qux]".equals(key)) { + data = toBytes(2, 2, 3, 0X66, 0X6F, 0X6F, 3, 0X62, 0X61, 0X72, 2, 3, 0X71, 0X61, 0X7A, 3, 0X71, + 0X75, 0X78); + } + break; case "Bool": case "Int8": case "UInt8": diff --git a/clickhouse-data/src/test/java/com/clickhouse/data/format/ClickHouseTabSeparatedProcessorTest.java b/clickhouse-data/src/test/java/com/clickhouse/data/format/ClickHouseTabSeparatedProcessorTest.java index dac888ecc..da910ae7f 100644 --- a/clickhouse-data/src/test/java/com/clickhouse/data/format/ClickHouseTabSeparatedProcessorTest.java +++ b/clickhouse-data/src/test/java/com/clickhouse/data/format/ClickHouseTabSeparatedProcessorTest.java @@ -150,8 +150,7 @@ protected byte[] getRawData(String typeName, String key) { @Override public void testDeserializeNestedTypes(ClickHouseDataConfig config, String typeName, String dataKey, - String valueClass, - Object arrVal, Object objVal, String strVal, String sqlExpr) throws IOException { + String valueClass, Object arrVal, Object objVal, String strVal, String sqlExpr) throws IOException { throw new SkipException("Skip as it's not fully implemented"); } diff --git a/clickhouse-data/src/test/java/com/clickhouse/data/value/BaseDataProcessorTest.java b/clickhouse-data/src/test/java/com/clickhouse/data/value/BaseDataProcessorTest.java index 707bf48e3..3426363db 100644 --- a/clickhouse-data/src/test/java/com/clickhouse/data/value/BaseDataProcessorTest.java +++ b/clickhouse-data/src/test/java/com/clickhouse/data/value/BaseDataProcessorTest.java @@ -5,6 +5,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.function.Consumer; import org.testng.Assert; @@ -350,6 +351,29 @@ public boolean isWidenUnsignedTypes() { "[null, , B, BC, BCD]", "[NULL,'','B','BC','BCD']" }, { defaultConf, "Array(String)", "4bcd", "ClickHouseArrayValue", new String[] { "", "B", "BC", "BCD" }, new String[] { "", "B", "BC", "BCD" }, "[, B, BC, BCD]", "['','B','BC','BCD']" }, + { defaultConf, "Array(Array(Array(UInt8)))", "[[3]],[[1,2],[2,1]],[[4,5],[5,4]]", + "ClickHouseArrayValue", + new Byte[][][] { { { 3 } }, { { 1, 2 }, { 2, 1 } }, { { 4, 5 }, { 5, 4 } } }, + new byte[][][] { { { 3 } }, { { 1, 2 }, { 2, 1 } }, { { 4, 5 }, { 5, 4 } } }, + "[[[3]], [[1, 2], [2, 1]], [[4, 5], [5, 4]]]", "[[[3]],[[1,2],[2,1]],[[4,5],[5,4]]]" }, + { defaultConf, "Array(Array(UInt64))", "[1,2,3],[3,2,1],[4,5]", "ClickHouseArrayValue", + new Long[][] { { 1L, 2L, 3L }, { 3L, 2L, 1L }, { 4L, 5L } }, + new long[][] { { 1L, 2L, 3L }, { 3L, 2L, 1L }, { 4L, 5L } }, "[[1, 2, 3], [3, 2, 1], [4, 5]]", + "[[1,2,3],[3,2,1],[4,5]]" }, + { defaultConf, "Map(String,Array(UInt8))", "[1,2,3],[3,2,1],[4,5]", "ClickHouseMapValue", + new Long[][] { { 1L, 2L, 3L }, { 3L, 2L, 1L }, { 4L, 5L } }, + new long[][] { { 1L, 2L, 3L }, { 3L, 2L, 1L }, { 4L, 5L } }, "[[1, 2, 3], [3, 2, 1], [4, 5]]", + "{'a' : [1,2,3],'b' : [3,2,1],'c' : [4,5]}" }, + { defaultConf, "Tuple(Array(UInt8),Array(UInt8),Array(UInt8))", "[1,2,3],[3,2,1],[4,5]", + "ClickHouseTupleValue", + new Object[] { new byte[] { 1, 2, 3 }, new byte[] { 3, 2, 1 }, new byte[] { 4, 5 } }, + Arrays.asList( + new Object[] { new byte[] { 1, 2, 3 }, new byte[] { 3, 2, 1 }, new byte[] { 4, 5 } }), + "[[1, 2, 3], [3, 2, 1], [4, 5]]", "([1,2,3],[3,2,1],[4,5])" }, + { defaultConf, "Array(Array(String))", "[foo,bar],[qaz,qux]", "ClickHouseArrayValue", + new String[][] { { "foo", "bar" }, { "qaz", "qux" } }, + new String[][] { { "foo", "bar" }, { "qaz", "qux" } }, "[[foo, bar], [qaz, qux]]", + "[['foo','bar'],['qaz','qux']]" }, { binStrConf, "Array(Nullable(String))", "4bcd", "ClickHouseArrayValue", new byte[][] { null, new byte[0], { 66 }, { 66, 67 }, { 66, 67, 68 } }, @@ -359,6 +383,13 @@ public boolean isWidenUnsignedTypes() { new byte[][] { new byte[0], { 66 }, { 66, 67 }, { 66, 67, 68 } }, new byte[][] { new byte[0], { 66 }, { 66, 67 }, { 66, 67, 68 } }, "[[], [66], [66, 67], [66, 67, 68]]", "[[],[66],[66,67],[66,67,68]]" }, + { binStrConf, "Array(Array(String))", "[foo,bar],[qaz,qux]", "ClickHouseArrayValue", + new byte[][][] { { { 0x66, 0x6F, 0x6F }, { 0x62, 0x61, 0x72 } }, + { { 0x71, 0x61, 0x7A }, { 0x71, 0x75, 0x78 } } }, + new byte[][][] { { { 0x66, 0x6F, 0x6F }, { 0x62, 0x61, 0x72 } }, + { { 0x71, 0x61, 0x7A }, { 0x71, 0x75, 0x78 } } }, + "[[[102, 111, 111], [98, 97, 114]], [[113, 97, 122], [113, 117, 120]]]", + "[[[102,111,111],[98,97,114]],[[113,97,122],[113,117,120]]]" }, { widenUtConf, "Array(Nullable(Bool))", "0,1", "ClickHouseArrayValue", new Boolean[] { null, false, true }, new Boolean[] { null, false, true }, "[null, false, true]", @@ -420,6 +451,16 @@ public boolean isWidenUnsignedTypes() { "[null, , B, BC, BCD]", "[NULL,'','B','BC','BCD']" }, { widenUtConf, "Array(String)", "4bcd", "ClickHouseArrayValue", new String[] { "", "B", "BC", "BCD" }, new String[] { "", "B", "BC", "BCD" }, "[, B, BC, BCD]", "['','B','BC','BCD']" }, + { widenUtConf, "Array(Array(Array(UInt8)))", "[[3]],[[1,2],[2,1]],[[4,5],[5,4]]", + "ClickHouseArrayValue", + new Short[][][] { { { 3 } }, { { 1, 2 }, { 2, 1 } }, { { 4, 5 }, { 5, 4 } } }, + new short[][][] { { { 3 } }, { { 1, 2 }, { 2, 1 } }, { { 4, 5 }, { 5, 4 } } }, + "[[[3]], [[1, 2], [2, 1]], [[4, 5], [5, 4]]]", "[[[3]],[[1,2],[2,1]],[[4,5],[5,4]]]" }, + // better to use UnsignedLong and fix the ArrayStoreException + { widenUtConf, "Array(Array(UInt64))", "[1,2,3],[3,2,1],[4,5]", "ClickHouseArrayValue", + new Long[][] { { 1L, 2L, 3L }, { 3L, 2L, 1L }, { 4L, 5L } }, + new long[][] { { 1L, 2L, 3L }, { 3L, 2L, 1L }, { 4L, 5L } }, + "[[1, 2, 3], [3, 2, 1], [4, 5]]", "[[1,2,3],[3,2,1],[4,5]]" }, { useObjsConf, "Array(Bool)", "0,1", "ClickHouseArrayValue", new Boolean[] { false, true }, new Boolean[] { false, true }, "[false, true]", "[false,true]" }, @@ -453,6 +494,31 @@ public boolean isWidenUnsignedTypes() { new Double[] { 0D, 1D, -1D }, "[0.0, 1.0, -1.0]", "[0.0,1.0,-1.0]" }, { useObjsConf, "Array(String)", "4bcd", "ClickHouseArrayValue", new String[] { "", "B", "BC", "BCD" }, new String[] { "", "B", "BC", "BCD" }, "[, B, BC, BCD]", "['','B','BC','BCD']" }, + { useObjsConf, "Array(Array(Array(UInt8)))", "[[3]],[[1,2],[2,1]],[[4,5],[5,4]]", + "ClickHouseArrayValue", + new UnsignedByte[][][] { { { UnsignedByte.valueOf((byte) 3) } }, + { { UnsignedByte.ONE, UnsignedByte.valueOf((byte) 2) }, + { UnsignedByte.valueOf((byte) 2), UnsignedByte.ONE } }, + { { UnsignedByte.valueOf((byte) 4), UnsignedByte.valueOf((byte) 5) }, + { UnsignedByte.valueOf((byte) 5), UnsignedByte.valueOf((byte) 4) } } }, + new UnsignedByte[][][] { { { UnsignedByte.valueOf((byte) 3) } }, + { { UnsignedByte.ONE, UnsignedByte.valueOf((byte) 2) }, + { UnsignedByte.valueOf((byte) 2), UnsignedByte.ONE } }, + { { UnsignedByte.valueOf((byte) 4), UnsignedByte.valueOf((byte) 5) }, + { UnsignedByte.valueOf((byte) 5), UnsignedByte.valueOf((byte) 4) } } }, + "[[[3]], [[1, 2], [2, 1]], [[4, 5], [5, 4]]]", "[[[3]],[[1,2],[2,1]],[[4,5],[5,4]]]" }, + { useObjsConf, "Array(Array(UInt64))", "[1,2,3],[3,2,1],[4,5]", "ClickHouseArrayValue", + new UnsignedLong[][] { { UnsignedLong.ONE, UnsignedLong.TWO, UnsignedLong.valueOf(3L) }, + { UnsignedLong.valueOf(3L), UnsignedLong.TWO, UnsignedLong.ONE }, + { UnsignedLong.valueOf(4L), UnsignedLong.valueOf(5L) } }, + new UnsignedLong[][] { { UnsignedLong.ONE, UnsignedLong.TWO, UnsignedLong.valueOf(3L) }, + { UnsignedLong.valueOf(3L), UnsignedLong.TWO, UnsignedLong.ONE }, + { UnsignedLong.valueOf(4L), UnsignedLong.valueOf(5L) } }, + "[[1, 2, 3], [3, 2, 1], [4, 5]]", "[[1,2,3],[3,2,1],[4,5]]" }, + { useObjsConf, "Array(Array(String))", "[foo,bar],[qaz,qux]", "ClickHouseArrayValue", + new String[][] { { "foo", "bar" }, { "qaz", "qux" } }, + new String[][] { { "foo", "bar" }, { "qaz", "qux" } }, "[[foo, bar], [qaz, qux]]", + "[['foo','bar'],['qaz','qux']]" }, { combinedConf, "Array(Bool)", "0,1", "ClickHouseArrayValue", new Boolean[] { false, true }, new Boolean[] { false, true }, "[false, true]", "[false,true]" }, @@ -480,6 +546,23 @@ public boolean isWidenUnsignedTypes() { new Double[] { 0D, 1D, -1D }, "[0.0, 1.0, -1.0]", "[0.0,1.0,-1.0]" }, { combinedConf, "Array(String)", "4bcd", "ClickHouseArrayValue", new String[] { "", "B", "BC", "BCD" }, new String[] { "", "B", "BC", "BCD" }, "[, B, BC, BCD]", "['','B','BC','BCD']" }, + { combinedConf, "Array(Array(Array(UInt8)))", "[[3]],[[1,2],[2,1]],[[4,5],[5,4]]", + "ClickHouseArrayValue", + new Short[][][] { { { 3 } }, { { 1, 2 }, { 2, 1 } }, { { 4, 5 }, { 5, 4 } } }, + new short[][][] { { { 3 } }, { { 1, 2 }, { 2, 1 } }, { { 4, 5 }, { 5, 4 } } }, + "[[[3]], [[1, 2], [2, 1]], [[4, 5], [5, 4]]]", "[[[3]],[[1,2],[2,1]],[[4,5],[5,4]]]" }, + { combinedConf, "Array(Array(UInt64))", "[1,2,3],[3,2,1],[4,5]", "ClickHouseArrayValue", + new UnsignedLong[][] { { UnsignedLong.ONE, UnsignedLong.TWO, UnsignedLong.valueOf(3L) }, + { UnsignedLong.valueOf(3L), UnsignedLong.TWO, UnsignedLong.ONE }, + { UnsignedLong.valueOf(4L), UnsignedLong.valueOf(5L) } }, + new UnsignedLong[][] { { UnsignedLong.ONE, UnsignedLong.TWO, UnsignedLong.valueOf(3L) }, + { UnsignedLong.valueOf(3L), UnsignedLong.TWO, UnsignedLong.ONE }, + { UnsignedLong.valueOf(4L), UnsignedLong.valueOf(5L) } }, + "[[1, 2, 3], [3, 2, 1], [4, 5]]", "[[1,2,3],[3,2,1],[4,5]]" }, + { combinedConf, "Array(Array(String))", "[foo,bar],[qaz,qux]", "ClickHouseArrayValue", + new String[][] { { "foo", "bar" }, { "qaz", "qux" } }, + new String[][] { { "foo", "bar" }, { "qaz", "qux" } }, "[[foo, bar], [qaz, qux]]", + "[['foo','bar'],['qaz','qux']]" }, }; } @@ -1000,13 +1083,19 @@ public void testDeserializeNestedTypes(ClickHouseDataConfig config, String typeN String valueClass, Object arrVal, Object objVal, String strVal, String sqlExpr) throws IOException { try (ClickHouseInputStream in = getInputData(typeName, dataKey)) { ClickHouseColumn column = ClickHouseColumn.of("a", typeName); - Assert.assertTrue(column.isArray()); - ClickHouseValue value = deserialize(null, config, column, in); + Assert.assertEquals(value.getClass().getSimpleName(), valueClass); - Assert.assertEquals(value.asArray(), arrVal); - Assert.assertEquals(value.asObject(), objVal); - Assert.assertEquals(value.asString(), strVal); + if (column.isArray()) { + Assert.assertEquals(value.asArray(), arrVal); + Assert.assertEquals(value.asObject(), objVal); + Assert.assertEquals(value.asString(), strVal); + } else if (column.isMap()) { + // Assert.assertEquals(value.asObject(), objVal); + } else if (column.isTuple()) { + Assert.assertEquals(value.asArray(), arrVal); + Assert.assertEquals(value.asString(), strVal); + } Assert.assertEquals(value.toSqlExpression(), sqlExpr); } }