Skip to content

Commit

Permalink
Support Object/JSON and Tuple with names and types
Browse files Browse the repository at this point in the history
  • Loading branch information
zhicwu committed Apr 4, 2022
1 parent 9140754 commit ed47720
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.clickhouse.client;

import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand All @@ -13,6 +14,8 @@
* This class represents a column defined in database.
*/
public final class ClickHouseColumn implements Serializable {
public static final ClickHouseColumn[] EMPTY_ARRAY = new ClickHouseColumn[0];

private static final long serialVersionUID = 8228660689532259640L;

private static final String ERROR_MISSING_NESTED_TYPE = "Missing nested data type";
Expand All @@ -22,6 +25,7 @@ public final class ClickHouseColumn implements Serializable {
private static final String KEYWORD_SIMPLE_AGGREGATE_FUNCTION = ClickHouseDataType.SimpleAggregateFunction.name();
private static final String KEYWORD_ARRAY = ClickHouseDataType.Array.name();
private static final String KEYWORD_TUPLE = ClickHouseDataType.Tuple.name();
private static final String KEYWORD_OBJECT = ClickHouseDataType.Object.name();
private static final String KEYWORD_MAP = ClickHouseDataType.Map.name();
private static final String KEYWORD_NESTED = ClickHouseDataType.Nested.name();

Expand Down Expand Up @@ -264,26 +268,27 @@ protected static int readColumn(String args, int startIndex, int len, String nam
null, nestedColumns);
fixedLength = false;
estimatedLength++;
} else if (args.startsWith(KEYWORD_TUPLE, i)) {
int index = args.indexOf('(', i + KEYWORD_TUPLE.length());
} else if (args.startsWith(matchedKeyword = KEYWORD_TUPLE, i)
|| args.startsWith(matchedKeyword = KEYWORD_OBJECT, i)) {
int index = args.indexOf('(', i + matchedKeyword.length());
if (index < i) {
throw new IllegalArgumentException(ERROR_MISSING_NESTED_TYPE);
}
int endIndex = ClickHouseUtils.skipBrackets(args, index, len, '(');
int endIndex = ClickHouseUtils.skipBrackets(args, index, len, '(') - 1;
List<ClickHouseColumn> nestedColumns = new LinkedList<>();
for (i = index + 1; i < endIndex; i++) {
for (i = index + 1; i <= endIndex; i++) {
char c = args.charAt(i);
if (c == ')') {
break;
} else if (c != ',' && !Character.isWhitespace(c)) {
i = readColumn(args, i, endIndex, "", nestedColumns) - 1;
i = readColumn(args, i, endIndex, "", nestedColumns);
}
}
if (nestedColumns.isEmpty()) {
throw new IllegalArgumentException("Tuple should have at least one nested column");
}
column = new ClickHouseColumn(ClickHouseDataType.Tuple, name, args.substring(startIndex, endIndex),
nullable, lowCardinality, null, nestedColumns);
column = new ClickHouseColumn(ClickHouseDataType.valueOf(matchedKeyword), name,
args.substring(startIndex, endIndex + 1), nullable, lowCardinality, null, nestedColumns);
for (ClickHouseColumn n : nestedColumns) {
estimatedLength += n.estimatedByteLength;
if (!n.fixedByteLength) {
Expand All @@ -300,10 +305,13 @@ protected static int readColumn(String args, int startIndex, int len, String nam
if (ch == '(') {
i = ClickHouseUtils.readParameters(args, i, len, params) - 1;
} else if (ch == ')') {
brackets--;
if (brackets <= 0) {
i++;
break;
} else {
if (--brackets <= 0) {
i++;
break;
}
}
} else if (ch == ',') {
break;
Expand Down Expand Up @@ -335,7 +343,13 @@ protected static int readColumn(String args, int startIndex, int len, String nam
i = ClickHouseUtils.skipContentsUntil(args, i, len, ',') - 1;
break;
} else {
builder.append(' ').append(modifier);
if ((name == null || name.isEmpty())
&& !ClickHouseDataType.mayStartWith(builder.toString())) {
return readColumn(args, i - modifier.length(), len, builder.toString(), list);
} else {
builder.append(' ');
}
builder.append(modifier);
i--;
}
}
Expand Down Expand Up @@ -503,6 +517,14 @@ public ClickHouseDataType getDataType() {
return dataType;
}

public Class<?> getObjectClass() {
return timeZone != null ? OffsetDateTime.class : dataType.getObjectClass();
}

public Class<?> getPrimitiveClass() {
return timeZone != null ? OffsetDateTime.class : dataType.getPrimitiveClass();
}

public ClickHouseEnum getEnumConstants() {
return enumConstants;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public enum ClickHouseDataType {
Decimal128(BigDecimal.class, true, false, true, 16, 38, 38, 0, 38),
Decimal256(BigDecimal.class, true, false, true, 32, 76, 20, 0, 76),
UUID(UUID.class, false, true, false, 16, 69, 0, 0, 0),
/**
* Enum data type.
*
* @deprecated will be removed in v0.3.3, please use {@link #Enum8} instead
*/
@Deprecated
Enum(String.class, true, true, false, 1, 0, 0, 0, 0),
Enum8(String.class, true, true, false, 1, 0, 0, 0, 0), // "ENUM"),
Expand All @@ -85,6 +90,8 @@ public enum ClickHouseDataType {
Map(Map.class, true, true, false, 0, 0, 0, 0, 0),
Nested(Object.class, true, true, false, 0, 0, 0, 0, 0),
Tuple(List.class, true, true, false, 0, 0, 0, 0, 0),
Object(Object.class, true, true, false, 0, 0, 0, 0, 0),
JSON(Object.class, false, false, false, 0, 0, 0, 0, 0), // same as Object('JSON')
Point(Object.class, false, true, true, 33, 0, 0, 0, 0), // same as Tuple(Float64, Float64)
Polygon(Object.class, false, true, true, 0, 0, 0, 0, 0), // same as Array(Ring)
MultiPolygon(Object.class, false, true, true, 0, 0, 0, 0, 0), // same as Array(Polygon)
Expand Down Expand Up @@ -173,6 +180,26 @@ public static List<String> match(String part) {
return types;
}

/**
* Checks if any alias uses the given prefix.
*
* @return true if any alias using the given prefix; false otherwise
*/
public static boolean mayStartWith(String prefix) {
if (prefix == null || prefix.isEmpty()) {
return false;
}

prefix = prefix.toUpperCase();
for (String alias : allAliases) {
if (alias.startsWith(prefix)) {
return true;
}
}

return false;
}

/**
* Converts given type name to corresponding data type.
*
Expand Down Expand Up @@ -324,7 +351,8 @@ public boolean isCaseSensitive() {
* @return true if it could be a nested structure; false otherwise
*/
public boolean isNested() {
return this == AggregateFunction || this == Array || this == Map || this == Nested || this == Tuple;
return this == AggregateFunction || this == Array || this == Map || this == Nested || this == Tuple
|| this == Object || this == JSON;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ public ResultSet getColumns(String catalog, String schemaPattern, String tableNa
r.getValue("CHAR_OCTET_LENGTH").update(column.getPrecision());
}

Class<?> clazz = column.getDataType().getObjectClass();
Class<?> clazz = column.getObjectClass();
if (column.getScale() > 0 || Number.class.isAssignableFrom(clazz) || Date.class.isAssignableFrom(clazz)
|| Temporal.class.isAssignableFrom(clazz)) {
r.getValue("DECIMAL_DIGITS").update(column.getScale());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public String getParameterTypeName(int param) throws SQLException {
@Override
public String getParameterClassName(int param) throws SQLException {
ClickHouseColumn p = getParameter(param);
return (p != null ? p.getDataType().getObjectClass() : Object.class).getName();
return (p != null ? p.getObjectClass() : Object.class).getName();
}

@Override
Expand Down

0 comments on commit ed47720

Please sign in to comment.