Skip to content

Commit b11e807

Browse files
committed
Improve binary data support
1 parent c9ea0a1 commit b11e807

File tree

5 files changed

+95
-43
lines changed

5 files changed

+95
-43
lines changed

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.net.Inet6Address;
99
import java.net.InetAddress;
1010
import java.net.UnknownHostException;
11+
import java.nio.charset.StandardCharsets;
1112
import java.time.Instant;
1213
import java.time.LocalDate;
1314
import java.time.LocalDateTime;
@@ -136,6 +137,10 @@ public final class ClickHouseValues {
136137

137138
public static final String TYPE_CLASS = "Class";
138139

140+
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
141+
private static final byte[] UNHEX_PREFIX = "unhex('".getBytes(StandardCharsets.US_ASCII);
142+
private static final byte[] UNHEX_SUFFIX = "')".getBytes(StandardCharsets.US_ASCII);
143+
139144
/**
140145
* Converts IP address to big integer.
141146
*
@@ -182,6 +187,52 @@ public static BigInteger convertToBigInteger(UUID value) {
182187
return low.add(high.multiply(BIGINT_HL_BOUNDARY));
183188
}
184189

190+
/**
191+
* Converts given byte array to string in hexadecimal format.
192+
*
193+
* @param bytes byte array
194+
* @return non-null string
195+
*/
196+
public static String convertToHexString(byte[] bytes) {
197+
int len = bytes != null ? bytes.length : 0;
198+
if (len == 0) {
199+
return "";
200+
}
201+
202+
byte[] hexChars = new byte[len * 2];
203+
for (int i = 0; i < len; i++) {
204+
int v = bytes[i] & 0xFF;
205+
int j = i * 2;
206+
hexChars[j] = HEX_ARRAY[v >>> 4];
207+
hexChars[j + 1] = HEX_ARRAY[v & 0x0F];
208+
}
209+
return new String(hexChars, StandardCharsets.UTF_8);
210+
}
211+
212+
/**
213+
* Converts given byte array to unhex() expression.
214+
*
215+
* @param bytes byte array
216+
* @return non-null expression
217+
*/
218+
public static String convertToUnhexExpression(byte[] bytes) {
219+
int len = bytes != null ? bytes.length : 0;
220+
if (len == 0) {
221+
return EMPTY_STRING_EXPR;
222+
}
223+
224+
int offset = UNHEX_PREFIX.length;
225+
byte[] hexChars = new byte[len * 2 + offset + UNHEX_SUFFIX.length];
226+
System.arraycopy(UNHEX_PREFIX, 0, hexChars, 0, offset);
227+
System.arraycopy(UNHEX_SUFFIX, 0, hexChars, hexChars.length - UNHEX_SUFFIX.length, UNHEX_SUFFIX.length);
228+
for (int i = 0; i < len; i++) {
229+
int v = bytes[i] & 0xFF;
230+
hexChars[offset++] = HEX_ARRAY[v >>> 4];
231+
hexChars[offset++] = HEX_ARRAY[v & 0x0F];
232+
}
233+
return new String(hexChars, StandardCharsets.UTF_8);
234+
}
235+
185236
/**
186237
* Converts big decimal to instant.
187238
*

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

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -268,19 +268,9 @@ public ClickHouseStringValue resetToNullOrEmpty() {
268268
public String toSqlExpression() {
269269
if (isNullOrEmpty()) {
270270
return ClickHouseValues.NULL_EXPR;
271+
} else if (binary) {
272+
return ClickHouseValues.convertToUnhexExpression(bytes);
271273
}
272-
// else if (binary) {
273-
// int len = bytes.length;
274-
// if (len == 0) {
275-
// return ClickHouseValues.EMPTY_STRING_EXPR;
276-
// }
277-
// StringBuilder builder = new StringBuilder(len * 3 + 5).append("char(");
278-
// for (byte b : bytes) {
279-
// builder.append(b).append(',');
280-
// }
281-
// builder.setLength(builder.length() - 1);
282-
// return builder.append(')').toString();
283-
// }
284274
return ClickHouseValues.convertToQuotedString(asString());
285275
}
286276

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public void testBinaryValue() {
7373
Assert.assertEquals(ClickHouseStringValue.of("a").asBinary(), new byte[] { 97 });
7474

7575
Assert.assertEquals(ClickHouseStringValue.of(new byte[0]).toSqlExpression(), "''");
76-
Assert.assertEquals(ClickHouseStringValue.of(new byte[] { 97 }).toSqlExpression(), "'a'");
76+
Assert.assertEquals(ClickHouseStringValue.of(new byte[] { 97, 98, 99 }).toSqlExpression(), "unhex('616263')");
7777
}
7878

7979
@Test(groups = { "unit" })

clickhouse-jdbc/src/main/java/com/clickhouse/jdbc/ClickHouseResultSet.java

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.clickhouse.jdbc;
22

3+
import java.io.ByteArrayInputStream;
34
import java.io.InputStream;
45
import java.io.Reader;
6+
import java.io.StringReader;
57
import java.io.UncheckedIOException;
68
import java.math.BigDecimal;
79
import java.math.BigInteger;
810
import java.net.MalformedURLException;
911
import java.net.URL;
12+
import java.nio.charset.StandardCharsets;
1013
import java.sql.Array;
1114
import java.sql.Blob;
1215
import java.sql.Clob;
@@ -217,14 +220,13 @@ public Array getArray(String columnLabel) throws SQLException {
217220

218221
@Override
219222
public InputStream getAsciiStream(int columnIndex) throws SQLException {
220-
// TODO Auto-generated method stub
221-
return null;
223+
ClickHouseValue v = getValue(columnIndex);
224+
return v.isNullOrEmpty() ? null : new ByteArrayInputStream(v.asBinary(StandardCharsets.US_ASCII));
222225
}
223226

224227
@Override
225228
public InputStream getAsciiStream(String columnLabel) throws SQLException {
226-
// TODO Auto-generated method stub
227-
return null;
229+
return getAsciiStream(findColumn(columnLabel));
228230
}
229231

230232
@Override
@@ -249,14 +251,13 @@ public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLExcepti
249251

250252
@Override
251253
public InputStream getBinaryStream(int columnIndex) throws SQLException {
252-
// TODO Auto-generated method stub
253-
return null;
254+
ClickHouseValue v = getValue(columnIndex);
255+
return v.isNullOrEmpty() ? null : new ByteArrayInputStream(v.asBinary());
254256
}
255257

256258
@Override
257259
public InputStream getBinaryStream(String columnLabel) throws SQLException {
258-
// TODO Auto-generated method stub
259-
return null;
260+
return getBinaryStream(findColumn(columnLabel));
260261
}
261262

262263
@Override
@@ -267,8 +268,7 @@ public Blob getBlob(int columnIndex) throws SQLException {
267268

268269
@Override
269270
public Blob getBlob(String columnLabel) throws SQLException {
270-
// TODO Auto-generated method stub
271-
return null;
271+
return getBlob(findColumn(columnLabel));
272272
}
273273

274274
@Override
@@ -293,26 +293,23 @@ public byte getByte(String columnLabel) throws SQLException {
293293

294294
@Override
295295
public byte[] getBytes(int columnIndex) throws SQLException {
296-
// TODO Auto-generated method stub
297-
return null;
296+
return getValue(columnIndex).asBinary();
298297
}
299298

300299
@Override
301300
public byte[] getBytes(String columnLabel) throws SQLException {
302-
// TODO Auto-generated method stub
303-
return null;
301+
return getValue(findColumn(columnLabel)).asBinary();
304302
}
305303

306304
@Override
307305
public Reader getCharacterStream(int columnIndex) throws SQLException {
308-
// TODO Auto-generated method stub
309-
return null;
306+
ClickHouseValue v = getValue(columnIndex);
307+
return v.isNullOrEmpty() ? null : new StringReader(v.asString());
310308
}
311309

312310
@Override
313311
public Reader getCharacterStream(String columnLabel) throws SQLException {
314-
// TODO Auto-generated method stub
315-
return null;
312+
return getCharacterStream(findColumn(columnLabel));
316313
}
317314

318315
@Override
@@ -323,8 +320,7 @@ public Clob getClob(int columnIndex) throws SQLException {
323320

324321
@Override
325322
public Clob getClob(String columnLabel) throws SQLException {
326-
// TODO Auto-generated method stub
327-
return null;
323+
return getClob(findColumn(columnLabel));
328324
}
329325

330326
@Override
@@ -420,14 +416,12 @@ public ResultSetMetaData getMetaData() throws SQLException {
420416

421417
@Override
422418
public Reader getNCharacterStream(int columnIndex) throws SQLException {
423-
// TODO Auto-generated method stub
424-
return null;
419+
return getCharacterStream(columnIndex);
425420
}
426421

427422
@Override
428423
public Reader getNCharacterStream(String columnLabel) throws SQLException {
429-
// TODO Auto-generated method stub
430-
return null;
424+
return getCharacterStream(findColumn(columnLabel));
431425
}
432426

433427
@Override
@@ -438,8 +432,7 @@ public NClob getNClob(int columnIndex) throws SQLException {
438432

439433
@Override
440434
public NClob getNClob(String columnLabel) throws SQLException {
441-
// TODO Auto-generated method stub
442-
return null;
435+
return getNClob(findColumn(columnLabel));
443436
}
444437

445438
@Override
@@ -662,14 +655,13 @@ public URL getURL(String columnLabel) throws SQLException {
662655

663656
@Override
664657
public InputStream getUnicodeStream(int columnIndex) throws SQLException {
665-
// TODO Auto-generated method stub
666-
return null;
658+
ClickHouseValue v = getValue(columnIndex);
659+
return v.isNullOrEmpty() ? null : new ByteArrayInputStream(v.asBinary(StandardCharsets.UTF_8));
667660
}
668661

669662
@Override
670663
public InputStream getUnicodeStream(String columnLabel) throws SQLException {
671-
// TODO Auto-generated method stub
672-
return null;
664+
return getUnicodeStream(findColumn(columnLabel));
673665
}
674666

675667
@Override

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ private Object[][] getTypedParameters() {
3434
LocalDateTime.of(2021, 11, 2, 2, 3, 4) } } };
3535
}
3636

37+
@Test(groups = "integration")
38+
public void testReadWriteBinaryString() throws SQLException {
39+
Properties props = new Properties();
40+
byte[] bytes = new byte[256];
41+
for (int i = 0; i < 256; i++) {
42+
bytes[i] = (byte) i;
43+
}
44+
try (ClickHouseConnection conn = newConnection(props);
45+
PreparedStatement ps = conn.prepareStatement("select ?, ?")) {
46+
ps.setBytes(1, bytes);
47+
ps.setString(2, Integer.toString(bytes.length));
48+
ResultSet rs = ps.executeQuery();
49+
Assert.assertTrue(rs.next());
50+
Assert.assertEquals(rs.getBytes(1), bytes);
51+
Assert.assertEquals(rs.getInt(2), bytes.length);
52+
Assert.assertFalse(rs.next());
53+
}
54+
}
55+
3756
@Test(groups = "integration")
3857
public void testReadWriteDate() throws SQLException {
3958
LocalDate d = LocalDate.of(2021, 3, 25);

0 commit comments

Comments
 (0)