Skip to content
Draft
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

- **[jdbc-v2]** Added `cluster_name` configuration property to specify a target cluster for statements like `KILL QUERY` that require an `ON CLUSTER` clause to execute across all nodes. (https://github.com/ClickHouse/clickhouse-java/issues/2837)

- **[client-v2, jdbc-v2]** Added support for ClickHouse `Geometry` type for ClickHouse `25.11+`, where `Geometry` changed from a `String` alias to `Variant(Point, Ring, LineString, MultiLineString, Polygon, MultiPolygon)` (client still compatible with older versions). Includes client read/write handling and JDBC type mapping for retrieving and inserting geometry values. Current writes infer the target geometry variant from array nesting depth, so `Ring` vs `LineString` and `Polygon` vs `MultiLineString` are not yet distinguishable through the generic `Geometry` write path. (https://github.com/ClickHouse/clickhouse-java/pull/2815)

### Bug Fixes

- **[client-v2]** Fixed inconsistent use of `executionTimeout` parameter in `Client` component. The timeout was previously set in milliseconds but mistakenly retrieved and used in seconds in some places. Now it correctly uses milliseconds consistently. (https://github.com/ClickHouse/clickhouse-java/issues/2358)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
/**
* This class represents a column defined in database.
*/
@SuppressWarnings("deprecation")
public final class ClickHouseColumn implements Serializable {
public static final String TYPE_NAME = "Column";
public static final ClickHouseColumn[] EMPTY_ARRAY = new ClickHouseColumn[0];
Expand Down Expand Up @@ -105,6 +106,8 @@ public final class ClickHouseColumn implements Serializable {

private Map<Class<?>, Integer> arrayToVariantOrdNumMap;

private Map<Integer, Integer> geometryTypeDimensionsToVariantOrdNumMap;

private Map<Class<?>, Integer> mapKeyToVariantOrdNumMap;
private Map<Class<?>, Integer> mapValueToVariantOrdNumMap;

Expand Down Expand Up @@ -293,12 +296,28 @@ private static ClickHouseColumn update(ClickHouseColumn column) {
case Ring:
column.template = ClickHouseGeoRingValue.ofEmpty();
break;
case LineString:
column.template = ClickHouseGeoRingValue.ofEmpty();
break;
case Polygon:
column.template = ClickHouseGeoPolygonValue.ofEmpty();
break;
case MultiLineString:
column.template = ClickHouseGeoPolygonValue.ofEmpty();
break;
case MultiPolygon:
column.template = ClickHouseGeoMultiPolygonValue.ofEmpty();
break;
case Geometry:
ClickHouseColumn geometryVariantHelper = GEOMETRY_VARIANT_COLUMN;
column.template = ClickHouseTupleValue.of();
column.nested = geometryVariantHelper.nested;
column.classToVariantOrdNumMap = geometryVariantHelper.classToVariantOrdNumMap;
column.arrayToVariantOrdNumMap = geometryVariantHelper.arrayToVariantOrdNumMap;
column.geometryTypeDimensionsToVariantOrdNumMap = geometryVariantHelper.geometryTypeDimensionsToVariantOrdNumMap;
column.mapKeyToVariantOrdNumMap = geometryVariantHelper.mapKeyToVariantOrdNumMap;
column.mapValueToVariantOrdNumMap = geometryVariantHelper.mapValueToVariantOrdNumMap;
break;
case Nested:
column.template = ClickHouseNestedValue.ofEmpty(column.nested);
break;
Expand All @@ -318,6 +337,28 @@ private static ClickHouseColumn update(ClickHouseColumn column) {
return column;
}

private static ClickHouseColumn createGeometryVariantColumn() {
ClickHouseColumn column = ClickHouseColumn.of("v",
"Variant(Point, Ring, LineString, MultiLineString, Polygon, MultiPolygon)");
Map<Integer, Integer> map = new HashMap<>();
map.put(1, getVariantOrdNum(column.nested, ClickHouseDataType.Point));
map.put(2, getVariantOrdNum(column.nested, ClickHouseDataType.Ring));
map.put(3, getVariantOrdNum(column.nested, ClickHouseDataType.Polygon));
map.put(4, getVariantOrdNum(column.nested, ClickHouseDataType.MultiPolygon));
column.geometryTypeDimensionsToVariantOrdNumMap = Collections.unmodifiableMap(map);
return column;
}

private static int getVariantOrdNum(List<ClickHouseColumn> nestedColumns, ClickHouseDataType dataType) {
for (int i = 0; i < nestedColumns.size(); i++) {
if (nestedColumns.get(i).getDataType() == dataType) {
return i;
}
}

throw new IllegalArgumentException("Missing nested geometry type: " + dataType);
}

protected static int readColumn(String args, int startIndex, int len, String name, List<ClickHouseColumn> list) {
ClickHouseColumn column = null;

Expand Down Expand Up @@ -374,7 +415,7 @@ protected static int readColumn(String args, int startIndex, int len, String nam
nestedColumns.add(ClickHouseColumn.of("", p));
}
}
column = new ClickHouseColumn(ClickHouseDataType.valueOf(matchedKeyword), name,
column = new ClickHouseColumn(ClickHouseDataType.of(matchedKeyword), name,
args.substring(startIndex, i), nullable, lowCardinality, params, nestedColumns);
column.aggFuncType = aggFunc;
if (!nestedColumns.isEmpty()) {
Expand Down Expand Up @@ -468,7 +509,7 @@ protected static int readColumn(String args, int startIndex, int len, String nam
variantDataTypes.add(c.dataType);
});
}
column = new ClickHouseColumn(ClickHouseDataType.valueOf(matchedKeyword), name,
column = new ClickHouseColumn(ClickHouseDataType.of(matchedKeyword), name,
args.substring(startIndex, endIndex + 1), nullable, lowCardinality, null, nestedColumns);
for (ClickHouseColumn n : nestedColumns) {
estimatedLength += n.estimatedByteLength;
Expand Down Expand Up @@ -821,18 +862,24 @@ public boolean isAggregateFunction() {

public int getVariantOrdNum(Object value) {
if (value != null && value.getClass().isArray()) {
if (arrayToVariantOrdNumMap == null) {
return -1;
}
// TODO: add cache by value class
Class<?> c = value.getClass();
while (c.isArray()) {
c = c.getComponentType();
}
return arrayToVariantOrdNumMap.getOrDefault(c, -1);
} else if (value != null && value instanceof List<?>) {
if (arrayToVariantOrdNumMap == null) {
return -1;
}
// TODO: add cache by instance of the list
Object tmpV = ((List) value).get(0);
Object tmpV = ((List<?>) value).get(0);
Class<?> valueClass = tmpV.getClass();
while (tmpV instanceof List<?>) {
tmpV = ((List) tmpV).get(0);
tmpV = ((List<?>) tmpV).get(0);
valueClass = tmpV.getClass();
}
return arrayToVariantOrdNumMap.getOrDefault(valueClass, -1);
Expand Down Expand Up @@ -861,10 +908,33 @@ public int getVariantOrdNum(Object value) {
}
return -1;
} else {
return classToVariantOrdNumMap.getOrDefault(value.getClass(), -1);
return getGeometryVariantOrdNum(value);
}
}

public int getGeometryVariantOrdNum(Object value) {
if (value == null || classToVariantOrdNumMap == null) {
return -1;
}

Integer ordNum = classToVariantOrdNumMap.get(value.getClass());
if (ordNum != null) {
return ordNum;
}

return -1;
}

public int getGeometryVariantOrdNum(int dimensions) {
if (dimensions < 1 || geometryTypeDimensionsToVariantOrdNumMap == null) {
return -1;
}

return geometryTypeDimensionsToVariantOrdNumMap.getOrDefault(dimensions, -1);
}

private static final ClickHouseColumn GEOMETRY_VARIANT_COLUMN = createGeometryVariantColumn();

public boolean isArray() {
return dataType == ClickHouseDataType.Array;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public enum ClickHouseDataType implements SQLType {
Ring(Object.class, false, true, true, 0, 0, 0, 0, 0, true), // same as Array(Point)
LineString( Object.class, false, true, true, 0, 0, 0, 0, 0, true), // same as Array(Point)
MultiLineString(Object.class, false, true, true, 0, 0, 0, 0, 0, true), // same as Array(Ring)
Geometry(Object.class, false, true, true, 0, 0, 0, 0, 0, true), // same as Variant(Point, ...)
JSON(Object.class, false, false, false, 0, 0, 0, 0, 0, true, 0x30),
@Deprecated // (since = "CH 25.11")
Object(Object.class, true, true, false, 0, 0, 0, 0, 0, true),
Expand All @@ -130,7 +131,6 @@ public enum ClickHouseDataType implements SQLType {
Time(LocalDateTime.class, true, false, false, 4, 9, 0, 0, 9, false, 0x32), // 0x33 for Time(Timezone)
Time64(LocalDateTime.class, true, false, false, 8, 9, 0, 0, 0, false, 0x34), // 0x35 for Time64(P, Timezone)
QBit(Double.class, true, true, false, 0, 0, 0, 0, 0, false, 0x36),
Geometry(Object.class, false, false, false, 0, 0, 0, 0, 0, true),
;

public static final List<ClickHouseDataType> ORDERED_BY_RANGE_INT_TYPES =
Expand Down Expand Up @@ -214,8 +214,19 @@ static Map<ClickHouseDataType, Set<Class<?>>> dataTypeClassMap() {

map.put(Point, setOf(double[].class, ClickHouseGeoPointValue.class));
map.put(Ring, setOf(double[][].class, ClickHouseGeoRingValue.class));
map.put(LineString, setOf(double[][].class, ClickHouseGeoRingValue.class));
map.put(Polygon, setOf(double[][][].class, ClickHouseGeoPolygonValue.class));
map.put(MultiLineString, setOf(double[][][].class, ClickHouseGeoPolygonValue.class));
map.put(MultiPolygon, setOf(double[][][][].class, ClickHouseGeoMultiPolygonValue.class));
map.put(Geometry, setOf(
double[].class,
double[][].class,
double[][][].class,
double[][][][].class,
ClickHouseGeoPointValue.class,
ClickHouseGeoRingValue.class,
ClickHouseGeoPolygonValue.class,
ClickHouseGeoMultiPolygonValue.class));

map.put(Date, setOf(LocalDateTime.class, LocalDate.class, ZonedDateTime.class));
map.put(Date32, setOf(LocalDateTime.class, LocalDate.class, ZonedDateTime.class));
Expand Down
Loading
Loading