Skip to content

Commit 589a152

Browse files
committed
draft Variant implementation
1 parent 54753c8 commit 589a152

File tree

4 files changed

+112
-2
lines changed

4 files changed

+112
-2
lines changed

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.util.ArrayList;
4242
import java.util.Arrays;
4343
import java.util.Collections;
44+
import java.util.Comparator;
4445
import java.util.LinkedList;
4546
import java.util.List;
4647
import java.util.Objects;
@@ -65,6 +66,7 @@ public final class ClickHouseColumn implements Serializable {
6566
private static final String KEYWORD_OBJECT = ClickHouseDataType.Object.name();
6667
private static final String KEYWORD_MAP = ClickHouseDataType.Map.name();
6768
private static final String KEYWORD_NESTED = ClickHouseDataType.Nested.name();
69+
private static final String KEYWORD_VARIANT = ClickHouseDataType.Variant.name();
6870

6971
private int columnCount;
7072
private int columnIndex;
@@ -273,6 +275,9 @@ private static ClickHouseColumn update(ClickHouseColumn column) {
273275
case Nothing:
274276
column.template = ClickHouseEmptyValue.INSTANCE;
275277
break;
278+
case Variant:
279+
column.template = ClickHouseTupleValue.of();
280+
break;
276281
default:
277282
break;
278283
}
@@ -398,7 +403,8 @@ protected static int readColumn(String args, int startIndex, int len, String nam
398403
fixedLength = false;
399404
estimatedLength++;
400405
} else if (args.startsWith(matchedKeyword = KEYWORD_TUPLE, i)
401-
|| args.startsWith(matchedKeyword = KEYWORD_OBJECT, i)) {
406+
|| args.startsWith(matchedKeyword = KEYWORD_OBJECT, i)
407+
|| args.startsWith(matchedKeyword = KEYWORD_VARIANT, i)) {
402408
int index = args.indexOf('(', i + matchedKeyword.length());
403409
if (index < i) {
404410
throw new IllegalArgumentException(ERROR_MISSING_NESTED_TYPE);
@@ -410,12 +416,17 @@ protected static int readColumn(String args, int startIndex, int len, String nam
410416
if (c == ')') {
411417
break;
412418
} else if (c != ',' && !Character.isWhitespace(c)) {
419+
String columnName = "";
413420
i = readColumn(args, i, endIndex, "", nestedColumns);
414421
}
415422
}
416423
if (nestedColumns.isEmpty()) {
417424
throw new IllegalArgumentException("Tuple should have at least one nested column");
418425
}
426+
if (matchedKeyword.equals(KEYWORD_VARIANT)) {
427+
nestedColumns.sort(Comparator.comparing(o -> o.getDataType().name()));
428+
nestedColumns.forEach(c -> c.columnName = "v." + c.getDataType().name());
429+
}
419430
column = new ClickHouseColumn(ClickHouseDataType.valueOf(matchedKeyword), name,
420431
args.substring(startIndex, endIndex + 1), nullable, lowCardinality, null, nestedColumns);
421432
for (ClickHouseColumn n : nestedColumns) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ public enum ClickHouseDataType {
101101
Nothing(Object.class, false, true, false, 0, 0, 0, 0, 0, true),
102102
SimpleAggregateFunction(String.class, true, true, false, 0, 0, 0, 0, 0, false),
103103
// implementation-defined intermediate state
104-
AggregateFunction(String.class, true, true, false, 0, 0, 0, 0, 0, true);
104+
AggregateFunction(String.class, true, true, false, 0, 0, 0, 0, 0, true),
105+
Variant(List.class, true, true, false, 0, 0, 0, 0, 0, true);
106+
105107

106108
/**
107109
* Immutable set(sorted) for all aliases.

clickhouse-data/src/main/java/com/clickhouse/data/format/ClickHouseRowBinaryProcessor.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,68 @@ public void serialize(ClickHouseValue value, ClickHouseOutputStream output) thro
245245
}
246246
}
247247

248+
public static class VariantDeserializer extends ClickHouseDeserializer.CompositeDeserializer {
249+
private final ClickHouseValue[] values;
250+
public VariantDeserializer(ClickHouseDataConfig config, ClickHouseColumn column,
251+
ClickHouseDeserializer... deserializers) {
252+
super(deserializers);
253+
254+
List<ClickHouseColumn> nestedCols = column.getNestedColumns();
255+
int len = nestedCols.size();
256+
if (deserializers.length != len) {
257+
throw new IllegalArgumentException(
258+
ClickHouseUtils.format("Expect %d deserializers but got %d", len, deserializers.length));
259+
}
260+
values = new ClickHouseValue[len];
261+
for (int i = 0; i < len; i++) {
262+
values[i] = nestedCols.get(i).newValue(config);
263+
}
264+
}
265+
266+
@Override
267+
public ClickHouseValue deserialize(ClickHouseValue ref, ClickHouseInputStream input) throws IOException {
268+
int len = values.length;
269+
Object[] tupleValues = new Object[len];
270+
int ordTypeNum = BinaryStreamUtils.readInt8(input);
271+
for (int i = 0; i < len; i++) {
272+
if (ordTypeNum == i) {
273+
tupleValues[i] = deserializers[i].deserialize(values[i], input).asObject();
274+
} else {
275+
tupleValues[i] = null;
276+
}
277+
}
278+
return ref.update(tupleValues);
279+
}
280+
}
281+
282+
public static class VariantSerializer extends ClickHouseSerializer.CompositeSerializer {
283+
private final ClickHouseValue[] values;
284+
285+
public VariantSerializer(ClickHouseDataConfig config, ClickHouseColumn column,
286+
ClickHouseSerializer... serializers) {
287+
super(serializers);
288+
289+
List<ClickHouseColumn> nestedCols = column.getNestedColumns();
290+
int len = nestedCols.size();
291+
if (serializers.length != len) {
292+
throw new IllegalArgumentException(
293+
ClickHouseUtils.format("Expect %d serializers but got %d", len, serializers.length));
294+
}
295+
values = new ClickHouseValue[len];
296+
for (int i = 0; i < len; i++) {
297+
values[i] = nestedCols.get(i).newValue(config);
298+
}
299+
}
300+
301+
@Override
302+
public void serialize(ClickHouseValue value, ClickHouseOutputStream output) throws IOException {
303+
List<Object> tupleValues = value.asTuple();
304+
for (int i = 0, len = serializers.length; i < len; i++) {
305+
serializers[i].serialize(values[i].update(tupleValues.get(i)), output);
306+
}
307+
}
308+
}
309+
248310
@Override
249311
protected void readAndFill(ClickHouseRecord r) throws IOException {
250312
ClickHouseInputStream in = input;
@@ -512,6 +574,10 @@ public ClickHouseDeserializer getDeserializer(ClickHouseDataConfig config, Click
512574
}
513575
deserializer = new BitmapSerDe(config, column)::deserialize;
514576
break;
577+
case Variant:
578+
deserializer = new VariantDeserializer(config, column,
579+
getDeserializers(config, column.getNestedColumns()));
580+
break;
515581
default:
516582
throw new IllegalArgumentException("Unsupported column:" + column.toString());
517583
}
@@ -668,6 +734,9 @@ public ClickHouseSerializer getSerializer(ClickHouseDataConfig config, ClickHous
668734
}
669735
serializer = new BitmapSerDe(config, column)::serialize;
670736
break;
737+
case Variant:
738+
serializer = new VariantSerializer(config, column, getSerializers(config, column.getNestedColumns()));
739+
break;
671740
default:
672741
throw new IllegalArgumentException("Unsupported column:" + column.toString());
673742
}

clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHouseStatementTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,4 +1521,32 @@ public void testMaxResultsRows() throws SQLException {
15211521
"Unexpected exception: " + e.getMessage());
15221522
}
15231523
}
1524+
1525+
@Test(groups = "integration")
1526+
public void testVariantDataType() throws SQLException {
1527+
String table = "test_variant_type_01";
1528+
Properties props = new Properties();
1529+
props.setProperty("custom_settings", "allow_experimental_variant_type=1");
1530+
props.setProperty(ClickHouseClientOption.COMPRESS.getKey(), "false");
1531+
try (ClickHouseConnection conn = newConnection(props);
1532+
ClickHouseStatement s = conn.createStatement()) {
1533+
1534+
s.execute("DROP TABLE IF EXISTS " + table);
1535+
s.execute("CREATE TABLE " + table +" ( id Variant(UInt32, String, UUID), name String) Engine = MergeTree ORDER BY ()");
1536+
1537+
s.execute("insert into " + table + " values ( 1, 'just number' )");
1538+
s.execute("insert into " + table + " values ( 'i-am-id-01', 'ID as string' ) ");
1539+
s.execute("insert into " + table + " values ( generateUUIDv4(), 'ID as UUID' ) ");
1540+
1541+
try (ResultSet rs = s.executeQuery("SELECT * FROM " + table)) {
1542+
while (rs.next()) {
1543+
Object variantValue = rs.getObject(1);
1544+
Object name = rs.getString(2);
1545+
Object variantSubColumn = rs.getObject("v.String");
1546+
System.out.println("-> " + name + " : " + variantValue);
1547+
System.out.println("sub: " + variantSubColumn);
1548+
}
1549+
}
1550+
}
1551+
}
15241552
}

0 commit comments

Comments
 (0)