Skip to content

Commit 241feb3

Browse files
committed
Use null binary value instead of empty byte array
1 parent b11e807 commit 241feb3

File tree

7 files changed

+94
-25
lines changed

7 files changed

+94
-25
lines changed

clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseInputStream.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,6 @@ public byte[] readBytes(int length) throws IOException {
298298
ensureOpen();
299299

300300
byte[] bytes = new byte[length];
301-
int offset = 0;
302301
int counter = 0;
303302
while (counter < length) {
304303
if (position >= limit && updateBuffer() < 0) {
@@ -314,9 +313,8 @@ public byte[] readBytes(int length) throws IOException {
314313
}
315314

316315
int size = Math.min(limit - position, length - counter);
317-
System.arraycopy(buffer, position, bytes, offset, size);
316+
System.arraycopy(buffer, position, bytes, counter, size);
318317
position += size;
319-
offset += size;
320318
counter += size;
321319
}
322320

clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ default <T, E extends Enum<E>> T asObject(Class<T> clazz) {
541541
/**
542542
* Gets binary value as byte array.
543543
*
544-
* @return non-null byte array
544+
* @return byte array which could be null
545545
*/
546546
default byte[] asBinary() {
547547
return asBinary(0, null);
@@ -551,7 +551,7 @@ default byte[] asBinary() {
551551
* Gets binary value as fixed length byte array.
552552
*
553553
* @param length byte length of value, 0 or negative number means no limit
554-
* @return non-null byte array
554+
* @return byte array which could be null
555555
*/
556556
default byte[] asBinary(int length) {
557557
return asBinary(length, null);
@@ -561,7 +561,7 @@ default byte[] asBinary(int length) {
561561
* Gets binary value as byte array.
562562
*
563563
* @param charset charset, null is same as default(UTF-8)
564-
* @return non-null byte array
564+
* @return byte array which could be null
565565
*/
566566
default byte[] asBinary(Charset charset) {
567567
return asBinary(0, charset);
@@ -572,11 +572,11 @@ default byte[] asBinary(Charset charset) {
572572
*
573573
* @param length byte length of value, 0 or negative number means no limit
574574
* @param charset charset, null is same as default(UTF-8)
575-
* @return non-null byte array
575+
* @return byte array which could be null
576576
*/
577577
default byte[] asBinary(int length, Charset charset) {
578578
if (isNullOrEmpty()) {
579-
return ClickHouseValues.EMPTY_BYTE_ARRAY;
579+
return null;
580580
}
581581

582582
byte[] bytes = asString().getBytes(charset == null ? StandardCharsets.UTF_8 : charset);

clickhouse-client/src/main/java/com/clickhouse/client/data/BinaryStreamUtils.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,6 +1649,19 @@ public static void writeString(OutputStream output, String value, Charset charse
16491649
output.write(bytes);
16501650
}
16511651

1652+
/**
1653+
* Writes a binary string to given output stream.
1654+
*
1655+
* @param output non-null output stream
1656+
* @param value non-null byte array
1657+
* @throws IOException when failed to write value to output stream or reached
1658+
* end of the stream
1659+
*/
1660+
public static void writeString(OutputStream output, byte[] value) throws IOException {
1661+
writeVarInt(output, value.length);
1662+
output.write(value);
1663+
}
1664+
16521665
/**
16531666
* Read varint from given input stream.
16541667
*

clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseRowBinaryProcessor.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,11 +335,7 @@ private void buildMappingsForDataTypes() {
335335
(v, f, c, o) -> o.write(v.asBinary(c.getPrecision())), ClickHouseDataType.FixedString);
336336
buildMappings(deserializers, serializers,
337337
(r, f, c, i) -> ClickHouseStringValue.of(r, i.readBytes(i.readVarInt())),
338-
(v, f, c, o) -> {
339-
byte[] bytes = v.asBinary();
340-
BinaryStreamUtils.writeVarInt(o, bytes.length);
341-
o.write(bytes);
342-
}, ClickHouseDataType.String);
338+
(v, f, c, o) -> BinaryStreamUtils.writeString(o, v.asBinary()), ClickHouseDataType.String);
343339
buildMappings(deserializers, serializers,
344340
(r, f, c, i) -> ClickHouseUuidValue.of(r, BinaryStreamUtils.readUuid(i)),
345341
(v, f, c, o) -> BinaryStreamUtils.writeUuid(o, v.asUuid()), ClickHouseDataType.UUID);

clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseStringValue.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.time.LocalDate;
1010
import java.time.LocalDateTime;
1111
import java.time.LocalTime;
12+
import java.util.Arrays;
1213
import java.util.Objects;
1314
import java.util.UUID;
1415
import com.clickhouse.client.ClickHouseChecker;
@@ -217,7 +218,7 @@ public byte[] asBinary() {
217218
bytes = value.getBytes(StandardCharsets.UTF_8);
218219
}
219220

220-
return bytes != null ? bytes : ClickHouseValues.EMPTY_BYTE_ARRAY;
221+
return bytes;
221222
}
222223

223224
@Override
@@ -229,7 +230,7 @@ public byte[] asBinary(int length, Charset charset) {
229230
if (bytes != null && length > 0) {
230231
return ClickHouseChecker.notWithDifferentLength(bytes, length);
231232
} else {
232-
return bytes != null ? bytes : ClickHouseValues.EMPTY_BYTE_ARRAY;
233+
return bytes;
233234
}
234235
}
235236

@@ -380,6 +381,16 @@ public ClickHouseStringValue update(Object value) {
380381
return update(value == null ? null : value.toString());
381382
}
382383

384+
@Override
385+
public int hashCode() {
386+
final int prime = 31;
387+
int result = 1;
388+
result = prime * result + (binary ? 1231 : 1237);
389+
result = prime * result + Arrays.hashCode(bytes);
390+
result = prime * result + ((value == null) ? 0 : value.hashCode());
391+
return result;
392+
}
393+
383394
@Override
384395
public boolean equals(Object obj) {
385396
if (this == obj) { // too bad this is a mutable class :<
@@ -389,12 +400,7 @@ public boolean equals(Object obj) {
389400
}
390401

391402
ClickHouseStringValue v = (ClickHouseStringValue) obj;
392-
return Objects.equals(bytes, v.bytes) && Objects.equals(value, v.value);
393-
}
394-
395-
@Override
396-
public int hashCode() {
397-
return Objects.hash(bytes, value);
403+
return binary == v.binary && Objects.equals(bytes, v.bytes) && Objects.equals(value, v.value);
398404
}
399405

400406
@Override

clickhouse-client/src/test/java/com/clickhouse/client/data/ClickHouseStringValueTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ public void testTypeConversion() {
5757

5858
@Test(groups = { "unit" })
5959
public void testBinaryValue() {
60-
Assert.assertEquals(ClickHouseStringValue.of((byte[]) null).asBinary(), new byte[0]);
61-
Assert.assertEquals(ClickHouseStringValue.of((String) null).asBinary(), new byte[0]);
60+
Assert.assertEquals(ClickHouseStringValue.of((byte[]) null).asBinary(), null);
61+
Assert.assertEquals(ClickHouseStringValue.of((String) null).asBinary(), null);
6262
Assert.assertEquals(ClickHouseStringValue.of(new byte[0]).asBinary(), new byte[0]);
6363
Assert.assertEquals(ClickHouseStringValue.of("").asBinary(), new byte[0]);
64-
Assert.assertEquals(ClickHouseStringValue.of((byte[]) null).asBinary(0), new byte[0]);
65-
Assert.assertEquals(ClickHouseStringValue.of((String) null).asBinary(0), new byte[0]);
64+
Assert.assertEquals(ClickHouseStringValue.of((byte[]) null).asBinary(0), null);
65+
Assert.assertEquals(ClickHouseStringValue.of((String) null).asBinary(0), null);
6666
Assert.assertEquals(ClickHouseStringValue.of(new byte[0]).asBinary(0), new byte[0]);
6767
Assert.assertEquals(ClickHouseStringValue.of("").asBinary(0), new byte[0]);
6868

clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHousePreparedStatementTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ private Object[][] getTypedParameters() {
3737
@Test(groups = "integration")
3838
public void testReadWriteBinaryString() throws SQLException {
3939
Properties props = new Properties();
40+
try (ClickHouseConnection conn = newConnection(props);
41+
Statement s = conn.createStatement()) {
42+
s.execute("drop table if exists test_binary_string; "
43+
+ "create table test_binary_string(id Int32, "
44+
+ "f0 FixedString(3), f1 Nullable(FixedString(3)), s0 String, s1 Nullable(String))engine=Memory");
45+
}
46+
4047
byte[] bytes = new byte[256];
4148
for (int i = 0; i < 256; i++) {
4249
bytes[i] = (byte) i;
@@ -51,6 +58,55 @@ public void testReadWriteBinaryString() throws SQLException {
5158
Assert.assertEquals(rs.getInt(2), bytes.length);
5259
Assert.assertFalse(rs.next());
5360
}
61+
62+
bytes = new byte[] { 0x61, 0x62, 0x63 };
63+
try (ClickHouseConnection conn = newConnection(props);
64+
PreparedStatement ps = conn.prepareStatement("insert into test_binary_string")) {
65+
ps.setInt(1, 1);
66+
ps.setBytes(2, bytes);
67+
ps.setBytes(3, null);
68+
ps.setBytes(4, bytes);
69+
ps.setBytes(5, null);
70+
ps.addBatch();
71+
ps.setInt(1, 2);
72+
ps.setString(2, "abc");
73+
ps.setString(3, null);
74+
ps.setString(4, "abc");
75+
ps.setString(5, null);
76+
ps.addBatch();
77+
ps.setInt(1, 3);
78+
ps.setBytes(2, bytes);
79+
ps.setBytes(3, bytes);
80+
ps.setBytes(4, bytes);
81+
ps.setBytes(5, bytes);
82+
ps.addBatch();
83+
ps.setInt(1, 4);
84+
ps.setString(2, "abc");
85+
ps.setString(3, "abc");
86+
ps.setString(4, "abc");
87+
ps.setString(5, "abc");
88+
ps.addBatch();
89+
ps.executeBatch();
90+
}
91+
92+
try (ClickHouseConnection conn = newConnection(props);
93+
PreparedStatement ps = conn
94+
.prepareStatement(
95+
"select distinct * except(id) from test_binary_string where f0 = ? order by id")) {
96+
ps.setBytes(1, bytes);
97+
ResultSet rs = ps.executeQuery();
98+
Assert.assertTrue(rs.next(), "Should have at least one row");
99+
Assert.assertEquals(rs.getBytes(1), bytes);
100+
Assert.assertNull(rs.getBytes(2), "f1 should be null");
101+
Assert.assertEquals(rs.getBytes(3), bytes);
102+
Assert.assertNull(rs.getBytes(4), "s1 should be null");
103+
Assert.assertTrue(rs.next(), "Should have at least two rows");
104+
for (int i = 1; i <= 4; i++) {
105+
Assert.assertEquals(rs.getBytes(i), bytes);
106+
Assert.assertEquals(rs.getString(i), "abc");
107+
}
108+
Assert.assertFalse(rs.next(), "Should not have more than two rows");
109+
}
54110
}
55111

56112
@Test(groups = "integration")

0 commit comments

Comments
 (0)