Skip to content

Commit 84f9e7e

Browse files
committed
Fixed spatial data returning correct type and object
1 parent b319fd6 commit 84f9e7e

File tree

5 files changed

+228
-48
lines changed

5 files changed

+228
-48
lines changed

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import java.util.List;
5353
import java.util.Locale;
5454
import java.util.Properties;
55+
import java.util.ServiceLoader;
5556
import java.util.TimeZone;
5657
import java.util.UUID;
5758
import java.util.concurrent.CountDownLatch;
@@ -1554,4 +1555,36 @@ public void testVariantDataType() throws SQLException {
15541555
}
15551556
}
15561557
}
1558+
1559+
@Test(groups = "integration")
1560+
public void testSpatialData() {
1561+
final String spatialQuery = "select \n" +
1562+
"\tcast(arrayJoin([(4.837388, 52.38795),\n" +
1563+
"\t\t\t(4.951513, 52.354582),\n" +
1564+
"\t\t\t(4.961987, 52.371763),\n" +
1565+
"\t\t\t(4.870017, 52.334932),\n" +
1566+
"\t\t\t(4.89813, 52.357238),\n" +
1567+
"\t\t\t(4.852437, 52.370315),\n" +
1568+
"\t\t\t(4.901712, 52.369567),\n" +
1569+
"\t\t\t(4.874112, 52.339823),\n" +
1570+
"\t\t\t(4.856942, 52.339122),\n" +
1571+
"\t\t\t(4.870253, 52.360353)]\n" +
1572+
"\t\t\t)\n" +
1573+
"\t\tas Point) as Point";
1574+
try (ClickHouseConnection conn = newConnection();
1575+
ClickHouseStatement stmt = conn.createStatement()) {
1576+
ResultSet rs = stmt.executeQuery(spatialQuery);
1577+
rs.next();
1578+
ResultSetMetaData metaData = rs.getMetaData();
1579+
String columnTypeName = metaData.getColumnTypeName(1);
1580+
int columnType = metaData.getColumnType(1);
1581+
Array asArray = rs.getArray(1);
1582+
Object asObject = rs.getObject(1);
1583+
String asString = rs.getString(1);
1584+
Assert.assertEquals(metaData.getColumnCount(), 7);
1585+
} catch (Exception e) {
1586+
e.printStackTrace();
1587+
Assert.fail("Failed to create connection", e);
1588+
}
1589+
}
15571590
}

jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.clickhouse.client.api.data_formats.ClickHouseBinaryFormatReader;
44
import com.clickhouse.client.api.metadata.TableSchema;
55
import com.clickhouse.client.api.query.QueryResponse;
6-
import com.clickhouse.data.ClickHouseDataType;
6+
import com.clickhouse.data.ClickHouseColumn;
77
import com.clickhouse.jdbc.internal.ExceptionUtils;
88
import com.clickhouse.jdbc.internal.FeatureManager;
99
import com.clickhouse.jdbc.internal.JdbcUtils;
@@ -16,7 +16,6 @@
1616
import java.io.Reader;
1717
import java.io.StringReader;
1818
import java.math.BigDecimal;
19-
import java.net.SocketTimeoutException;
2019
import java.net.URL;
2120
import java.nio.charset.StandardCharsets;
2221
import java.sql.Blob;
@@ -36,6 +35,7 @@
3635
import java.sql.Timestamp;
3736
import java.time.ZonedDateTime;
3837
import java.util.Calendar;
38+
import java.util.Collections;
3939
import java.util.Map;
4040
import java.util.function.Consumer;
4141

@@ -465,16 +465,6 @@ protected void setMetaData(ResultSetMetaDataImpl metaData) {
465465
this.metaData = metaData;
466466
}
467467

