From 6bc12ab668f04854e81b510a60fed25cdf7363bb Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Wed, 6 Nov 2024 13:29:26 -0800 Subject: [PATCH] implemented convertion from boolean, to boolean. Added tests --- .../internal/AbstractBinaryFormatReader.java | 28 ++-- .../internal/SerializerUtils.java | 141 +++++++++++++----- .../ClickHouseBinaryFormatReaderTest.java | 48 ++++++ .../clickhouse/client/query/QueryTests.java | 2 - 4 files changed, 170 insertions(+), 49 deletions(-) diff --git a/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/AbstractBinaryFormatReader.java b/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/AbstractBinaryFormatReader.java index ce1f1d87f..e226c6fae 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/AbstractBinaryFormatReader.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/AbstractBinaryFormatReader.java @@ -231,7 +231,7 @@ protected void setSchema(TableSchema schema) { for (int i = 0; i < columns.length; i++) { ClickHouseColumn column = columns[i]; - Map> converters = new HashMap<>(); + Map> converters = new HashMap<>(); switch (column.getDataType()) { case Int8: case Int16: @@ -300,9 +300,9 @@ public String getString(int index) { private T readNumberValue(String colName, NumberType targetType) { int colIndex = schema.nameToIndex(colName); - Function converter = (Function) convertions[colIndex].get(targetType); + Function converter = (Function) convertions[colIndex].get(targetType); if (converter != null) { - Number value = readValue(colName); + Object value = readValue(colName); if (value == null) { throw new NullValueException("Column " + colName + " has null value and it cannot be cast to " + targetType.getTypeName()); @@ -466,17 +466,25 @@ public ClickHouseGeoMultiPolygonValue getGeoMultiPolygon(String colName) { @Override public List getList(String colName) { - BinaryStreamReader.ArrayValue array = readValue(colName); - return array.asList(); + try { + BinaryStreamReader.ArrayValue array = readValue(colName); + return array.asList(); + } catch (ClassCastException e) { + throw new ClientException("Column is not of array type", e); + } } private T getPrimitiveArray(String colName) { - BinaryStreamReader.ArrayValue array = readValue(colName); - if (array.itemType.isPrimitive()) { - return (T) array.array; - } else { - throw new ClientException("Array is not of primitive type"); + try { + BinaryStreamReader.ArrayValue array = readValue(colName); + if (array.itemType.isPrimitive()) { + return (T) array.array; + } else { + throw new ClientException("Array is not of primitive type"); + } + } catch (ClassCastException e) { + throw new ClientException("Column is not of array type", e); } } diff --git a/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/SerializerUtils.java b/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/SerializerUtils.java index 37a067b99..7b7aba03f 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/SerializerUtils.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/SerializerUtils.java @@ -585,73 +585,140 @@ public static void writeBoolean(OutputStream output, boolean value) throws IOExc public static class NumberConverter { - public static byte toByte(Number value) { - if (value.byteValue() == value.shortValue()) { - return value.byteValue(); + public static byte toByte(Object value) { + if (value instanceof Number) { + Number number = (Number) value; + if (number.byteValue() == number.shortValue()) { + return number.byteValue(); + } else { + throw new ArithmeticException("integer overflow: " + value + " cannot be presented as byte"); + } + } else if (value instanceof Boolean) { + return (byte) ((Boolean) value ? 1 : 0); + } else if (value instanceof String) { + return Byte.parseByte(value.toString()); } else { - throw new ArithmeticException("integer overflow: " + value + " cannot be presented as byte"); + throw new IllegalArgumentException("Cannot convert " + value + " to byte value"); } } - public static short toShort(Number value) { - if (value.shortValue() == value.intValue()) { - return value.shortValue(); + public static short toShort(Object value) { + if (value instanceof Number) { + Number number = (Number) value; + if (number.shortValue() == number.intValue()) { + return number.shortValue(); + } else { + throw new ArithmeticException("integer overflow: " + value + " cannot be presented as short"); + } + } else if (value instanceof Boolean) { + return (short) ((Boolean) value ? 1 : 0); + } else if ( value instanceof String) { + return Short.parseShort(value.toString()); } else { - throw new ArithmeticException("integer overflow: " + value + " cannot be presented as short"); + throw new IllegalArgumentException("Cannot convert " + value + " to short value"); } } - public static int toInt(Number value) { - if (value.intValue() == value.longValue()) { - return value.intValue(); + public static int toInt(Object value) { + if (value instanceof Number) { + Number number = (Number) value; + if (number.intValue() == number.longValue()) { + return number.intValue(); + } else { + throw new ArithmeticException("integer overflow: " + value + " cannot be presented as int"); + } + } else if (value instanceof Boolean) { + return (Boolean) value ? 1 : 0; + } else if (value instanceof String) { + return Integer.parseInt((String) value); } else { - throw new ArithmeticException("integer overflow: " + value + " cannot be presented as int"); + throw new IllegalArgumentException("Cannot convert " + value + " to int value"); } } - public static long toLong(Number value) { - if (value.longValue() == value.doubleValue()) { - return value.longValue(); + public static long toLong(Object value) { + if (value instanceof Number) { + Number number = (Number) value; + if (number.longValue() == number.doubleValue()) { + return number.longValue(); + } else { + throw new ArithmeticException("integer overflow: " + value + " cannot be presented as long"); + } + } else if (value instanceof Boolean) { + return (Boolean) value ? 1 : 0; + } else if (value instanceof String) { + return Long.parseLong((String) value); } else { - throw new ArithmeticException("integer overflow: " + value + " cannot be presented as long"); + throw new IllegalArgumentException("Cannot convert " + value + " to long value"); } } - - public static BigInteger toBigInteger(Number value) { - return value instanceof BigInteger ? (BigInteger) value : BigInteger.valueOf(value.longValue()); - } - - public static BigInteger toBigInteger(String value) { - return new BigInteger(value); + public static BigInteger toBigInteger(Object value) { + if (value instanceof BigInteger) { + return (BigInteger) value; + } else if (value instanceof Number) { + return BigInteger.valueOf(((Number) value).longValue()); + } else if (value instanceof Boolean) { + return (Boolean) value ? BigInteger.ONE : BigInteger.ZERO; + } else if (value instanceof String) { + return new BigInteger((String) value); + } else { + throw new IllegalArgumentException("Cannot convert " + value + " to BigInteger value"); + } } - public static float toFloat(Number value) { + public static float toFloat(Object value) { if (value instanceof Float) { return (Float) value; - } else if (value.floatValue() == value.doubleValue()) { - return value.floatValue(); + } else if (value instanceof Number) { + Number number = (Number) value; + if (number.floatValue() == number.doubleValue()) { + return number.floatValue(); + } else { + throw new ArithmeticException("float overflow: " + value + " cannot be presented as float"); + } + } else if (value instanceof Boolean) { + return (Boolean) value ? 1.0f : 0.0f; + } else if (value instanceof String ) { + return Float.parseFloat((String) value); } else { - throw new ArithmeticException("float overflow: " + value + " cannot be presented as float"); + throw new IllegalArgumentException("Cannot convert " + value + " to float value"); } } - public static double toDouble(Number value) { + public static double toDouble(Object value) { if (value instanceof Double) { return (Double) value; - } else if (value.doubleValue() == value.floatValue()) { - return value.doubleValue(); + } else if (value instanceof Number) { + Number number = (Number) value; + if (number.doubleValue() == number.floatValue()) { + return number.doubleValue(); + } else { + throw new ArithmeticException("double overflow: " + value + " cannot be presented as double"); + } + } else if (value instanceof Boolean) { + return (Boolean) value ? 1.0 : 0.0; + } else if (value instanceof String) { + return Double.parseDouble((String) value); } else { - throw new ArithmeticException("double overflow: " + value + " cannot be presented as double"); + throw new IllegalArgumentException("Cannot convert " + value + " to double value"); } } - public static BigDecimal toBigDecimal(Number value) { - return value instanceof BigDecimal ? (BigDecimal) value : BigDecimal.valueOf(value.doubleValue()); - } - - public static BigDecimal toBigDecimal(String value) { - return new BigDecimal(value); + public static BigDecimal toBigDecimal(Object value) { + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } else if (value instanceof BigInteger) { + return new BigDecimal((BigInteger) value); + } else if (value instanceof Number) { + return BigDecimal.valueOf(((Number) value).doubleValue()); + } else if (value instanceof String) { + return new BigDecimal((String) value); + } else if (value instanceof Boolean) { + return (Boolean) value ? BigDecimal.ONE : BigDecimal.ZERO; + } else { + throw new IllegalArgumentException("Cannot convert " + value + " to BigDecimal value"); + } } } } diff --git a/client-v2/src/test/java/com/clickhouse/client/api/data_formats/ClickHouseBinaryFormatReaderTest.java b/client-v2/src/test/java/com/clickhouse/client/api/data_formats/ClickHouseBinaryFormatReaderTest.java index 280fa7070..23998c09b 100644 --- a/client-v2/src/test/java/com/clickhouse/client/api/data_formats/ClickHouseBinaryFormatReaderTest.java +++ b/client-v2/src/test/java/com/clickhouse/client/api/data_formats/ClickHouseBinaryFormatReaderTest.java @@ -140,4 +140,52 @@ public void testReadingNumbersWithOverflow() throws IOException { Arrays.stream("h,i,j,k,l,n,p,q,r".split(",")).forEach(floatConsumer); Arrays.stream("h,i,j,k,l,p,q,r".split(",")).forEach(doubleConsumer); } + + @Test + public void testReadingAsBoolean() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + String[] names = new String[]{ "a", "b"}; + String[] types = new String[]{"Bool", "Bool"}; + + BinaryStreamUtils.writeVarInt(out, names.length); + for (String name : names) { + BinaryStreamUtils.writeString(out, name); + } + for (String type : types) { + BinaryStreamUtils.writeString(out, type); + } + + + BinaryStreamUtils.writeBoolean(out, true); + BinaryStreamUtils.writeBoolean(out, false); + + InputStream in = new ByteArrayInputStream(out.toByteArray()); + QuerySettings querySettings = new QuerySettings().setUseTimeZone(TimeZone.getTimeZone("UTC").toZoneId().getId()); + RowBinaryWithNamesAndTypesFormatReader reader = + new RowBinaryWithNamesAndTypesFormatReader(in, querySettings, new BinaryStreamReader.CachingByteBufferAllocator()); + + reader.next(); + + Assert.assertEquals(reader.getBoolean("a"), Boolean.TRUE); + Assert.assertEquals(reader.getBoolean("b"), Boolean.FALSE); + Assert.assertEquals(reader.getByte("a"), (byte) 1); + Assert.assertEquals(reader.getByte("b"), (byte) 0); + Assert.assertEquals(reader.getShort("a"), (short) 1); + Assert.assertEquals(reader.getShort("b"), (short) 0); + Assert.assertEquals(reader.getInteger("a"), 1); + Assert.assertEquals(reader.getInteger("b"), 0); + Assert.assertEquals(reader.getLong("a"), 1); + Assert.assertEquals(reader.getLong("b"), 0); + Assert.assertEquals(reader.getFloat("a"), 1.0f); + Assert.assertEquals(reader.getFloat("b"), 0.0f); + Assert.assertEquals(reader.getDouble("a"), 1.0d); + Assert.assertEquals(reader.getDouble("b"), 0.0d); + Assert.assertEquals(reader.getBigInteger("a"), BigInteger.ONE); + Assert.assertEquals(reader.getBigInteger("b"), BigInteger.ZERO); + Assert.assertEquals(reader.getBigDecimal("a"), BigDecimal.ONE); + Assert.assertEquals(reader.getBigDecimal("b"), BigDecimal.ZERO); + Assert.assertEquals(reader.getString("a"), "true"); + Assert.assertEquals(reader.getString("b"), "false"); + } } \ No newline at end of file diff --git a/client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java b/client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java index 3bb4b705c..994a71768 100644 --- a/client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java +++ b/client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java @@ -189,12 +189,10 @@ public void testBigUnsignedInt() throws Exception { final BigInteger expected256 = BigInteger.valueOf(2).pow(256).subtract(BigInteger.ONE).subtract(BigInteger.ONE); String sqlQuery = "SELECT toUInt128('" + expected128 + "') as i128, toUInt256('" + expected256 + "') as i256"; - System.out.println(sqlQuery); Records records = client.queryRecords(sqlQuery).get(3, TimeUnit.SECONDS); GenericRecord firstRecord = records.iterator().next(); - System.out.println(firstRecord.getBigInteger("i128")); Assert.assertEquals(firstRecord.getBigInteger("i128"), expected128); Assert.assertEquals(firstRecord.getBigInteger("i256"), expected256); }