Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,12 @@ public static BigDecimal toBigDecimal(Object value) {
return (BigDecimal) value;
} else if (value instanceof BigInteger) {
return new BigDecimal((BigInteger) value);
} else if (value instanceof Number) {
} else if (value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long) {
return BigDecimal.valueOf(((Number) value).longValue());
} else if (value instanceof Float || value instanceof Double) {
return BigDecimal.valueOf(((Number) value).doubleValue());
} else if (value instanceof Number) {
return new BigDecimal(value.toString());
} else if (value instanceof String) {
return new BigDecimal((String) value);
} else if (value instanceof Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.RETURN;

Comment thread
chernser marked this conversation as resolved.
@SuppressWarnings("deprecation")
public class SerializerUtils {

public static void serializeData(OutputStream stream, Object value, ClickHouseColumn column) throws IOException {
Expand Down Expand Up @@ -505,10 +506,10 @@ private static void serializePrimitiveData(OutputStream stream, Object value, Cl
BinaryStreamUtils.writeInt64(stream, convertToLong(value));
break;
case Int128:
BinaryStreamUtils.writeInt128(stream, convertToBigInteger(value));
BinaryStreamUtils.writeInt128(stream, NumberConverter.toBigInteger(value));
break;
case Int256:
BinaryStreamUtils.writeInt256(stream, convertToBigInteger(value));
BinaryStreamUtils.writeInt256(stream, NumberConverter.toBigInteger(value));
break;
case UInt8:
BinaryStreamUtils.writeUnsignedInt8(stream, convertToInteger(value));
Expand All @@ -520,13 +521,13 @@ private static void serializePrimitiveData(OutputStream stream, Object value, Cl
BinaryStreamUtils.writeUnsignedInt32(stream, convertToLong(value));
break;
case UInt64:
BinaryStreamUtils.writeUnsignedInt64(stream, convertToLong(value));
BinaryStreamUtils.writeUnsignedInt64(stream, NumberConverter.toBigInteger(value));
break;
case UInt128:
BinaryStreamUtils.writeUnsignedInt128(stream, convertToBigInteger(value));
BinaryStreamUtils.writeUnsignedInt128(stream, NumberConverter.toBigInteger(value));
break;
case UInt256:
BinaryStreamUtils.writeUnsignedInt256(stream, convertToBigInteger(value));
BinaryStreamUtils.writeUnsignedInt256(stream, NumberConverter.toBigInteger(value));
break;
case Float32:
BinaryStreamUtils.writeFloat32(stream, (float) value);
Expand All @@ -539,7 +540,7 @@ private static void serializePrimitiveData(OutputStream stream, Object value, Cl
case Decimal64:
case Decimal128:
case Decimal256:
BinaryStreamUtils.writeDecimal(stream, convertToBigDecimal(value), column.getPrecision(), column.getScale());
BinaryStreamUtils.writeDecimal(stream, NumberConverter.toBigDecimal(value), column.getPrecision(), column.getScale());
break;
case Bool:
BinaryStreamUtils.writeBoolean(stream, (Boolean) value);
Expand Down Expand Up @@ -765,32 +766,6 @@ public static Long convertToLong(Object value) {
}
}

public static BigInteger convertToBigInteger(Object value) {
if (value instanceof BigInteger) {
return (BigInteger) value;
} else if (value instanceof Number) {
return BigInteger.valueOf(((Number) value).longValue());
} else if (value instanceof String) {
return new BigInteger((String) value);
} else {
throw new IllegalArgumentException("Cannot convert " + value + " to BigInteger");
}
}

public static BigDecimal convertToBigDecimal(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 {
throw new IllegalArgumentException("Cannot convert " + value + " to BigDecimal");
}
}

public static String convertToString(Object value) {
return java.lang.String.valueOf(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Collections;
Expand Down Expand Up @@ -210,7 +209,13 @@ public BigInteger convertNumberToBigInteger(Object value) {
}

public BigDecimal convertNumberToBigDecimal(Object value) {
return BigDecimal.valueOf(((Number) value).doubleValue());
Number number = (Number) value;
Comment thread
chernser marked this conversation as resolved.
Outdated
if (number instanceof Byte || number instanceof Short || number instanceof Integer || number instanceof Long) {
return BigDecimal.valueOf(number.longValue());
} else if (number instanceof Float || number instanceof Double) {
return BigDecimal.valueOf(number.doubleValue());
}
return new BigDecimal(number.toString());
}

// Date & Time converters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.TimeZone;
import java.util.function.Consumer;

@SuppressWarnings("deprecation")
public class ClickHouseBinaryFormatReaderTest {

@Test
Expand Down Expand Up @@ -83,6 +84,66 @@ public void testReadingNumbers() throws IOException {
}
}

@Test
public void testGetBigDecimalForIntegerWidths8To256() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();

String[] names = new String[] {
"i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64", "i128", "u128", "i256", "u256"
};
String[] types = new String[] {
"Int8", "UInt8", "Int16", "UInt16", "Int32", "UInt32", "Int64", "UInt64",
"Int128", "UInt128", "Int256", "UInt256"
};

BinaryStreamUtils.writeVarInt(out, names.length);
for (String name : names) {
BinaryStreamUtils.writeString(out, name);
}
for (String type : types) {
BinaryStreamUtils.writeString(out, type);
}

BigInteger u64 = new BigInteger("18446744073709551615");
BigInteger i128 = new BigInteger("-170141183460469231731687303715884105728");
BigInteger u128 = new BigInteger("340282366920938463463374607431768211455");
BigInteger i256 = new BigInteger("-57896044618658097711785492504343953926634992332820282019728792003956564819968");
BigInteger u256 = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639935");

BinaryStreamUtils.writeInt8(out, -128);
BinaryStreamUtils.writeUnsignedInt8(out, 255);
BinaryStreamUtils.writeInt16(out, -32768);
BinaryStreamUtils.writeUnsignedInt16(out, 65535);
BinaryStreamUtils.writeInt32(out, Integer.MIN_VALUE);
BinaryStreamUtils.writeUnsignedInt32(out, 4294967295L);
BinaryStreamUtils.writeInt64(out, Long.MAX_VALUE);
BinaryStreamUtils.writeUnsignedInt64(out, u64);
BinaryStreamUtils.writeInt128(out, i128);
BinaryStreamUtils.writeUnsignedInt128(out, u128);
BinaryStreamUtils.writeInt256(out, i256);
BinaryStreamUtils.writeUnsignedInt256(out, u256);

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.getBigDecimal("i8"), BigDecimal.valueOf(-128));
Assert.assertEquals(reader.getBigDecimal("u8"), BigDecimal.valueOf(255));
Assert.assertEquals(reader.getBigDecimal("i16"), BigDecimal.valueOf(-32768));
Assert.assertEquals(reader.getBigDecimal("u16"), BigDecimal.valueOf(65535));
Assert.assertEquals(reader.getBigDecimal("i32"), BigDecimal.valueOf(Integer.MIN_VALUE));
Assert.assertEquals(reader.getBigDecimal("u32"), BigDecimal.valueOf(4294967295L));
Assert.assertEquals(reader.getBigDecimal("i64"), BigDecimal.valueOf(Long.MAX_VALUE));
Assert.assertEquals(reader.getBigDecimal("u64"), new BigDecimal(u64));
Assert.assertEquals(reader.getBigDecimal("i128"), new BigDecimal(i128));
Assert.assertEquals(reader.getBigDecimal("u128"), new BigDecimal(u128));
Assert.assertEquals(reader.getBigDecimal("i256"), new BigDecimal(i256));
Assert.assertEquals(reader.getBigDecimal("u256"), new BigDecimal(u256));
}

@Test
public void testReadingNumbersWithOverflow() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,24 @@ public void testVariantWithDecimals() throws Exception {
});
}

@Test(groups = {"integration"})
public void testGetBigDecimalDoesNotTruncateLargeIntegers() throws Exception {
BigDecimal expectedInt64 = BigDecimal.valueOf(Long.MAX_VALUE);
BigDecimal expectedUInt64 = new BigDecimal("18446744073709551615");

String sql = "SELECT toInt64(" + Long.MAX_VALUE + ") AS i64, "
+ "toUInt64('" + expectedUInt64.toPlainString() + "') AS u64";

try (QueryResponse response = client.query(sql).get(3, TimeUnit.SECONDS)) {
Comment thread
chernser marked this conversation as resolved.
Outdated
ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);

Assert.assertNotNull(reader.next());
Assert.assertEquals(reader.getBigDecimal("i64"), expectedInt64);
Assert.assertEquals(reader.getBigDecimal("u64"), expectedUInt64);
Assert.assertFalse(reader.hasNext());
}
}

@Test(groups = {"integration"})
public void testVariantWithArrays() throws Exception {
testVariantWith("arrays", new String[]{"field Variant(String, Array(String))"},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
package com.clickhouse.client.internal;

import com.clickhouse.client.api.data_formats.RowBinaryWithNamesAndTypesFormatReader;
import com.clickhouse.client.api.data_formats.internal.BinaryStreamReader;
import com.clickhouse.client.api.data_formats.internal.SerializerUtils;
import com.clickhouse.client.api.query.QuerySettings;
import com.clickhouse.data.ClickHouseColumn;
import com.clickhouse.data.format.BinaryStreamUtils;
import org.testng.annotations.Test;

import org.testng.Assert;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.TimeZone;

public class SerializerUtilsTests {

@Test
Expand All @@ -13,4 +25,114 @@
Assert.assertEquals(SerializerUtils.convertToInteger("1640995199").intValue(), expected);
Assert.assertEquals(SerializerUtils.convertToInteger(false).intValue(), 0);
}

@Test
public void testSerializeNumbersRoundTrip() throws IOException {
String[] names = new String[]{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r"};
String[] types = new String[]{"Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64",
"Int128", "Int256", "UInt128", "UInt256", "Float32", "Float64", "Decimal32(3)", "Decimal64(3)",
"Decimal128(4)", "Decimal256(4)"};
Object[] values = new Object[]{
(byte) 120,
(short) 120,
120,
120L,
120,
120,
120L,
BigInteger.valueOf(120),
BigInteger.valueOf(120),
BigInteger.valueOf(120),
BigInteger.valueOf(120),
BigInteger.valueOf(120),
120.0f,
Comment thread
chernser marked this conversation as resolved.
120.0d,
BigDecimal.valueOf(120),
BigDecimal.valueOf(120),
BigDecimal.valueOf(120),
BigDecimal.valueOf(120)
};

RowBinaryWithNamesAndTypesFormatReader reader = serializeRow(names, types, values);
reader.next();

for (String name : names) {
Assert.assertTrue(reader.getBigDecimal(name).compareTo(BigDecimal.valueOf(120)) == 0);
}
}

@Test
public void testSerializeIntegerWidths8To256RoundTripAsBigDecimal() throws IOException {
String[] names = new String[]{
"i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64", "i128", "u128", "i256", "u256"
};
String[] types = new String[]{
"Int8", "UInt8", "Int16", "UInt16", "Int32", "UInt32", "Int64", "UInt64",
"Int128", "UInt128", "Int256", "UInt256"
};

BigInteger u64 = new BigInteger("18446744073709551615");
BigInteger i128 = new BigInteger("-170141183460469231731687303715884105728");
BigInteger u128 = new BigInteger("340282366920938463463374607431768211455");
BigInteger i256 = new BigInteger("-57896044618658097711785492504343953926634992332820282019728792003956564819968");
BigInteger u256 = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639935");

Object[] values = new Object[]{
-128,
255,
-32768,
65535,
Integer.MIN_VALUE,
4294967295L,
Long.MAX_VALUE,
u64,
i128,
u128,
i256,
u256
};

BigDecimal[] expected = new BigDecimal[]{
BigDecimal.valueOf(-128),
BigDecimal.valueOf(255),
BigDecimal.valueOf(-32768),
BigDecimal.valueOf(65535),
BigDecimal.valueOf(Integer.MIN_VALUE),
BigDecimal.valueOf(4294967295L),
BigDecimal.valueOf(Long.MAX_VALUE),
new BigDecimal(u64),
new BigDecimal(i128),
new BigDecimal(u128),
new BigDecimal(i256),
new BigDecimal(u256)
};

RowBinaryWithNamesAndTypesFormatReader reader = serializeRow(names, types, values);
reader.next();

for (int i = 0; i < names.length; i++) {
Assert.assertEquals(reader.getBigDecimal(names[i]), expected[i]);
}
}

private RowBinaryWithNamesAndTypesFormatReader serializeRow(String[] names, String[] types, Object[] values)
throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
BinaryStreamUtils.writeVarInt(out, names.length);

Check warning on line 122 in client-v2/src/test/java/com/clickhouse/client/internal/SerializerUtilsTests.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "BinaryStreamUtils"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZ1Pf2X1qMfC8EjjbkYm&open=AZ1Pf2X1qMfC8EjjbkYm&pullRequest=2814
for (String name : names) {
BinaryStreamUtils.writeString(out, name);

Check warning on line 124 in client-v2/src/test/java/com/clickhouse/client/internal/SerializerUtilsTests.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "BinaryStreamUtils"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZ1Pf2X1qMfC8EjjbkYn&open=AZ1Pf2X1qMfC8EjjbkYn&pullRequest=2814
}
for (String type : types) {
BinaryStreamUtils.writeString(out, type);

Check warning on line 127 in client-v2/src/test/java/com/clickhouse/client/internal/SerializerUtilsTests.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "BinaryStreamUtils"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=ClickHouse_clickhouse-java&issues=AZ1Pf2X1qMfC8EjjbkYo&open=AZ1Pf2X1qMfC8EjjbkYo&pullRequest=2814
}
for (int i = 0; i < names.length; i++) {
SerializerUtils.serializeData(out, values[i], ClickHouseColumn.of(names[i], types[i]));
}

return new RowBinaryWithNamesAndTypesFormatReader(
new ByteArrayInputStream(out.toByteArray()),
new QuerySettings().setUseTimeZone(TimeZone.getTimeZone("UTC").toZoneId().getId()),
new BinaryStreamReader.CachingByteBufferAllocator());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ void testPrimitiveValues(Object array, ClickHouseColumn column) throws SQLExcept
assertEquals(rs.getLong(valueColumn), number.longValue());
assertEquals(rs.getFloat(valueColumn), number.floatValue());
assertEquals(rs.getDouble(valueColumn), number.doubleValue());
assertEquals(rs.getBigDecimal(valueColumn), BigDecimal.valueOf(number.doubleValue()));
assertEquals(rs.getBigDecimal(valueColumn), expectedBigDecimal(number));
} else if (itemClass == Boolean.class || itemClass == boolean.class) {
Number number = ((Boolean) value) ? 1 : 0;
assertEquals(rs.getBoolean(valueColumn), ((Boolean) value));
Expand All @@ -207,6 +207,16 @@ void testPrimitiveValues(Object array, ClickHouseColumn column) throws SQLExcept
}
}

private static BigDecimal expectedBigDecimal(Number number) {
if (number instanceof Byte || number instanceof Short || number instanceof Integer || number instanceof Long) {
return BigDecimal.valueOf(number.longValue());
} else if (number instanceof Float || number instanceof Double) {
return BigDecimal.valueOf(number.doubleValue());
}

return new BigDecimal(number.toString());
}

@DataProvider
static Object[][] testPrimitiveValues() {
return new Object[][]{
Expand Down
Loading