468-
@Override
469-
public Object getObject(int columnIndex) throws SQLException {
470-
return getObject(columnIndex, JdbcUtils.convertToJavaClass(getSchema().getColumnByIndex(columnIndex).getDataType()));
471-
}
472-
473-
@Override
474-
public Object getObject(String columnLabel) throws SQLException {
475-
return getObject(columnLabel, JdbcUtils.convertToJavaClass(getSchema().getColumnByName(columnLabel).getDataType()));
476-
}
477-
478468
@Override
479469
public int findColumn(String columnLabel) throws SQLException {
480470
checkClosed();
@@ -945,12 +935,6 @@ public Statement getStatement() throws SQLException {
945935
return this.parentStatement;
946936
}
947937

948-
@Override
949-
public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException {
950-
ClickHouseDataType type = getSchema().getColumnByIndex(columnIndex).getDataType();
951-
return getObject(columnIndex, map.get(JdbcUtils.convertToSqlType(type).getName()));
952-
}
953-
954938
@Override
955939
public Ref getRef(int columnIndex) throws SQLException {
956940
return getRef(columnIndexToName(columnIndex));
@@ -971,12 +955,6 @@ public java.sql.Array getArray(int columnIndex) throws SQLException {
971955
return getObject(columnIndex, java.sql.Array.class);
972956
}
973957

974-
@Override
975-
public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException {
976-
checkClosed();
977-
return getObject(columnLabel, map.get(JdbcUtils.convertToSqlType(getSchema().getColumnByName(columnLabel).getDataType()).getName()));
978-
}
979-
980958
@Override
981959
public Ref getRef(String columnLabel) throws SQLException {
982960
checkClosed();
@@ -1420,38 +1398,71 @@ public void updateNClob(String columnLabel, Reader reader) throws SQLException {
14201398
}
14211399

14221400
@Override
1423-
public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
1401+
public Object getObject(int columnIndex) throws SQLException {
1402+
return getObject(columnIndexToName(columnIndex));
1403+
}
1404+
1405+
@Override
1406+
public Object getObject(String columnLabel) throws SQLException {
1407+
return getObjectImpl(columnLabel, null, Collections.emptyMap());
1408+
}
1409+
1410+
@Override
1411+
public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException {
1412+
return getObject(columnIndexToName(columnIndex), map);
1413+
}
1414+
1415+
@Override
1416+
public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException {
14241417
checkClosed();
1425-
try {
1426-
if (reader.hasValue(columnIndex)) {
1427-
wasNull = false;
1428-
if (type == null) {//As a fallback, try to get the value as is
1429-
return reader.readValue(columnIndex);
1430-
}
1418+
return getObjectImpl(columnLabel, null, map);
1419+
}
14311420

1432-
return (T) JdbcUtils.convert(reader.readValue(columnIndex), type, type == java.sql.Array.class ? getSchema().getColumnByIndex(columnIndex) : null);
1433-
} else {
1434-
wasNull = true;
1435-
return null;
1436-
}
1437-
} catch (Exception e) {
1438-
throw ExceptionUtils.toSqlState(String.format("Method: getObject(\"%s\", %s) encountered an exception.",
1439-
reader.getSchema().columnIndexToName(columnIndex), type),
1440-
String.format("SQL: [%s]", parentStatement.getLastStatementSql()), e);
1441-
}
1421+
@Override
1422+
public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
1423+
checkClosed();
1424+
return getObject(columnIndexToName(columnIndex), type);
14421425
}
14431426

14441427
@Override
14451428
public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
14461429
checkClosed();
1430+
return getObjectImpl(columnLabel, type, Collections.emptyMap());
1431+
}
1432+
1433+
@SuppressWarnings("unchecked")
1434+
public <T> T getObjectImpl(String columnLabel, Class<?> type, Map<String, Class<?>> typeMap) throws SQLException {
14471435
try {
1436+
ClickHouseColumn column = getSchema().getColumnByName(columnLabel);
1437+
if (column == null) {
1438+
throw new SQLException("Column \"" + columnLabel + "\" does not exist.");
1439+
}
1440+
14481441
if (reader.hasValue(columnLabel)) {
14491442
wasNull = false;
1443+
1444+
if (type == null) {
1445+
switch (column.getDataType()) {
1446+
case Point:
1447+
case Ring:
1448+
case LineString:
1449+
case MultiPolygon:
1450+
case MultiLineString:
1451+
break; // read as is
1452+
default:
1453+
if (typeMap == null || typeMap.isEmpty()) {
1454+
type = JdbcUtils.convertToJavaClass(column.getDataType());
1455+
} else {
1456+
type = typeMap.get(JdbcUtils.convertToSqlType(column.getDataType()).getName());
1457+
}
1458+
}
1459+
}
1460+
14501461
if (type == null) {//As a fallback, try to get the value as is
14511462
return reader.readValue(columnLabel);
14521463
}
14531464

1454-
return (T) JdbcUtils.convert(reader.readValue(columnLabel), type, type == java.sql.Array.class ? getSchema().getColumnByName(columnLabel) : null);
1465+
return (T) JdbcUtils.convert(reader.readValue(columnLabel), type, column);
14551466
} else {
14561467
wasNull = true;
14571468
return null;

jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.time.chrono.ChronoZonedDateTime;
2525
import java.time.temporal.TemporalAccessor;
2626
import java.util.ArrayList;
27+
import java.util.Arrays;
2728
import java.util.Collections;
2829
import java.util.EnumSet;
2930
import java.util.HashMap;
@@ -78,12 +79,12 @@ private static Map<ClickHouseDataType, SQLType> generateTypeMap() {
7879
map.put(ClickHouseDataType.Array, JDBCType.ARRAY);
7980
map.put(ClickHouseDataType.Nested, JDBCType.ARRAY);
8081
map.put(ClickHouseDataType.Map, JDBCType.JAVA_OBJECT);
81-
map.put(ClickHouseDataType.Point, JDBCType.OTHER);
82-
map.put(ClickHouseDataType.Ring, JDBCType.OTHER);
83-
map.put(ClickHouseDataType.Polygon, JDBCType.OTHER);
84-
map.put(ClickHouseDataType.LineString, JDBCType.OTHER);
85-
map.put(ClickHouseDataType.MultiPolygon, JDBCType.OTHER);
86-
map.put(ClickHouseDataType.MultiLineString, JDBCType.OTHER);
82+
map.put(ClickHouseDataType.Point, JDBCType.ARRAY);
83+
map.put(ClickHouseDataType.Ring, JDBCType.ARRAY);
84+
map.put(ClickHouseDataType.Polygon, JDBCType.ARRAY);
85+
map.put(ClickHouseDataType.LineString, JDBCType.ARRAY);
86+
map.put(ClickHouseDataType.MultiPolygon, JDBCType.ARRAY);
87+
map.put(ClickHouseDataType.MultiLineString, JDBCType.ARRAY);
8788
return ImmutableMap.copyOf(map);
8889
}
8990

@@ -281,6 +282,8 @@ public static Object convert(Object value, Class<?> type, ClickHouseColumn colum
281282
}
282283
// base type is unknown. all objects should be converted
283284
return new Array(column, ((List<?>) value).toArray());
285+
} else if (type == java.sql.Array.class && value.getClass().isArray()) {
286+
return new Array(column, arrayToObjectArray(value));
284287
} else if (type == Inet4Address.class && value instanceof Inet6Address) {
285288
// Convert Inet6Address to Inet4Address
286289
return InetAddressConverter.convertToIpv4((InetAddress) value);
@@ -322,4 +325,75 @@ public static Object[] convertArray(Object[] values, Class<?> type) throws SQLEx
322325
}
323326
return convertedValues;
324327
}
328+
329+
private static Object[] arrayToObjectArray(Object array) {
330+
if (array == null) {
331+
return null;
332+
}
333+
if (array instanceof Object[]) {
334+
return (Object[]) array;
335+
}
336+
if (!array.getClass().isArray()) {
337+
throw new IllegalArgumentException("Not an array: " + array.getClass().getName());
338+
}
339+
340+
if (array instanceof byte[]) {
341+
byte[] src = (byte[]) array;
342+
Object[] dst = new Object[src.length];
343+
for (int i = 0; i < src.length; i++) {
344+
dst[i] = src[i];
345+
}
346+
return dst;
347+
} else if (array instanceof short[]) {
348+
short[] src = (short[]) array;
349+
Object[] dst = new Object[src.length];
350+
for (int i = 0; i < src.length; i++) {
351+
dst[i] = src[i];
352+
}
353+
return dst;
354+
} else if (array instanceof int[]) {
355+
int[] src = (int[]) array;
356+
Object[] dst = new Object[src.length];
357+
for (int i = 0; i < src.length; i++) {
358+
dst[i] = src[i];
359+
}
360+
return dst;
361+
} else if (array instanceof long[]) {
362+
long[] src = (long[]) array;
363+
Object[] dst = new Object[src.length];
364+
for (int i = 0; i < src.length; i++) {
365+
dst[i] = src[i];
366+
}
367+
return dst;
368+
} else if (array instanceof float[]) {
369+
float[] src = (float[]) array;
370+
Object[] dst = new Object[src.length];
371+
for (int i = 0; i < src.length; i++) {
372+
dst[i] = src[i];
373+
}
374+
return dst;
375+
} else if (array instanceof double[]) {
376+
double[] src = (double[]) array;
377+
Object[] dst = new Object[src.length];
378+
for (int i = 0; i < src.length; i++) {
379+
dst[i] = src[i];
380+
}
381+
return dst;
382+
} else if (array instanceof char[]) {
383+
char[] src = (char[]) array;
384+
Object[] dst = new Object[src.length];
385+
for (int i = 0; i < src.length; i++) {
386+
dst[i] = src[i];
387+
}
388+
return dst;
389+
} else if (array instanceof boolean[]) {
390+
boolean[] src = (boolean[]) array;
391+
Object[] dst = new Object[src.length];
392+
for (int i = 0; i < src.length; i++) {
393+
dst[i] = src[i];
394+
}
395+
return dst;
396+
}
397+
throw new IllegalArgumentException("Cannot convert " + array.getClass().getName() + " to an Object[]");
398+
}
325399
}

jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,16 @@ private void ensureValid() throws SQLException {
118118
throw ExceptionUtils.toSqlState(new SQLFeatureNotSupportedException("Array is not valid. Possible free() was called."));
119119
}
120120
}
121+
122+
@Override
123+
public boolean equals(Object obj) {
124+
if (this == obj) {
125+
return true;
126+
}
127+
if (obj == null || getClass() != obj.getClass()) {
128+
return false;
129+
}
130+
Array other = (Array) obj;
131+
return type == other.type && java.util.Arrays.equals(array, other.array);
132+
}
121133
}

jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.sql.SQLException;
3030
import java.sql.Statement;
3131
import java.sql.Timestamp;
32+
import java.sql.Types;
3233
import java.text.DecimalFormat;
3334
import java.time.Instant;
3435
import java.time.LocalDate;
@@ -1651,4 +1652,53 @@ public void testVariantTypesSimpleStatement() throws SQLException {
16511652
}
16521653
}
16531654
}
1655+
1656+
@Test(groups = { "integration" })
1657+
public void testSpatialData() throws Exception {
1658+
final Double[][] spatialArrayData = new Double[][] {
1659+
{4.837388, 52.38795},
1660+
{4.951513, 52.354582},
1661+
{4.961987, 52.371763},
1662+
{4.870017, 52.334932},
1663+
{4.89813, 52.357238},
1664+
{4.852437, 52.370315},
1665+
{4.901712, 52.369567},
1666+
{4.874112, 52.339823},
1667+
{4.856942, 52.339122},
1668+
{4.870253, 52.360353},
1669+
};
1670+
1671+
StringBuilder sql = new StringBuilder();
1672+
sql.append("SELECT \n");
1673+
sql.append("\tcast(arrayJoin([");
1674+
for (int i = 0; i < spatialArrayData.length; i++) {
1675+
sql.append("(" + spatialArrayData[i][0] + ", " + spatialArrayData[i][1] + ")").append(',');
1676+
}
1677+
sql.setLength(sql.length() - 1);
1678+
sql.append("])");
1679+
sql.append("as Point) as Point");
1680+
1681+
1682+
1683+
try (Connection conn = getJdbcConnection(); Statement stmt = conn.createStatement()) {
1684+
try (ResultSet rs = stmt.executeQuery(sql.toString())) {
1685+
1686+
ResultSetMetaData metaData = rs.getMetaData();
1687+
assertEquals(metaData.getColumnCount(), 1);
1688+
assertEquals(metaData.getColumnTypeName(1), "Point");
1689+
assertEquals(metaData.getColumnType(1), Types.ARRAY);
1690+
1691+
int rowCount = 0;
1692+
while (rs.next()) {
1693+
Object asObject = rs.getObject(1);
1694+
assertTrue(asObject instanceof double[]);
1695+
Array asArray = rs.getArray(1);
1696+
assertEquals(asArray.getArray(), spatialArrayData[rowCount]);
1697+
assertEquals(asObject, asArray.getArray());
1698+
rowCount++;
1699+
}
1700+
assertTrue(rowCount > 0);
1701+
}
1702+
}
1703+
}
16541704
}

0 commit comments

Comments
 (0)