Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
180 changes: 113 additions & 67 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,27 @@ public static Class<?> convertToJavaClass(ClickHouseDataType clickhouseType) {
return DATA_TYPE_CLASS_MAP.get(clickhouseType);
}

private static Class<?> unwrapPrimitiveType(Class<?> type) {
if (type == int.class) {
return Integer.class;
} else if (type == long.class) {
return Long.class;
} else if (type == boolean.class) {
return Boolean.class;
} else if (type == float.class) {
return Float.class;
} else if (type == double.class) {
return Double.class;
} else if (type == char.class) {
return Character.class;
} else if (type == byte.class) {
return Byte.class;
} else if (type == short.class) {
return Short.class;
}
return type;
}

public static Object convert(Object value, Class<?> type) throws SQLException {
return convert(value, type, null);
}
Expand All @@ -225,74 +246,100 @@ public static Object convert(Object value, Class<?> type, ClickHouseColumn colum
if (value == null || type == null) {
return value;
}

type = unwrapPrimitiveType(type);
if (type.isInstance(value)) {
return value;
}

if (value instanceof List<?>) {
List<?> listValue = (List<?>) value;
if (type != java.sql.Array.class) {
return convertList(listValue, type);
}

if (column != null && column.getArrayBaseColumn() != null) {
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
Object[] convertedValues = convertList(listValue, convertToJavaClass(baseType));
return new Array(column, convertedValues);
}

// base type is unknown. all objects should be converted
return new Array(column, listValue.toArray());
}

if (value.getClass().isArray()) {
if (type == java.sql.Array.class) {
return new Array(column, arrayToObjectArray(value));
} else if (type == Tuple.class) {
return new Tuple(true, value);
}
}

if (type == java.sql.Array.class && value instanceof BinaryStreamReader.ArrayValue) {
BinaryStreamReader.ArrayValue arrayValue = (BinaryStreamReader.ArrayValue) value;

if (column != null && column.getArrayBaseColumn() != null) {
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
Object[] convertedValues = convertArray(arrayValue.getArrayOfObjects(), convertToJavaClass(baseType));
return new Array(column, convertedValues);
}

return new Array(column, arrayValue.getArrayOfObjects());
}

return convertObject(value, type);
}

