Skip to content

Commit f000e1f

Browse files
authored
Merge pull request #2581 from ClickHouse/fix_spatial_data
[JDBC-V2] Fixed spatial data returning correct type and object
2 parents bddb45f + a9d9b44 commit f000e1f

File tree

6 files changed

+465
-52
lines changed

6 files changed

+465
-52
lines changed

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

Lines changed: 1 addition & 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;

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -929,9 +929,9 @@ public String encodeArray(Object[] elements, int levels, ClickHouseDataType elem
929929
} else if (cursor.arrayAsTuple) {
930930
arraySb.append(encodeTuple((Object[]) element)).append(',');
931931
cursor.pos++;
932-
} else if (cursor.level == 1 && elementType == ClickHouseDataType.Tuple && element instanceof Array ) {
932+
} else if (cursor.level == 1 && isTupleType(elementType) && element instanceof Array ) {
933933
cursor.arrayObjAsTuple = true;
934-
} else if (cursor.level == 1 && elementType == ClickHouseDataType.Tuple && element instanceof Object[] ) {
934+
} else if (cursor.level == 1 && isTupleType(elementType) && element instanceof Object[] ) {
935935
cursor.arrayAsTuple = true;
936936
} else if (cursor.level == 1) {
937937
arraySb.append(encodeObject(element)).append(',');
@@ -947,6 +947,10 @@ public String encodeArray(Object[] elements, int levels, ClickHouseDataType elem
947947
return arraySb.toString();
948948
}
949949

950+
private static boolean isTupleType(ClickHouseDataType type ) {
951+
return type == ClickHouseDataType.Tuple || type == ClickHouseDataType.Point;
952+
}
953+
950954
private static final class ArrayProcessingCursor {
951955
Object[] array; // current array
952956
int pos; // processing position

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

Lines changed: 54 additions & 41 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;
@@ -35,6 +35,7 @@
3535
import java.sql.Timestamp;
3636
import java.time.ZonedDateTime;
3737
import java.util.Calendar;
38+
import java.util.Collections;
3839
import java.util.Map;
3940
import java.util.function.Consumer;
4041

@@ -481,16 +482,6 @@ protected void setMetaData(ResultSetMetaDataImpl metaData) {
481482
this.metaData = metaData;
482483
}
483484

484-
@Override
485-
public Object getObject(int columnIndex) throws SQLException {
486-
return getObject(columnIndex, JdbcUtils.convertToJavaClass(getSchema().getColumnByIndex(columnIndex).getDataType()));
487-
}
488-
489-
@Override
490-
public Object getObject(String columnLabel) throws SQLException {
491-
return getObject(columnLabel, JdbcUtils.convertToJavaClass(getSchema().getColumnByName(columnLabel).getDataType()));
492-
}
493-
494485
@Override
495486
public int findColumn(String columnLabel) throws SQLException {
496487
checkClosed();
@@ -962,12 +953,6 @@ public Statement getStatement() throws SQLException {
962953
return this.parentStatement;
963954
}
964955

965-
@Override
966-
public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException {
967-
ClickHouseDataType type = getSchema().getColumnByIndex(columnIndex).getDataType();
968-
return getObject(columnIndex, map.get(JdbcUtils.convertToSqlType(type).getName()));
969-
}
970-
971956
@Override
972957
public Ref getRef(int columnIndex) throws SQLException {
973958
return getRef(columnIndexToName(columnIndex));
@@ -988,12 +973,6 @@ public java.sql.Array getArray(int columnIndex) throws SQLException {
988973
return getObject(columnIndex, java.sql.Array.class);
989974
}
990975

991-
@Override
992-
public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException {
993-
checkClosed();
994-
return getObject(columnLabel, map.get(JdbcUtils.convertToSqlType(getSchema().getColumnByName(columnLabel).getDataType()).getName()));
995-
}
996-
997976
@Override
998977
public Ref getRef(String columnLabel) throws SQLException {
999978
checkClosed();
@@ -1437,38 +1416,72 @@ public void updateNClob(String columnLabel, Reader reader) throws SQLException {
14371416
}
14381417

14391418
@Override
1440-
public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
1419+
public Object getObject(int columnIndex) throws SQLException {
1420+
return getObject(columnIndexToName(columnIndex));
1421+
}
1422+
1423+
@Override
1424+
public Object getObject(String columnLabel) throws SQLException {
1425+
return getObjectImpl(columnLabel, null, Collections.emptyMap());
1426+
}
1427+
1428+
@Override
1429+
public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException {
1430+
return getObject(columnIndexToName(columnIndex), map);
1431+
}
1432+
1433+
@Override
1434+
public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException {
14411435
checkClosed();
1442-
try {
1443-
if (reader.hasValue(columnIndex)) {
1444-
wasNull = false;
1445-
if (type == null) {//As a fallback, try to get the value as is
1446-
return reader.readValue(columnIndex);
1447-
}
1436+
return getObjectImpl(columnLabel, null, map);
1437+
}
14481438

1449-
return (T) JdbcUtils.convert(reader.readValue(columnIndex), type, type == java.sql.Array.class ? getSchema().getColumnByIndex(columnIndex) : null);
1450-
} else {
1451-
wasNull = true;
1452-
return null;
1453-
}
1454-
} catch (Exception e) {
1455-
throw ExceptionUtils.toSqlState(String.format("Method: getObject(\"%s\", %s) encountered an exception.",
1456-
reader.getSchema().columnIndexToName(columnIndex), type),
1457-
String.format("SQL: [%s]", parentStatement.getLastStatementSql()), e);
1458-
}
1439+
@Override
1440+
public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
1441+
checkClosed();
1442+
return getObject(columnIndexToName(columnIndex), type);
14591443
}
14601444

14611445
@Override
14621446
public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
14631447
checkClosed();
1448+
return getObjectImpl(columnLabel, type, Collections.emptyMap());
1449+
}
1450+
1451+
@SuppressWarnings("unchecked")
1452+
public <T> T getObjectImpl(String columnLabel, Class<?> type, Map<String, Class<?>> typeMap) throws SQLException {
14641453
try {
1454+
ClickHouseColumn column = getSchema().getColumnByName(columnLabel);
1455+
if (column == null) {
1456+
throw new SQLException("Column \"" + columnLabel + "\" does not exist.");
1457+
}
1458+
14651459
if (reader.hasValue(columnLabel)) {
14661460
wasNull = false;
1461+
1462+
if (type == null) {
1463+
switch (column.getDataType()) {
1464+
case Point:
1465+
case Ring:
1466+
case LineString:
1467+
case Polygon:
1468+
case MultiPolygon:
1469+
case MultiLineString:
1470+
break; // read as is
1471+
default:
1472+
if (typeMap == null || typeMap.isEmpty()) {
1473+
type = JdbcUtils.convertToJavaClass(column.getDataType());
1474+
} else {
1475+
type = typeMap.get(JdbcUtils.convertToSqlType(column.getDataType()).getName());
1476+
}
1477+
}
1478+
}
1479+
14671480
if (type == null) {//As a fallback, try to get the value as is
14681481
return reader.readValue(columnLabel);
14691482
}
14701483

1471-
return (T) JdbcUtils.convert(reader.readValue(columnLabel), type, type == java.sql.Array.class ? getSchema().getColumnByName(columnLabel) : null);
1484+
return (T) JdbcUtils.convert(reader.readValue(columnLabel), type, column);
14721485
} else {
14731486
wasNull = true;
14741487
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
}

0 commit comments

Comments
 (0)