Skip to content

Commit

Permalink
Better support of Bool, Enum, and custom type mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
zhicwu committed Dec 21, 2021
1 parent 262b3eb commit 6b7d1f6
Show file tree
Hide file tree
Showing 27 changed files with 1,183 additions and 392 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,69 +36,76 @@ public final class ClickHouseColumn implements Serializable {
private int scale;
private List<ClickHouseColumn> nested;
private List<String> parameters;
private ClickHouseEnum enumConstants;

private int arrayLevel;
private ClickHouseColumn arrayBaseColumn;

private static ClickHouseColumn update(ClickHouseColumn column) {
column.enumConstants = ClickHouseEnum.EMPTY;
int size = column.parameters.size();
switch (column.dataType) {
case Array:
column.arrayLevel = 1;
column.arrayBaseColumn = column.nested.get(0);
while (column.arrayLevel < 255) {
if (column.arrayBaseColumn.dataType == ClickHouseDataType.Array) {
column.arrayLevel++;
column.arrayBaseColumn = column.arrayBaseColumn.nested.get(0);
} else {
break;
case Array:
column.arrayLevel = 1;
column.arrayBaseColumn = column.nested.get(0);
while (column.arrayLevel < 255) {
if (column.arrayBaseColumn.dataType == ClickHouseDataType.Array) {
column.arrayLevel++;
column.arrayBaseColumn = column.arrayBaseColumn.nested.get(0);
} else {
break;
}
}
}
break;
case DateTime:
if (size >= 2) { // same as DateTime64
column.scale = Integer.parseInt(column.parameters.get(0));
column.timeZone = TimeZone.getTimeZone(column.parameters.get(1).replace("'", ""));
} else if (size == 1) { // same as DateTime32
// unfortunately this will fall back to GMT if the time zone
// cannot be resolved
TimeZone tz = TimeZone.getTimeZone(column.parameters.get(0).replace("'", ""));
column.timeZone = tz;
}
break;
case DateTime32:
if (size > 0) {
// unfortunately this will fall back to GMT if the time zone
// cannot be resolved
TimeZone tz = TimeZone.getTimeZone(column.parameters.get(0).replace("'", ""));
column.timeZone = tz;
}
break;
case DateTime64:
if (size > 0) {
break;
case Enum:
case Enum8:
case Enum16:
column.enumConstants = new ClickHouseEnum(column.parameters);
break;
case DateTime:
if (size >= 2) { // same as DateTime64
column.scale = Integer.parseInt(column.parameters.get(0));
column.timeZone = TimeZone.getTimeZone(column.parameters.get(1).replace("'", ""));
} else if (size == 1) { // same as DateTime32
// unfortunately this will fall back to GMT if the time zone
// cannot be resolved
TimeZone tz = TimeZone.getTimeZone(column.parameters.get(0).replace("'", ""));
column.timeZone = tz;
}
break;
case DateTime32:
if (size > 0) {
// unfortunately this will fall back to GMT if the time zone
// cannot be resolved
TimeZone tz = TimeZone.getTimeZone(column.parameters.get(0).replace("'", ""));
column.timeZone = tz;
}
break;
case DateTime64:
if (size > 0) {
column.scale = Integer.parseInt(column.parameters.get(0));
}
if (size > 1) {
column.timeZone = TimeZone.getTimeZone(column.parameters.get(1).replace("'", ""));
}
break;
case Decimal:
if (size >= 2) {
column.precision = Integer.parseInt(column.parameters.get(0));
column.scale = Integer.parseInt(column.parameters.get(1));
}
break;
case Decimal32:
case Decimal64:
case Decimal128:
case Decimal256:
column.scale = Integer.parseInt(column.parameters.get(0));
}
if (size > 1) {
column.timeZone = TimeZone.getTimeZone(column.parameters.get(1).replace("'", ""));
}
break;
case Decimal:
if (size >= 2) {
break;
case FixedString:
column.precision = Integer.parseInt(column.parameters.get(0));
column.scale = Integer.parseInt(column.parameters.get(1));
}
break;
case Decimal32:
case Decimal64:
case Decimal128:
case Decimal256:
column.scale = Integer.parseInt(column.parameters.get(0));
break;
case FixedString:
column.precision = Integer.parseInt(column.parameters.get(0));
break;
default:
break;
break;
default:
break;
}

return column;
Expand Down Expand Up @@ -395,6 +402,11 @@ public boolean isArray() {
return dataType == ClickHouseDataType.Array;
}

public boolean isEnum() {
return dataType == ClickHouseDataType.Enum || dataType == ClickHouseDataType.Enum8
|| dataType == ClickHouseDataType.Enum16;
}

public boolean isMap() {
return dataType == ClickHouseDataType.Map;
}
Expand All @@ -419,6 +431,10 @@ public ClickHouseDataType getDataType() {
return dataType;
}

public ClickHouseEnum getEnumConstants() {
return enumConstants;
}

public String getOriginalTypeName() {
return originalTypeName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ public enum ClickHouseDataType {
UInt64(Long.class, false, true, false, 8, 20, 0, 0, 0, "BIGINT UNSIGNED"),
UInt128(BigInteger.class, false, true, false, 16, 39, 0, 0, 0),
UInt256(BigInteger.class, false, true, false, 32, 78, 0, 0, 0), Int8(Byte.class, false, true, true, 1, 3, 0, 0, 0,
"BOOL", "BOOLEAN", "BYTE", "INT1", "INT1 SIGNED", "TINYINT", "TINYINT SIGNED"),
"BYTE", "INT1", "INT1 SIGNED", "TINYINT", "TINYINT SIGNED"),
Int16(Short.class, false, true, true, 2, 5, 0, 0, 0, "SMALLINT", "SMALLINT SIGNED"),
Int32(Integer.class, false, true, true, 4, 10, 0, 0, 0, "INT", "INTEGER", "MEDIUMINT", "INT SIGNED",
"INTEGER SIGNED", "MEDIUMINT SIGNED"),
Int64(Long.class, false, true, true, 8, 19, 0, 0, 0, "BIGINT", "BIGINT SIGNED"),
Int128(BigInteger.class, false, true, true, 16, 39, 0, 0, 0),
Int256(BigInteger.class, false, true, true, 32, 77, 0, 0, 0),
Bool(Boolean.class, false, false, true, 1, 3, 0, 0, 0, "BOOLEAN"),
Date(LocalDate.class, false, false, false, 2, 10, 0, 0, 0),
Date32(LocalDate.class, false, false, false, 4, 10, 0, 0, 0),
DateTime(LocalDateTime.class, true, false, false, 0, 29, 0, 0, 9, "TIMESTAMP"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.clickhouse.client;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

public class ClickHouseEnum {
public static final ClickHouseEnum EMPTY = new ClickHouseEnum(Collections.emptyList());

public static ClickHouseEnum of(Class<? extends Enum> clazz) {
if (clazz == null || !Enum.class.isAssignableFrom(clazz)) {
return EMPTY;
}

Enum<?>[] constants = clazz.getEnumConstants();
int size = constants.length;
String[] names = new String[size];
int[] values = new int[size];
int i = 0;
for (Enum<?> e : clazz.getEnumConstants()) {
names[i] = e.name();
values[i] = e.ordinal();
i++;
}

return new ClickHouseEnum(names, values);
}

private final int size;
private final String[] names;
private final int[] values;

protected ClickHouseEnum(Collection<String> params) {
size = params.size();
names = new String[size];
values = new int[size];

int i = 0;
for (String p : params) {
int index = p.lastIndexOf('=');
if (index > 0) {
names[i] = ClickHouseUtils.unescape(p.substring(0, index));
values[i] = Integer.parseInt(p.substring(index + 1));
} else {
throw new IllegalArgumentException("Invalid enum entry: " + p);
}
i++;
}
}

protected ClickHouseEnum(String[] names, int[] values) {
if (names == null || values == null) {
throw new IllegalArgumentException("Non-null names and values are required");
} else if (names.length != values.length) {
throw new IllegalArgumentException("Names and values should have same length");
}

this.size = names.length;
this.names = names;
this.values = values;
}

public String validate(String name) {
for (int i = 0; i < size; i++) {
if (names[i].equals(name)) {
return name;
}
}

throw new IllegalArgumentException("Unknown enum name: " + name);
}

public int validate(int value) {
for (int i = 0; i < size; i++) {
if (values[i] == value) {
return value;
}
}

throw new IllegalArgumentException("Unknown enum value: " + value);
}

public String name(int value) {
for (int i = 0; i < size; i++) {
if (values[i] == value) {
return names[i];
}
}

throw new IllegalArgumentException("Unknown enum value: " + value);
}

public int value(String name) {
for (int i = 0; i < size; i++) {
if (names[i].equals(name)) {
return values[i];
}
}

throw new IllegalArgumentException("Unknown enum name: " + name);
}

@Override
public int hashCode() {
final int prime = 31;
int result = prime + size;
result = prime * result + Arrays.hashCode(names);
result = prime * result + Arrays.hashCode(values);
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
ClickHouseEnum other = (ClickHouseEnum) obj;
return size == other.size && Arrays.equals(names, other.names) && Arrays.equals(values, other.values);
}

public String toSqlException() {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < size; i++) {
builder.append('\'').append(ClickHouseUtils.escape(names[i], '\'')).append('\'').append('=')
.append(values[i]).append(',');
}
if (builder.length() > 0) {
builder.setLength(builder.length() - 1);
}
return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -454,11 +454,60 @@ default <K, V> Map<K, V> asMap(Class<K> keyClass, Class<V> valueClass) {
* Gets value as a typed object.
*
* @param <T> type of the object
* @param <E> type of the enum
* @param clazz class of the object
* @return a typed object representing the value, could be null
*/
default <T> T asObject(Class<T> clazz) {
return isNullOrEmpty() ? null : ClickHouseChecker.nonNull(clazz, ClickHouseValues.TYPE_CLASS).cast(asObject());
default <T, E extends Enum<E>> T asObject(Class<T> clazz) {
if (clazz == null) {
return null;
} else if (clazz == boolean.class || clazz == Boolean.class) {
return clazz.cast(asBoolean());
} else if (clazz == byte.class || clazz == Byte.class) {
return clazz.cast(asByte());
} else if (clazz == char.class || clazz == Character.class) {
return clazz.cast(asCharacter());
} else if (clazz == short.class || clazz == Short.class) {
return clazz.cast(asShort());
} else if (clazz == int.class || clazz == Integer.class) {
return clazz.cast(asInteger());
} else if (clazz == long.class || clazz == Long.class) {
return clazz.cast(asLong());
} else if (clazz == float.class || clazz == Float.class) {
return clazz.cast(asFloat());
} else if (clazz == double.class || clazz == Double.class) {
return clazz.cast(asDouble());
} else if (clazz == String.class) {
return clazz.cast(asString());
} else if (clazz == LocalDate.class) {
return clazz.cast(asDate());
} else if (clazz == LocalDateTime.class) {
return clazz.cast(asDateTime());
} else if (clazz == OffsetDateTime.class) {
return clazz.cast(asOffsetDateTime());
} else if (clazz == ZonedDateTime.class) {
return clazz.cast(asZonedDateTime());
} else if (clazz == LocalTime.class) {
return clazz.cast(asTime());
} else if (clazz == BigInteger.class) {
return clazz.cast(asBigInteger());
} else if (clazz == BigDecimal.class) {
return clazz.cast(asBigDecimal());
} else if (clazz == Inet4Address.class) {
return clazz.cast(asInet4Address());
} else if (clazz == Inet6Address.class) {
return clazz.cast(asInet6Address());
} else if (clazz == UUID.class) {
return clazz.cast(asUuid());
} else if (Array.class.isAssignableFrom(clazz)) {
return clazz.cast(asArray());
} else if (List.class.isAssignableFrom(clazz)) {
return clazz.cast(asTuple());
} else if (Enum.class.isAssignableFrom(clazz)) {
return clazz.cast(asEnum((Class<E>) clazz));
} else {
return clazz.cast(asObject());
}
}

/**
Expand Down
Loading

0 comments on commit 6b7d1f6

Please sign in to comment.