Skip to content

Commit 5cb801c

Browse files
committed
implemented convertion to smaller number format if possible
1 parent 909e777 commit 5cb801c

File tree

3 files changed

+129
-31
lines changed

3 files changed

+129
-31
lines changed

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

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -234,34 +234,28 @@ protected void setSchema(TableSchema schema) {
234234
Map<NumberType, Function<Number, ?>> converters = new HashMap<>();
235235
switch (column.getDataType()) {
236236
case Int8:
237-
converters.put(NumberType.Byte, SerializerUtils::convertToByte);
238237
case Int16:
239238
case UInt8:
240-
converters.put(NumberType.Short, SerializerUtils::convertToShort);
241239
case Int32:
242240
case UInt16:
243-
converters.put(NumberType.Int, SerializerUtils::convertToInt);
244241
case Int64:
245242
case UInt32:
246-
converters.put(NumberType.Long, SerializerUtils::convertToLong);
247-
converters.put(NumberType.Float, SerializerUtils::convertToFloat);
248-
converters.put(NumberType.Double, SerializerUtils::convertToDouble);
249243
case Int128:
250244
case UInt64:
251245
case Int256:
252246
case UInt128:
253247
case UInt256:
254-
converters.put(NumberType.BigInteger, SerializerUtils::convertToBigInteger);
255-
converters.put(NumberType.BigDecimal, SerializerUtils::convertToBigDecimal);
256-
converters.put(NumberType.Boolean, SerializerUtils::convertToBoolean);
257-
break;
258248
case Float32:
259-
converters.put(NumberType.Float, SerializerUtils::convertToFloat);
260249
case Float64:
261-
converters.put(NumberType.Double, SerializerUtils::convertToDouble);
262-
converters.put(NumberType.Boolean, SerializerUtils::convertToBoolean);
263-
break;
264250
case Bool:
251+
converters.put(NumberType.Byte, SerializerUtils.NumberConverter::toByte);
252+
converters.put(NumberType.Short, SerializerUtils.NumberConverter::toShort);
253+
converters.put(NumberType.Int, SerializerUtils.NumberConverter::toInt);
254+
converters.put(NumberType.Long, SerializerUtils.NumberConverter::toLong);
255+
converters.put(NumberType.BigInteger, SerializerUtils.NumberConverter::toBigInteger);
256+
converters.put(NumberType.BigDecimal, SerializerUtils.NumberConverter::toBigDecimal);
257+
converters.put(NumberType.Float, SerializerUtils.NumberConverter::toFloat);
258+
converters.put(NumberType.Double, SerializerUtils.NumberConverter::toDouble);
265259
converters.put(NumberType.Boolean, SerializerUtils::convertToBoolean);
266260
break;
267261
}

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

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -541,27 +541,75 @@ public Class<?> defineClass(String name, byte[] code) throws ClassNotFoundExcept
541541
}
542542
}
543543

