Skip to content

Commit

Permalink
Merge pull request #1913 from ClickHouse/v2_fix_reading_boolean
Browse files Browse the repository at this point in the history
[client-v2] Implements conversion from boolean, to boolean. Added tests
  • Loading branch information
chernser authored Nov 6, 2024
2 parents eb0bfaf + 6bc12ab commit b08a38f
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ protected void setSchema(TableSchema schema) {
for (int i = 0; i < columns.length; i++) {
ClickHouseColumn column = columns[i];

Map<NumberType, Function<Number, ?>> converters = new HashMap<>();
Map<NumberType, Function<Object, ?>> converters = new HashMap<>();
switch (column.getDataType()) {
case Int8:
case Int16:
Expand Down Expand Up @@ -300,9 +300,9 @@ public String getString(int index) {

private <T> T readNumberValue(String colName, NumberType targetType) {
int colIndex = schema.nameToIndex(colName);
Function<Number, Number> converter = (Function<Number, Number>) convertions[colIndex].get(targetType);
Function<Object, Object> converter = (Function<Object, Object>) 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());
Expand Down Expand Up @@ -466,17 +466,25 @@ public ClickHouseGeoMultiPolygonValue getGeoMultiPolygon(String colName) {

@Override
public <T> List<T> 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> 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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit b08a38f

Please sign in to comment.