public static Object convertObject(Object value, Class<?> type) throws SQLException {
if (value == null || type == null) {
return value;
}
try {
if (type.isInstance(value)) {
return value;
} else if (type != java.sql.Array.class && value instanceof List<?>) {
return convertList((List<?>) value, type);
} else if (type == String.class) {
if (type == String.class) {
return value.toString();
} else if (type == Boolean.class || type == boolean.class) {
} else if (type == Boolean.class) {
String str = value.toString();
return !("false".equalsIgnoreCase(str) || "0".equalsIgnoreCase(str));
} else if (type == Byte.class || type == byte.class) {
} else if (type == Byte.class) {
return Byte.parseByte(value.toString());
} else if (type == Short.class || type == short.class) {
} else if (type == Short.class) {
return Short.parseShort(value.toString());
} else if (type == Integer.class || type == int.class) {
} else if (type == Integer.class) {
return Integer.parseInt(value.toString());
} else if (type == Long.class || type == long.class) {
} else if (type == Long.class) {
return Long.parseLong(value.toString());
} else if (type == Float.class || type == float.class) {
} else if (type == Float.class) {
return Float.parseFloat(value.toString());
} else if (type == Double.class || type == double.class) {
} else if (type == Double.class) {
return Double.parseDouble(value.toString());
} else if (type == java.math.BigDecimal.class) {
return new java.math.BigDecimal(value.toString());
} else if (type == byte[].class) {
return value.toString().getBytes();
} else if (type == LocalDate.class && value instanceof TemporalAccessor) {
return LocalDate.from((TemporalAccessor) value);
} else if (type == LocalDateTime.class && value instanceof TemporalAccessor) {
return LocalDateTime.from((TemporalAccessor) value);
} else if (type == OffsetDateTime.class && value instanceof TemporalAccessor) {
return OffsetDateTime.from((TemporalAccessor) value);
} else if (type == ZonedDateTime.class && value instanceof TemporalAccessor) {
return ZonedDateTime.from((TemporalAccessor) value);
} else if (type == Instant.class && value instanceof TemporalAccessor) {
return Instant.from((TemporalAccessor) value);
} else if (type == Date.class && value instanceof TemporalAccessor) {
return Date.valueOf(LocalDate.from((TemporalAccessor) value));
} else if (type == java.sql.Timestamp.class && value instanceof TemporalAccessor) {
return java.sql.Timestamp.valueOf(LocalDateTime.from((TemporalAccessor) value));
} else if (type == java.sql.Time.class && value instanceof TemporalAccessor) {
return java.sql.Time.valueOf(LocalTime.from((TemporalAccessor) value));
} else if (type == java.sql.Array.class && value instanceof BinaryStreamReader.ArrayValue) {//It's cleaner to use getList but this handles the more generic getObject
BinaryStreamReader.ArrayValue arrayValue = (BinaryStreamReader.ArrayValue) value;
if (column != null && column.getArrayBaseColumn() != null) {
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
Object[] convertedValues = convertArray(arrayValue.getArrayOfObjects(), JdbcUtils.convertToJavaClass(baseType));
return new Array(column, convertedValues);
} else if (value instanceof TemporalAccessor) {
TemporalAccessor temporalValue = (TemporalAccessor) value;
if (type == LocalDate.class) {
return LocalDate.from(temporalValue);
} else if (type == LocalDateTime.class) {
return LocalDateTime.from(temporalValue);
} else if (type == OffsetDateTime.class) {
return OffsetDateTime.from(temporalValue);
} else if (type == ZonedDateTime.class) {
return ZonedDateTime.from(temporalValue);
} else if (type == Instant.class) {
return Instant.from(temporalValue);
} else if (type == Date.class) {
return Date.valueOf(LocalDate.from(temporalValue));
} else if (type == java.sql.Timestamp.class) {
return java.sql.Timestamp.valueOf(LocalDateTime.from(temporalValue));
} else if (type == java.sql.Time.class) {
return java.sql.Time.valueOf(LocalTime.from(temporalValue));
}
return new Array(column, arrayValue.getArrayOfObjects());
} else if (type == java.sql.Array.class && value instanceof List<?>) {
if (column != null && column.getArrayBaseColumn() != null) {
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
Object[] convertedValues = convertList((List<?>) value, JdbcUtils.convertToJavaClass(baseType));
return new Array(column, convertedValues);
}
// base type is unknown. all objects should be converted
return new Array(column, ((List<?>) value).toArray());
} else if (type == java.sql.Array.class && value.getClass().isArray()) {
return new Array(column, arrayToObjectArray(value));
} else if (type == Inet4Address.class && value instanceof Inet6Address) {
// Convert Inet6Address to Inet4Address
return InetAddressConverter.convertToIpv4((InetAddress) value);
} else if (type == Inet6Address.class && value instanceof Inet4Address) {
// Convert Inet4Address to Inet6Address
return InetAddressConverter.convertToIpv6((InetAddress) value);
} else if (type == Tuple.class && value.getClass().isArray()) {
return new Tuple(true, value);
}
} catch (Exception e) {
throw new SQLException("Failed to convert from " + value.getClass().getName() + " to " + type.getName(), ExceptionUtils.SQL_STATE_DATA_EXCEPTION, e);
Expand All @@ -301,28 +348,27 @@ public static Object convert(Object value, Class<?> type, ClickHouseColumn colum
throw new SQLException("Unsupported conversion from " + value.getClass().getName() + " to " + type.getName(), ExceptionUtils.SQL_STATE_DATA_EXCEPTION);
}

public static Object[] convertList(List<?> values, Class<?> type) throws SQLException {
public static <T> T[] convertList(List<?> values, Class<T> type) throws SQLException {
if (values == null) {
return null;
}
if (values.isEmpty()) {
return new Object[0];
return (T[]) java.lang.reflect.Array.newInstance(type, 0);
}

Object[] convertedValues = new Object[values.size()];
T[] convertedValues = (T[]) java.lang.reflect.Array.newInstance(type, values.size());
for (int i = 0; i < values.size(); i++) {
convertedValues[i] = convert(values.get(i), type);
convertedValues[i] = (T) convert(values.get(i), type);
}
return convertedValues;
}

public static Object[] convertArray(Object[] values, Class<?> type) throws SQLException {
if (values == null || type == null) {
return values;
public static <T> T[] convertArray(Object[] values, Class<T> type) throws SQLException {
if (values == null) {
return null;
}
Object[] convertedValues = new Object[values.length];
T[] convertedValues = (T[]) java.lang.reflect.Array.newInstance(type, values.length);
for (int i = 0; i < values.length; i++) {
convertedValues[i] = convert(values[i], type);
convertedValues[i] = (T) convert(values[i], type);
}
return convertedValues;
}
Expand All @@ -340,56 +386,56 @@ private static Object[] arrayToObjectArray(Object array) {

if (array instanceof byte[]) {
byte[] src = (byte[]) array;
Object[] dst = new Object[src.length];
Byte[] dst = new Byte[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof short[]) {
short[] src = (short[]) array;
Object[] dst = new Object[src.length];
Short[] dst = new Short[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof int[]) {
int[] src = (int[]) array;
Object[] dst = new Object[src.length];
Integer[] dst = new Integer[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof long[]) {
long[] src = (long[]) array;
Object[] dst = new Object[src.length];
Long[] dst = new Long[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof float[]) {
float[] src = (float[]) array;
Object[] dst = new Object[src.length];
Float[] dst = new Float[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof double[]) {
double[] src = (double[]) array;
Object[] dst = new Object[src.length];
Double[] dst = new Double[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof char[]) {
char[] src = (char[]) array;
Object[] dst = new Object[src.length];
Character[] dst = new Character[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof boolean[]) {
boolean[] src = (boolean[]) array;
Object[] dst = new Object[src.length];
Boolean[] dst = new Boolean[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ public class JdbcUtilsTest {

@Test(groups = {"unit"})
public void testConvertPrimitiveTypes() throws SQLException {
assertEquals(JdbcUtils.convert(1, int.class), 1);
assertEquals(JdbcUtils.convert(1L, long.class), 1L);
assertEquals(JdbcUtils.convert(1, int.class), Integer.valueOf(1));
assertEquals(JdbcUtils.convert(1L, long.class), Long.valueOf(1));
assertEquals(JdbcUtils.convert("1", String.class), "1");
assertEquals(JdbcUtils.convert(1.0f, float.class), 1.0f);
assertEquals(JdbcUtils.convert(1.0, double.class), 1.0);
assertEquals(JdbcUtils.convert(true, boolean.class), true);
assertEquals(JdbcUtils.convert((short) 1, short.class), (short) 1);
assertEquals(JdbcUtils.convert((byte) 1, byte.class), (byte) 1);
assertEquals(JdbcUtils.convert(1.0d, BigDecimal.class), BigDecimal.valueOf(1.0d));
assertEquals(JdbcUtils.convert(1.0f, float.class), Float.valueOf(1));
assertEquals(JdbcUtils.convert(1.0, double.class), Double.valueOf(1));
assertEquals(JdbcUtils.convert(true, boolean.class), Boolean.TRUE);
assertEquals(JdbcUtils.convert((short) 1, short.class), Short.valueOf("1"));
assertEquals(JdbcUtils.convert((byte) 1, byte.class), Byte.valueOf("1"));
assertEquals(JdbcUtils.convert(1.0d, BigDecimal.class), new BigDecimal("1.0"));
}


Expand All @@ -40,32 +40,34 @@ public void testConvertToArray() throws Exception {
java.sql.Array array = (java.sql.Array) JdbcUtils.convert(arrayValue, java.sql.Array.class, column);
Object arr = array.getArray();
assertEquals(array.getBaseTypeName(), "Int32");
assertEquals(arr.getClass().getComponentType(), Object.class);
Object[] arrs = (Object[]) arr;
assertEquals(arr.getClass().getComponentType(), Integer.class);
Integer[] arrs = (Integer[]) arr;
assertEquals(arrs[0], 1);
assertEquals(arrs[1], 2);
}


@Test(groups = {"unit"})
public void testConvertArray() throws Exception {
Object[] src = {1, 2, 3};
Object[] dst = JdbcUtils.convertArray(src, int.class);
assertEquals(dst.length, src.length);
assertEquals(dst[0], src[0]);
assertEquals(dst[1], src[1]);
assertEquals(dst[2], src[2]);

assertNull(JdbcUtils.convertArray(null, int.class));
assertEquals(JdbcUtils.convertArray(new Integer[] { 1, 2}, null), new Integer[] { 1, 2});
// primitive classes are unwrapped
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Boolean.class), new Boolean[] { false, true });
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Byte.class), new Byte[] { 0, 1 });
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Short.class), new Short[] { 0, 1 });
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Integer.class), new Integer[] { 0, 1 });
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Long.class), new Long[] { 0L, 1L });
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Float.class), new Float[] { 0.0f, 1.0f });
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, Double.class), new Double[] { 0.0, 1.0 });
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, String.class), new String[] { "0", "1" });
assertEquals(JdbcUtils.convertArray(new Object[] { 0, 1 }, BigDecimal.class), new BigDecimal[] { BigDecimal.valueOf(0), BigDecimal.valueOf(1) });
assertNull(JdbcUtils.convertArray(null, Integer.class));
}


@Test(groups = {"unit"})
public void testConvertList() throws Exception {
ClickHouseColumn column = ClickHouseColumn.of("arr", "Array(Int32)");
List<Integer> src = Arrays.asList(1, 2, 3);
Object[] dst = JdbcUtils.convertList(src, Integer.class);
Integer[] dst = JdbcUtils.convertList(src, Integer.class);
assertEquals(dst.length, src.size());
assertEquals(dst[0], src.get(0));
assertEquals(dst[1], src.get(1));
Expand Down