Skip to content

Commit

Permalink
Improve binary data support
Browse files Browse the repository at this point in the history
  • Loading branch information
zhicwu committed Jan 24, 2022
1 parent c9ea0a1 commit b11e807
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 43 deletions.
Expand Up @@ -8,6 +8,7 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -136,6 +137,10 @@ public final class ClickHouseValues {

public static final String TYPE_CLASS = "Class";

private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
private static final byte[] UNHEX_PREFIX = "unhex('".getBytes(StandardCharsets.US_ASCII);
private static final byte[] UNHEX_SUFFIX = "')".getBytes(StandardCharsets.US_ASCII);

/**
* Converts IP address to big integer.
*
Expand Down Expand Up @@ -182,6 +187,52 @@ public static BigInteger convertToBigInteger(UUID value) {
return low.add(high.multiply(BIGINT_HL_BOUNDARY));
}

/**
* Converts given byte array to string in hexadecimal format.
*
* @param bytes byte array
* @return non-null string
*/
public static String convertToHexString(byte[] bytes) {
int len = bytes != null ? bytes.length : 0;
if (len == 0) {
return "";
}

byte[] hexChars = new byte[len * 2];
for (int i = 0; i < len; i++) {
int v = bytes[i] & 0xFF;
int j = i * 2;
hexChars[j] = HEX_ARRAY[v >>> 4];
hexChars[j + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}

/**
* Converts given byte array to unhex() expression.
*
* @param bytes byte array
* @return non-null expression
*/
public static String convertToUnhexExpression(byte[] bytes) {
int len = bytes != null ? bytes.length : 0;
if (len == 0) {
return EMPTY_STRING_EXPR;
}

int offset = UNHEX_PREFIX.length;
byte[] hexChars = new byte[len * 2 + offset + UNHEX_SUFFIX.length];
System.arraycopy(UNHEX_PREFIX, 0, hexChars, 0, offset);
System.arraycopy(UNHEX_SUFFIX, 0, hexChars, hexChars.length - UNHEX_SUFFIX.length, UNHEX_SUFFIX.length);
for (int i = 0; i < len; i++) {
int v = bytes[i] & 0xFF;
hexChars[offset++] = HEX_ARRAY[v >>> 4];
hexChars[offset++] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}

/**
* Converts big decimal to instant.
*
Expand Down
Expand Up @@ -268,19 +268,9 @@ public ClickHouseStringValue resetToNullOrEmpty() {
public String toSqlExpression() {
if (isNullOrEmpty()) {
return ClickHouseValues.NULL_EXPR;
} else if (binary) {
return ClickHouseValues.convertToUnhexExpression(bytes);
}
// else if (binary) {
// int len = bytes.length;
// if (len == 0) {
// return ClickHouseValues.EMPTY_STRING_EXPR;
// }
// StringBuilder builder = new StringBuilder(len * 3 + 5).append("char(");
// for (byte b : bytes) {
// builder.append(b).append(',');
// }
// builder.setLength(builder.length() - 1);
// return builder.append(')').toString();
// }
return ClickHouseValues.convertToQuotedString(asString());
}

Expand Down
Expand Up @@ -73,7 +73,7 @@ public void testBinaryValue() {
Assert.assertEquals(ClickHouseStringValue.of("a").asBinary(), new byte[] { 97 });

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

@Test(groups = { "unit" })
Expand Down
@@ -1,12 +1,15 @@
package com.clickhouse.jdbc;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UncheckedIOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
Expand Down Expand Up @@ -217,14 +220,13 @@ public Array getArray(String columnLabel) throws SQLException {

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

@Override
public InputStream getAsciiStream(String columnLabel) throws SQLException {
// TODO Auto-generated method stub
return null;
return getAsciiStream(findColumn(columnLabel));
}

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

@Override
public InputStream getBinaryStream(int columnIndex) throws SQLException {
// TODO Auto-generated method stub
return null;
ClickHouseValue v = getValue(columnIndex);
return v.isNullOrEmpty() ? null : new ByteArrayInputStream(v.asBinary());
}

@Override
public InputStream getBinaryStream(String columnLabel) throws SQLException {
// TODO Auto-generated method stub
return null;
return getBinaryStream(findColumn(columnLabel));
}

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

@Override
public Blob getBlob(String columnLabel) throws SQLException {
// TODO Auto-generated method stub
return null;
return getBlob(findColumn(columnLabel));
}

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

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

@Override
public byte[] getBytes(String columnLabel) throws SQLException {
// TODO Auto-generated method stub
return null;
return getValue(findColumn(columnLabel)).asBinary();
}

@Override
public Reader getCharacterStream(int columnIndex) throws SQLException {
// TODO Auto-generated method stub
return null;
ClickHouseValue v = getValue(columnIndex);
return v.isNullOrEmpty() ? null : new StringReader(v.asString());
}

@Override
public Reader getCharacterStream(String columnLabel) throws SQLException {
// TODO Auto-generated method stub
return null;
return getCharacterStream(findColumn(columnLabel));
}

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

@Override
public Clob getClob(String columnLabel) throws SQLException {
// TODO Auto-generated method stub
return null;
return getClob(findColumn(columnLabel));
}

@Override
Expand Down Expand Up @@ -420,14 +416,12 @@ public ResultSetMetaData getMetaData() throws SQLException {

@Override
public Reader getNCharacterStream(int columnIndex) throws SQLException {
// TODO Auto-generated method stub
return null;
return getCharacterStream(columnIndex);
}

@Override
public Reader getNCharacterStream(String columnLabel) throws SQLException {
// TODO Auto-generated method stub
return null;
return getCharacterStream(findColumn(columnLabel));
}

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

@Override
public NClob getNClob(String columnLabel) throws SQLException {
// TODO Auto-generated method stub
return null;
return getNClob(findColumn(columnLabel));
}

@Override
Expand Down Expand Up @@ -662,14 +655,13 @@ public URL getURL(String columnLabel) throws SQLException {

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

@Override
public InputStream getUnicodeStream(String columnLabel) throws SQLException {
// TODO Auto-generated method stub
return null;
return getUnicodeStream(findColumn(columnLabel));
}

@Override
Expand Down
Expand Up @@ -34,6 +34,25 @@ private Object[][] getTypedParameters() {
LocalDateTime.of(2021, 11, 2, 2, 3, 4) } } };
}

@Test(groups = "integration")
public void testReadWriteBinaryString() throws SQLException {
Properties props = new Properties();
byte[] bytes = new byte[256];
for (int i = 0; i < 256; i++) {
bytes[i] = (byte) i;
}
try (ClickHouseConnection conn = newConnection(props);
PreparedStatement ps = conn.prepareStatement("select ?, ?")) {
ps.setBytes(1, bytes);
ps.setString(2, Integer.toString(bytes.length));
ResultSet rs = ps.executeQuery();
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getBytes(1), bytes);
Assert.assertEquals(rs.getInt(2), bytes.length);
Assert.assertFalse(rs.next());
}
}

@Test(groups = "integration")
public void testReadWriteDate() throws SQLException {
LocalDate d = LocalDate.of(2021, 3, 25);
Expand Down

0 comments on commit b11e807

Please sign in to comment.