Skip to content

Commit df769cc

Browse files
committed
implemented reading JSON columns with different definitions
1 parent 25d8c60 commit df769cc

File tree

3 files changed

+68
-18
lines changed

3 files changed

+68
-18
lines changed

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

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,9 @@
3737

3838
import java.io.Serializable;
3939
import java.lang.reflect.Array;
40-
import java.math.BigInteger;
4140
import java.time.OffsetDateTime;
4241
import java.util.ArrayList;
4342
import java.util.Arrays;
44-
import java.util.Collection;
4543
import java.util.Collections;
4644
import java.util.Comparator;
4745
import java.util.HashMap;
@@ -507,19 +505,19 @@ protected static int readColumn(String args, int startIndex, int len, String nam
507505
}
508506
} else if (args.startsWith(KEYWORD_JSON, i)) {
509507
int index = args.indexOf('(', i + KEYWORD_JSON.length());
510-
if (index < i) {
511-
throw new IllegalArgumentException(ERROR_MISSING_NESTED_TYPE);
512-
}
513-
i = ClickHouseUtils.skipBrackets(args, index, len, '(');
514-
String originalTypeName = args.substring(startIndex, i);
515-
List<ClickHouseColumn> nestedColumns = parse(args.substring(index + 1, i - 1));
516-
if (nestedColumns.isEmpty()) {
517-
throw new IllegalArgumentException("Nested should have at least one nested column");
508+
if (index > i) {
509+
i = ClickHouseUtils.skipBrackets(args, index, len, '(');
510+
String originalTypeName = args.substring(startIndex, i);
511+
List<String> params = new ArrayList<>();
512+
List<ClickHouseColumn> nestedColumns = new ArrayList<>();
513+
List<String> parameters = new ArrayList<>();
514+
parseJSONColumn(args.substring(index + 1, i - 1), nestedColumns, parameters);
515+
nestedColumns.sort(Comparator.comparing(o -> o.getDataType().name()));
516+
column = new ClickHouseColumn(ClickHouseDataType.JSON, name, originalTypeName, nullable, lowCardinality,
517+
parameters, nestedColumns);
518+
fixedLength = false;
519+
estimatedLength++;
518520
}
519-
column = new ClickHouseColumn(ClickHouseDataType.JSON, name, originalTypeName, nullable, lowCardinality,
520-
null, nestedColumns);
521-
fixedLength = false;
522-
estimatedLength++;
523521
}
524522

525523
if (column == null) {
@@ -674,6 +672,48 @@ public static List<ClickHouseColumn> parse(String args) {
674672
return Collections.unmodifiableList(c);
675673
}
676674

675+
public static final String JSON_MAX_PATHS_PARAM = "max_dynamic_paths";
676+
public static final String JSON_MAX_DYN_TYPES_PARAM = "max_dynamic_types";
677+
public static final String JSON_SKIP_MARKER = "SKIP";
678+
679+
public static void parseJSONColumn(String args, List<ClickHouseColumn> nestedColumns, List<String> parameters) {
680+
if (args == null || args.isEmpty()) {
681+
return;
682+
}
683+
684+
String name = null;
685+
ClickHouseColumn column = null;
686+
StringBuilder builder = new StringBuilder();
687+
for (int i = 0, len = args.length(); i < len; i++) {
688+
char ch = args.charAt(i);
689+
if (Character.isWhitespace(ch)) {
690+
continue;
691+
}
692+
693+
if (name == null) { // column name
694+
i = ClickHouseUtils.readNameOrQuotedString(args, i, len, builder) - 1;
695+
name = builder.toString();
696+
if (name.startsWith(JSON_SKIP_MARKER)) {
697+
name = null; // skip parameters
698+
i = ClickHouseUtils.skipContentsUntil(args, i, len, ',') - 1;
699+
} else if ( name.startsWith(JSON_MAX_PATHS_PARAM) || name.startsWith(JSON_MAX_DYN_TYPES_PARAM)) {
700+
parameters.add(name);
701+
name = null;
702+
i = ClickHouseUtils.skipContentsUntil(args, i, len, ',') - 1;
703+
}
704+
builder.setLength(0);
705+
} else if (column == null) { // now type
706+
LinkedList<ClickHouseColumn> colList = new LinkedList<>();
707+
i = readColumn(args, i, len, name, colList) - 1;
708+
nestedColumns.add(column = colList.getFirst());
709+
} else { // prepare for next column
710+
i = ClickHouseUtils.skipContentsUntil(args, i, len, ',') - 1;
711+
name = null;
712+
column = null;
713+
}
714+
}
715+
}
716+
677717
public ClickHouseColumn(ClickHouseDataType dataType, String columnName, String originalTypeName, boolean nullable,
678718
boolean lowCardinality, List<String> parameters, List<ClickHouseColumn> nestedColumns) {
679719
this(dataType, columnName, originalTypeName, nullable, lowCardinality, parameters, nestedColumns, ClickHouseEnum.EMPTY);

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReader.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import java.util.Set;
3636
import java.util.TimeZone;
3737
import java.util.UUID;
38+
import java.util.function.Function;
39+
import java.util.stream.Collectors;
3840

3941
/**
4042
* This class is not thread safe and should not be shared between multiple threads.
@@ -226,7 +228,7 @@ public <T> T readValue(ClickHouseColumn column, Class<?> typeHint) throws IOExce
226228
if (jsonAsString) {
227229
return (T) readString(input);
228230
} else {
229-
return (T) readJsonData(input);
231+
return (T) readJsonData(input, actualColumn);
230232
}
231233
// case Object: // deprecated https://clickhouse.com/docs/en/sql-reference/data-types/object-data-type
232234
case Array:
@@ -1192,16 +1194,20 @@ private ClickHouseColumn readDynamicData() throws IOException {
11921194

11931195
private static final ClickHouseColumn JSON_PLACEHOLDER_COL = ClickHouseColumn.parse("v Dynamic").get(0);
11941196

1195-
private Map<String, Object> readJsonData(InputStream input) throws IOException {
1197+
private Map<String, Object> readJsonData(InputStream input, ClickHouseColumn column) throws IOException {
11961198
int numOfPaths = readVarInt(input);
11971199
if (numOfPaths == 0) {
11981200
return Collections.emptyMap();
11991201
}
12001202

12011203
Map<String, Object> obj = new HashMap<>();
1204+
1205+
final int predefinedPaths = column.getNestedColumns().size();
1206+
final List<ClickHouseColumn> predefinedColumns = column.getNestedColumns();
12021207
for (int i = 0; i < numOfPaths; i++) {
12031208
String path = readString(input);
1204-
Object value = readValue(JSON_PLACEHOLDER_COL);
1209+
ClickHouseColumn dataColumn = i < predefinedPaths ? predefinedColumns.get(i) : JSON_PLACEHOLDER_COL;
1210+
Object value = readValue(dataColumn);
12051211
obj.put(path, value);
12061212
}
12071213
return obj;

client-v2/src/test/java/com/clickhouse/client/datatypes/DataTypeTests.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,8 +904,12 @@ public Object[][] testJSONBinaryFormat_dp() {
904904

905905
return new Object[][] {
906906
{"JSON"},
907-
// {"JSON(a Int32, d String)"},
907+
{"JSON()"},
908908
{"JSON(stat.name String, count Int32)"},
909+
{"JSON(stat.name String, `comments` String)"},
910+
{"JSON(max_dynamic_paths=3, stat.name String, SKIP alt_count)"},
911+
{"JSON(max_dynamic_paths=3, stat.name String, SKIP REGEXP '^-.*')"},
912+
{"JSON(max_dynamic_paths=3,SKIP REGEXP '^-.*',SKIP ff, flags Array(Array(Array(Int8))), SKIP alt_count)"},
909913
};
910914
}
911915

0 commit comments

Comments
 (0)