544-
public static byte convertToByte(Number value) {
545-
return value.byteValue();
546-
}
544+
public static class NumberConverter {
547545

548-
public static short convertToShort(Number value) {
549-
return value.shortValue();
550-
}
546+
public static byte toByte(Number value) {
547+
if (value.byteValue() == value.shortValue()) {
548+
return value.byteValue();
549+
} else {
550+
throw new ArithmeticException("integer overflow: " + value + " cannot be presented as byte");
551+
}
552+
}
551553

552-
public static int convertToInt(Number value) {
553-
return value.intValue();
554-
}
554+
public static short toShort(Number value) {
555+
if (value.shortValue() == value.intValue()) {
556+
return value.shortValue();
557+
} else {
558+
throw new ArithmeticException("integer overflow: " + value + " cannot be presented as short");
559+
}
560+
}
555561

556-
public static long convertToLong(Number value) {
557-
return value.longValue();
558-
}
562+
public static int toInt(Number value) {
563+
if (value.intValue() == value.longValue()) {
564+
return value.intValue();
565+
} else {
566+
throw new ArithmeticException("integer overflow: " + value + " cannot be presented as int");
567+
}
568+
}
559569

560-
public static float convertToFloat(Number value) {
561-
return value.floatValue();
562-
}
570+
public static long toLong(Number value) {
571+
if (value.longValue() == value.doubleValue()) {
572+
return value.longValue();
573+
} else {
574+
throw new ArithmeticException("integer overflow: " + value + " cannot be presented as long");
575+
}
576+
}
563577

564-
public static double convertToDouble(Number value) {
565-
return value.doubleValue();
578+
579+
public static BigInteger toBigInteger(Number value) {
580+
return value instanceof BigInteger ? (BigInteger) value : BigInteger.valueOf(value.longValue());
581+
}
582+
583+
public static BigInteger toBigInteger(String value) {
584+
return new BigInteger(value);
585+
}
586+
587+
public static float toFloat(Number value) {
588+
if (value instanceof Float) {
589+
return (Float) value;
590+
} else if (value.floatValue() == value.doubleValue()) {
591+
return value.floatValue();
592+
} else {
593+
throw new ArithmeticException("float overflow: " + value + " cannot be presented as float");
594+
}
595+
}
596+
597+
public static double toDouble(Number value) {
598+
if (value instanceof Double) {
599+
return (Double) value;
600+
} else if (value.doubleValue() == value.floatValue()) {
601+
return value.doubleValue();
602+
} else {
603+
throw new ArithmeticException("double overflow: " + value + " cannot be presented as double");
604+
}
605+
}
606+
607+
public static BigDecimal toBigDecimal(Number value) {
608+
return value instanceof BigDecimal ? (BigDecimal) value : BigDecimal.valueOf(value.doubleValue());
609+
}
610+
611+
public static BigDecimal toBigDecimal(String value) {
612+
return new BigDecimal(value);
613+
}
566614
}
567615
}

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,60 @@ public void testReadingNumbers() throws IOException {
8686
}
8787
}
8888
}
89+
90+
@Test
91+
public void testReadingNumbersWithOverflow() throws IOException {
92+
ByteArrayOutputStream out = new ByteArrayOutputStream();
93+
94+
String[] names = new String[]{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
95+
String[] types = new String[]{"Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Float32", "Float64"};
96+
97+
BinaryStreamUtils.writeVarInt(out, names.length);
98+
for (String name : names) {
99+
BinaryStreamUtils.writeString(out, name);
100+
}
101+
for (String type : types) {
102+
BinaryStreamUtils.writeString(out, type);
103+
}
104+
105+
BinaryStreamUtils.writeInt8(out, 127); // Max value for Int8
106+
BinaryStreamUtils.writeInt16(out, 2000);
107+
BinaryStreamUtils.writeInt32(out, 300000);
108+
BinaryStreamUtils.writeInt64(out, 4000000000L);
109+
BinaryStreamUtils.writeUnsignedInt8(out, 255);
110+
BinaryStreamUtils.writeUnsignedInt16(out, 60000);
111+
BinaryStreamUtils.writeUnsignedInt32(out, 4000000000L);
112+
BinaryStreamUtils.writeUnsignedInt64(out, new BigInteger("18000044073709551615"));
113+
BinaryStreamUtils.writeFloat32(out, 9.0f);
114+
BinaryStreamUtils.writeFloat64(out, 10.0);
115+
116+
117+
InputStream in = new ByteArrayInputStream(out.toByteArray());
118+
QuerySettings querySettings = new QuerySettings().setUseTimeZone(TimeZone.getTimeZone("UTC").toZoneId().getId());
119+
RowBinaryWithNamesAndTypesFormatReader reader =
120+
new RowBinaryWithNamesAndTypesFormatReader(in, querySettings, new BinaryStreamReader.CachingByteBufferAllocator());
121+
122+
reader.next();
123+
124+
for (int i = 0; i < names.length; i++) {
125+
String name = names[i];
126+
String type = types[i];
127+
128+
if (type.equalsIgnoreCase("int16")) {
129+
Assert.expectThrows(ArithmeticException.class, () -> reader.getByte(name)) ;
130+
} else if (type.equalsIgnoreCase("int32")) {
131+
Assert.expectThrows(ArithmeticException.class, () -> reader.getShort(name)) ;
132+
} else if (type.equalsIgnoreCase("int64")) {
133+
Assert.expectThrows(ArithmeticException.class, () -> reader.getInteger(name)) ;
134+
} else if (type.equalsIgnoreCase("uint8")) {
135+
Assert.expectThrows(ArithmeticException.class, () -> reader.getByte(name)) ;
136+
} else if (type.equalsIgnoreCase("uint16")) {
137+
Assert.expectThrows(ArithmeticException.class, () -> reader.getShort(name)) ;
138+
} else if (type.equalsIgnoreCase("uint32")) {
139+
Assert.expectThrows(ArithmeticException.class, () -> reader.getInteger(name)) ;
140+
} else if (type.equalsIgnoreCase("uint64")) {
141+
Assert.expectThrows(ArithmeticException.class, () -> reader.getLong(name)) ;
142+
}
143+
}
144+
}
89145
}

0 commit comments

Comments
 (0)