Skip to content

Commit 14384ae

Browse files
authored
Merge pull request #666 from zhicwu/negative-decimal
Fix the issue of hanlding negative big decimals
2 parents 5a5fb7d + 4e67c9c commit 14384ae

File tree

4 files changed

+80
-15
lines changed

4 files changed

+80
-15
lines changed

clickhouse-jdbc/docs/datetime.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ The methods which take a [Calendar]((https://docs.oracle.com/javase/8/docs/api/j
5151
For Date and DateTime fields, the JDBC driver has enough time zone related information available, so these methods would only be relevant for String or other typed fields. There might be valid use cases, but for now we think that adding such an option would make things even more complicated.
5252

5353
Requested Type | Number | Date | DateTime | Other
54-
---------------| ----------------------------------
54+
---------------| -------|------|----------|--------
5555
[Date](https://docs.oracle.com/javase/8/docs/api/java/sql/Date.html) | Seconds or milliseconds past epoch truncated to day in relevant time zone | Date in relevant time zone, midnight | Date time in relevant time zone, rewind to midnight | Try number, date time (with or without offset) truncated to day, date
5656
[Time](https://docs.oracle.com/javase/8/docs/api/java/sql/Time.html) | Local time at 1970-01-01 (e.g. “1337” is “13:37:00” at TZ) | Midnight on 1970-01-01 in relevant time zone | Local time in relevant time zone | Local time in relevant time zone via ISO format or via number, at 1970-01-01
5757
[Timestamp](https://docs.oracle.com/javase/8/docs/api/java/sql/Timestamp.html) | Seconds or milliseconds past epoch | Local date at midnight in relevant time zone | Local date and time in relevant time zone | Number, date time with or without offset

clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/util/ClickHouseRowBinaryStream.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -188,22 +188,24 @@ public void writeFloat64(double value) throws IOException {
188188
Utils.writeLong(out, Double.doubleToLongBits(value));
189189
}
190190

191-
public void writeDecimal128(BigDecimal num, int scale) throws IOException {
192-
BigInteger bi = Utils.toBigInteger(num, scale);
193-
byte[] r = bi.toByteArray();
194-
for (int i = r.length; i > 0; i--) {
195-
out.write(r[i - 1]);
191+
public void writeBigInteger(BigInteger value, int byteLength) throws IOException {
192+
byte empty = value.signum() == -1 ? (byte) 0xFF : 0x00;
193+
byte[] bytes = value.toByteArray();
194+
for (int i = bytes.length - 1; i >= 0; i--) {
195+
out.write(bytes[i]);
196+
}
197+
198+
for (int i = byteLength - bytes.length; i > 0; i--) {
199+
out.write(empty);
196200
}
197-
out.write(new byte[16 - r.length]);
201+
}
202+
203+
public void writeDecimal128(BigDecimal num, int scale) throws IOException {
204+
writeBigInteger(Utils.toBigInteger(num, scale), 16);
198205
}
199206

200207
public void writeDecimal256(BigDecimal num, int scale) throws IOException {
201-
BigInteger bi = Utils.toBigInteger(num, scale);
202-
byte[] r = bi.toByteArray();
203-
for (int i = r.length; i > 0; i--) {
204-
out.write(r[i - 1]);
205-
}
206-
out.write(new byte[32 - r.length]);
208+
writeBigInteger(Utils.toBigInteger(num, scale), 32);
207209
}
208210

209211
public void writeDecimal64(BigDecimal num, int scale) throws IOException {

clickhouse-jdbc/src/test/java/ru/yandex/clickhouse/integration/RowBinaryStreamTest.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
import static org.testng.Assert.assertEquals;
44
import static org.testng.Assert.assertFalse;
5-
import static org.testng.Assert.assertThrows;
65
import static org.testng.Assert.assertTrue;
76

87
import java.io.EOFException;
98
import java.io.IOException;
9+
import java.math.BigDecimal;
1010
import java.math.BigInteger;
1111
import java.sql.Date;
1212
import java.sql.ResultSet;
@@ -276,6 +276,46 @@ public void testBitmap() throws Exception {
276276
}
277277
}
278278

279+
@Test
280+
public void testBigDecimals() throws Exception {
281+
try (ClickHouseStatement statement = connection.createStatement()) {
282+
statement.execute("set allow_experimental_bigint_types=1;"
283+
+ "create table if not exists test.test_big_decimals(d128 Decimal128(6), d256 Decimal256(12)) engine=Memory");
284+
} catch (SQLException e) {
285+
return;
286+
}
287+
288+
try (ClickHouseStatement statement = connection.createStatement()) {
289+
BigDecimal[] values = new BigDecimal[] {
290+
BigDecimal.valueOf(-123.123456789D),
291+
BigDecimal.ZERO,
292+
BigDecimal.valueOf(123.123456789D)
293+
};
294+
statement.sendRowBinaryStream("insert into table test.test_big_decimals", new ClickHouseStreamCallback() {
295+
@Override
296+
public void writeTo(ClickHouseRowBinaryStream stream) throws IOException {
297+
for (int i = 0; i < values.length; i++) {
298+
stream.writeDecimal128(values[i], 6);
299+
stream.writeDecimal256(values[i], 12);
300+
}
301+
}
302+
});
303+
304+
try (ResultSet rs = statement.executeQuery("select * from test.test_big_decimals order by d128")) {
305+
int rowCounter = 0;
306+
while (rs.next()) {
307+
rowCounter++;
308+
assertEquals(rs.getBigDecimal(1, 6), values[rowCounter - 1].setScale(6, BigDecimal.ROUND_DOWN));
309+
assertEquals(rs.getBigDecimal(2, 12), values[rowCounter - 1].setScale(12, BigDecimal.ROUND_DOWN));
310+
}
311+
312+
assertEquals(rowCounter, values.length);
313+
}
314+
315+
statement.execute("drop table if exists test.test_big_decimals");
316+
}
317+
}
318+
279319
private void testRowBinaryStream(boolean rowBinaryResult) throws Exception {
280320
createTable("test.raw_binary");
281321
ClickHouseStatement statement = connection.createStatement();

clickhouse-jdbc/src/test/java/ru/yandex/clickhouse/util/ClickHouseRowBinaryStreamTest.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,32 @@ public void testDecimal128() throws Exception {
112112
@Override
113113
public void write(ClickHouseRowBinaryStream stream) throws Exception {
114114
stream.writeDecimal128(new BigDecimal(10.23), 3);
115+
stream.writeDecimal128(new BigDecimal(-10.23), 3);
115116
}
116117
},
117-
new byte[]{-10, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
118+
new byte[] {
119+
-10, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120+
10, -40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
121+
}
122+
);
123+
}
124+
125+
@Test
126+
public void testDecimal256() throws Exception {
127+
check(
128+
new StreamWriter() {
129+
@Override
130+
public void write(ClickHouseRowBinaryStream stream) throws Exception {
131+
stream.writeDecimal256(new BigDecimal(10.23), 3);
132+
stream.writeDecimal256(new BigDecimal(-10.23), 3);
133+
}
134+
},
135+
new byte[] {
136+
-10, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
137+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138+
10, -40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
139+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
140+
}
118141
);
119142
}
120143

0 commit comments

Comments
 (0)