Skip to content

Commit 6b7d1f6

Browse files
committed
Better support of Bool, Enum, and custom type mapping
1 parent 262b3eb commit 6b7d1f6

27 files changed

Lines changed: 1183 additions & 392 deletions

clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseColumn.java

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -36,69 +36,76 @@ public final class ClickHouseColumn implements Serializable {
3636
private int scale;
3737
private List<ClickHouseColumn> nested;
3838
private List<String> parameters;
39+
private ClickHouseEnum enumConstants;
3940

4041
private int arrayLevel;
4142
private ClickHouseColumn arrayBaseColumn;
4243

4344
private static ClickHouseColumn update(ClickHouseColumn column) {
45+
column.enumConstants = ClickHouseEnum.EMPTY;
4446
int size = column.parameters.size();
4547
switch (column.dataType) {
46-
case Array:
47-
column.arrayLevel = 1;
48-
column.arrayBaseColumn = column.nested.get(0);
49-
while (column.arrayLevel < 255) {
50-
if (column.arrayBaseColumn.dataType == ClickHouseDataType.Array) {
51-
column.arrayLevel++;
52-
column.arrayBaseColumn = column.arrayBaseColumn.nested.get(0);
53-
} else {
54-
break;
48+
case Array:
49+
column.arrayLevel = 1;
50+
column.arrayBaseColumn = column.nested.get(0);
51+
while (column.arrayLevel < 255) {
52+
if (column.arrayBaseColumn.dataType == ClickHouseDataType.Array) {
53+
column.arrayLevel++;
54+
column.arrayBaseColumn = column.arrayBaseColumn.nested.get(0);
55+
} else {
56+
break;
57+
}
5558
}
56-
}
57-
break;
58-
case DateTime:
59-
if (size >= 2) { // same as DateTime64
60-
column.scale = Integer.parseInt(column.parameters.get(0));
61-
column.timeZone = TimeZone.getTimeZone(column.parameters.get(1).replace("'", ""));
62-
} else if (size == 1) { // same as DateTime32
63-
// unfortunately this will fall back to GMT if the time zone
64-
// cannot be resolved
65-
TimeZone tz = TimeZone.getTimeZone(column.parameters.get(0).replace("'", ""));
66-
column.timeZone = tz;
67-
}
68-
break;
69-
case DateTime32:
70-
if (size > 0) {
71-
// unfortunately this will fall back to GMT if the time zone
72-
// cannot be resolved
73-
TimeZone tz = TimeZone.getTimeZone(column.parameters.get(0).replace("'", ""));
74-
column.timeZone = tz;
75-
}
76-
break;
77-
case DateTime64:
78-
if (size > 0) {
59+
break;
60+
case Enum:
61+
case Enum8:
62+
case Enum16:
63+
column.enumConstants = new ClickHouseEnum(column.parameters);
64+
break;
65+
case DateTime:
66+
if (size >= 2) { // same as DateTime64
67+
column.scale = Integer.parseInt(column.parameters.get(0));
68+
column.timeZone = TimeZone.getTimeZone(column.parameters.get(1).replace("'", ""));
69+
} else if (size == 1) { // same as DateTime32
70+
// unfortunately this will fall back to GMT if the time zone
71+
// cannot be resolved
72+
TimeZone tz = TimeZone.getTimeZone(column.parameters.get(0).replace("'", ""));
73+
column.timeZone = tz;
74+
}
75+
break;
76+
case DateTime32:
77+
if (size > 0) {
78+
// unfortunately this will fall back to GMT if the time zone
79+
// cannot be resolved
80+
TimeZone tz = TimeZone.getTimeZone(column.parameters.get(0).replace("'", ""));
81+
column.timeZone = tz;
82+
}
83+
break;
84+
case DateTime64:
85+
if (size > 0) {
86+
column.scale = Integer.parseInt(column.parameters.get(0));
87+
}
88+
if (size > 1) {
89+
column.timeZone = TimeZone.getTimeZone(column.parameters.get(1).replace("'", ""));
90+
}
91+
break;
92+
case Decimal:
93+
if (size >= 2) {
94+
column.precision = Integer.parseInt(column.parameters.get(0));
95+
column.scale = Integer.parseInt(column.parameters.get(1));
96+
}
97+
break;
98+
case Decimal32:
99+
case Decimal64:
100+
case Decimal128:
101+
case Decimal256:
79102
column.scale = Integer.parseInt(column.parameters.get(0));
80-
}
81-
if (size > 1) {
82-
column.timeZone = TimeZone.getTimeZone(column.parameters.get(1).replace("'", ""));
83-
}
84-
break;
85-
case Decimal:
86-
if (size >= 2) {
103+
break;
104+
case FixedString:
87105
column.precision = Integer.parseInt(column.parameters.get(0));
88-
column.scale = Integer.parseInt(column.parameters.get(1));
89-
}
90-
break;
91-
case Decimal32:
92-
case Decimal64:
93-
case Decimal128:
94-
case Decimal256:
95-
column.scale = Integer.parseInt(column.parameters.get(0));
96-
break;
97-
case FixedString:
98-
column.precision = Integer.parseInt(column.parameters.get(0));
99-
break;
100-
default:
101-
break;
106+
break;
107+
default:
108+
break;
102109
}
103110

104111
return column;
@@ -395,6 +402,11 @@ public boolean isArray() {
395402
return dataType == ClickHouseDataType.Array;
396403
}
397404

405+
public boolean isEnum() {
406+
return dataType == ClickHouseDataType.Enum || dataType == ClickHouseDataType.Enum8
407+
|| dataType == ClickHouseDataType.Enum16;
408+
}
409+
398410
public boolean isMap() {
399411
return dataType == ClickHouseDataType.Map;
400412
}
@@ -419,6 +431,10 @@ public ClickHouseDataType getDataType() {
419431
return dataType;
420432
}
421433

434+
public ClickHouseEnum getEnumConstants() {
435+
return enumConstants;
436+
}
437+
422438
public String getOriginalTypeName() {
423439
return originalTypeName;
424440
}

clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseDataType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,14 @@ public enum ClickHouseDataType {
4545
UInt64(Long.class, false, true, false, 8, 20, 0, 0, 0, "BIGINT UNSIGNED"),
4646
UInt128(BigInteger.class, false, true, false, 16, 39, 0, 0, 0),
4747
UInt256(BigInteger.class, false, true, false, 32, 78, 0, 0, 0), Int8(Byte.class, false, true, true, 1, 3, 0, 0, 0,
48-
"BOOL", "BOOLEAN", "BYTE", "INT1", "INT1 SIGNED", "TINYINT", "TINYINT SIGNED"),
48+
"BYTE", "INT1", "INT1 SIGNED", "TINYINT", "TINYINT SIGNED"),
4949
Int16(Short.class, false, true, true, 2, 5, 0, 0, 0, "SMALLINT", "SMALLINT SIGNED"),
5050
Int32(Integer.class, false, true, true, 4, 10, 0, 0, 0, "INT", "INTEGER", "MEDIUMINT", "INT SIGNED",
5151
"INTEGER SIGNED", "MEDIUMINT SIGNED"),
5252
Int64(Long.class, false, true, true, 8, 19, 0, 0, 0, "BIGINT", "BIGINT SIGNED"),
5353
Int128(BigInteger.class, false, true, true, 16, 39, 0, 0, 0),
5454
Int256(BigInteger.class, false, true, true, 32, 77, 0, 0, 0),
55+
Bool(Boolean.class, false, false, true, 1, 3, 0, 0, 0, "BOOLEAN"),
5556
Date(LocalDate.class, false, false, false, 2, 10, 0, 0, 0),
5657
Date32(LocalDate.class, false, false, false, 4, 10, 0, 0, 0),
5758
DateTime(LocalDateTime.class, true, false, false, 0, 29, 0, 0, 9, "TIMESTAMP"),
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.clickhouse.client;
2+
3+
import java.util.Arrays;
4+
import java.util.Collection;
5+
import java.util.Collections;
6+
7+
public class ClickHouseEnum {
8+
public static final ClickHouseEnum EMPTY = new ClickHouseEnum(Collections.emptyList());
9+
10+
public static ClickHouseEnum of(Class<? extends Enum> clazz) {
11+
if (clazz == null || !Enum.class.isAssignableFrom(clazz)) {
12+
return EMPTY;
13+
}
14+
15+
Enum<?>[] constants = clazz.getEnumConstants();
16+
int size = constants.length;
17+
String[] names = new String[size];
18+
int[] values = new int[size];
19+
int i = 0;
20+
for (Enum<?> e : clazz.getEnumConstants()) {
21+
names[i] = e.name();
22+
values[i] = e.ordinal();
23+
i++;
24+
}
25+
26+
return new ClickHouseEnum(names, values);
27+
}
28+
29+
private final int size;
30+
private final String[] names;
31+
private final int[] values;
32+
33+
protected ClickHouseEnum(Collection<String> params) {
34+
size = params.size();
35+
names = new String[size];
36+
values = new int[size];
37+
38+
int i = 0;
39+
for (String p : params) {
40+
int index = p.lastIndexOf('=');
41+
if (index > 0) {
42+
names[i] = ClickHouseUtils.unescape(p.substring(0, index));
43+
values[i] = Integer.parseInt(p.substring(index + 1));
44+
} else {
45+
throw new IllegalArgumentException("Invalid enum entry: " + p);
46+
}
47+
i++;
48+
}
49+
}
50+
51+
protected ClickHouseEnum(String[] names, int[] values) {
52+
if (names == null || values == null) {
53+
throw new IllegalArgumentException("Non-null names and values are required");
54+
} else if (names.length != values.length) {
55+
throw new IllegalArgumentException("Names and values should have same length");
56+
}
57+
58+
this.size = names.length;
59+
this.names = names;
60+
this.values = values;
61+
}
62+
63+
public String validate(String name) {
64+
for (int i = 0; i < size; i++) {
65+
if (names[i].equals(name)) {
66+
return name;
67+
}
68+
}
69+
70+
throw new IllegalArgumentException("Unknown enum name: " + name);
71+
}
72+
73+
public int validate(int value) {
74+
for (int i = 0; i < size; i++) {
75+
if (values[i] == value) {
76+
return value;
77+
}
78+
}
79+
80+
throw new IllegalArgumentException("Unknown enum value: " + value);
81+
}
82+
83+
public String name(int value) {
84+
for (int i = 0; i < size; i++) {
85+
if (values[i] == value) {
86+
return names[i];
87+
}
88+
}
89+
90+
throw new IllegalArgumentException("Unknown enum value: " + value);
91+
}
92+
93+
public int value(String name) {
94+
for (int i = 0; i < size; i++) {
95+
if (names[i].equals(name)) {
96+
return values[i];
97+
}
98+
}
99+
100+
throw new IllegalArgumentException("Unknown enum name: " + name);
101+
}
102+
103+
@Override
104+
public int hashCode() {
105+
final int prime = 31;
106+
int result = prime + size;
107+
result = prime * result + Arrays.hashCode(names);
108+
result = prime * result + Arrays.hashCode(values);
109+
return result;
110+
}
111+
112+
@Override
113+
public boolean equals(Object obj) {
114+
if (this == obj) {
115+
return true;
116+
}
117+
if (obj == null || getClass() != obj.getClass()) {
118+
return false;
119+
}
120+
ClickHouseEnum other = (ClickHouseEnum) obj;
121+
return size == other.size && Arrays.equals(names, other.names) && Arrays.equals(values, other.values);
122+
}
123+
124+
public String toSqlException() {
125+
StringBuilder builder = new StringBuilder();
126+
for (int i = 0; i < size; i++) {
127+
builder.append('\'').append(ClickHouseUtils.escape(names[i], '\'')).append('\'').append('=')
128+
.append(values[i]).append(',');
129+
}
130+
if (builder.length() > 0) {
131+
builder.setLength(builder.length() - 1);
132+
}
133+
return builder.toString();
134+
}
135+
}

clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,11 +454,60 @@ default <K, V> Map<K, V> asMap(Class<K> keyClass, Class<V> valueClass) {
454454
* Gets value as a typed object.
455455
*
456456
* @param <T> type of the object
457+
* @param <E> type of the enum
457458
* @param clazz class of the object
458459
* @return a typed object representing the value, could be null
459460
*/
460-
default <T> T asObject(Class<T> clazz) {
461-
return isNullOrEmpty() ? null : ClickHouseChecker.nonNull(clazz, ClickHouseValues.TYPE_CLASS).cast(asObject());
461+
default <T, E extends Enum<E>> T asObject(Class<T> clazz) {
462+
if (clazz == null) {
463+
return null;
464+
} else if (clazz == boolean.class || clazz == Boolean.class) {
465+
return clazz.cast(asBoolean());
466+
} else if (clazz == byte.class || clazz == Byte.class) {
467+
return clazz.cast(asByte());
468+
} else if (clazz == char.class || clazz == Character.class) {
469+
return clazz.cast(asCharacter());
470+
} else if (clazz == short.class || clazz == Short.class) {
471+
return clazz.cast(asShort());
472+
} else if (clazz == int.class || clazz == Integer.class) {
473+
return clazz.cast(asInteger());
474+
} else if (clazz == long.class || clazz == Long.class) {
475+
return clazz.cast(asLong());
476+
} else if (clazz == float.class || clazz == Float.class) {
477+
return clazz.cast(asFloat());
478+
} else if (clazz == double.class || clazz == Double.class) {
479+
return clazz.cast(asDouble());
480+
} else if (clazz == String.class) {
481+
return clazz.cast(asString());
482+
} else if (clazz == LocalDate.class) {
483+
return clazz.cast(asDate());
484+
} else if (clazz == LocalDateTime.class) {
485+
return clazz.cast(asDateTime());
486+
} else if (clazz == OffsetDateTime.class) {
487+
return clazz.cast(asOffsetDateTime());
488+
} else if (clazz == ZonedDateTime.class) {
489+
return clazz.cast(asZonedDateTime());
490+
} else if (clazz == LocalTime.class) {
491+
return clazz.cast(asTime());
492+
} else if (clazz == BigInteger.class) {
493+
return clazz.cast(asBigInteger());
494+
} else if (clazz == BigDecimal.class) {
495+
return clazz.cast(asBigDecimal());
496+
} else if (clazz == Inet4Address.class) {
497+
return clazz.cast(asInet4Address());
498+
} else if (clazz == Inet6Address.class) {
499+
return clazz.cast(asInet6Address());
500+
} else if (clazz == UUID.class) {
501+
return clazz.cast(asUuid());
502+
} else if (Array.class.isAssignableFrom(clazz)) {
503+
return clazz.cast(asArray());
504+
} else if (List.class.isAssignableFrom(clazz)) {
505+
return clazz.cast(asTuple());
506+
} else if (Enum.class.isAssignableFrom(clazz)) {
507+
return clazz.cast(asEnum((Class<E>) clazz));
508+
} else {
509+
return clazz.cast(asObject());
510+
}
462511
}
463512

464513
/**

0 commit comments

Comments
 (0)