Skip to content

Commit 6601b61

Browse files
committed
continue
1 parent 0042b17 commit 6601b61

2 files changed

Lines changed: 91 additions & 3 deletions

File tree

lib/ch/row_binary.ex

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ defmodule Ch.RowBinary do
129129
{t, Enum.map(ts, &encoding_type/1)}
130130
end
131131

132+
defp encoding_type({:variant = v, ts}) do
133+
{v, Enum.map(ts, &encoding_type/1)}
134+
end
135+
132136
defp encoding_type({:map = m, kt, vt}) do
133137
{m, encoding_type(kt), encoding_type(vt)}
134138
end
@@ -302,6 +306,12 @@ defmodule Ch.RowBinary do
302306
Enum.map(types, fn type -> encode(type, nil) end)
303307
end
304308

309+
def encode({:variant, _types}, nil), do: 255
310+
311+
def encode({:variant, types}, value) do
312+
try_encode_variant(types, 0, value)
313+
end
314+
305315
def encode(:datetime, %NaiveDateTime{} = datetime) do
306316
<<NaiveDateTime.diff(datetime, @epoch_naive_datetime)::32-little>>
307317
end
@@ -453,6 +463,20 @@ defmodule Ch.RowBinary do
453463

454464
defp encode_many_kv([] = done, _key_type, _value_type), do: done
455465

466+
defp try_encode_variant([type | types], idx, value) do
467+
try do
468+
encode(type, value)
469+
else
470+
encoded -> [idx | encoded]
471+
rescue
472+
_e -> try_encode_variant(types, idx + 1, value)
473+
end
474+
end
475+
476+
defp try_encode_variant([], _idx, value) do
477+
raise ArgumentError, "no matching type found for encoding #{inspect(value)} as Variant"
478+
end
479+
456480
@compile {:inline, d: 1}
457481

458482
defp d(?0), do: 0
@@ -606,8 +630,8 @@ defmodule Ch.RowBinary do
606630
{t, Enum.map(ts, &decoding_type/1)}
607631
end
608632

609-
defp decoding_type({:variant = t, ts}) do
610-
{t, Enum.map(ts, &decoding_type/1)}
633+
defp decoding_type({:variant = v, ts}) do
634+
{v, Enum.map(ts, &decoding_type/1)}
611635
end
612636

613637
defp decoding_type({:map = m, kt, vt}) do

test/ch/variant_test.exs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,83 @@
11
defmodule Ch.VariantTest do
22
use ExUnit.Case
33

4+
# https://clickhouse.com/docs/sql-reference/data-types/variant
5+
46
@moduletag :variant
57

68
setup do
79
conn = start_supervised!({Ch, database: Ch.Test.database()})
810
{:ok, conn: conn}
911
end
1012

11-
test "it works", %{conn: conn} do
13+
test "basic", %{conn: conn} do
1214
assert Ch.query!(conn, "select null::Variant(UInt64, String, Array(UInt64))").rows == [[nil]]
1315
assert Ch.query!(conn, "select [1]::Variant(UInt64, String, Array(UInt64))").rows == [[[1]]]
1416
assert Ch.query!(conn, "select 0::Variant(UInt64, String, Array(UInt64))").rows == [[0]]
1517

1618
assert Ch.query!(conn, "select 'Hello, World!'::Variant(UInt64, String, Array(UInt64))").rows ==
1719
[["Hello, World!"]]
1820
end
21+
22+
test "with a table", %{conn: conn} do
23+
# https://clickhouse.com/docs/sql-reference/data-types/variant#creating-variant
24+
Ch.query!(conn, """
25+
CREATE TABLE variant_test (v Variant(UInt64, String, Array(UInt64))) ENGINE = Memory;
26+
""")
27+
28+
on_exit(fn -> Ch.Test.query("DROP TABLE variant_test", [], database: Ch.Test.database()) end)
29+
30+
Ch.query!(
31+
conn,
32+
"INSERT INTO variant_test VALUES (NULL), (42), ('Hello, World!'), ([1, 2, 3]);"
33+
)
34+
35+
assert Ch.query!(conn, "SELECT v FROM variant_test").rows == [
36+
[nil],
37+
[42],
38+
["Hello, World!"],
39+
[[1, 2, 3]]
40+
]
41+
42+
# https://clickhouse.com/docs/sql-reference/data-types/variant#reading-variant-nested-types-as-subcolumns
43+
assert Ch.query!(conn, "SELECT v, v.String, v.UInt64, v.`Array(UInt64)` FROM variant_test;").rows ==
44+
[
45+
[nil, nil, nil, []],
46+
[42, nil, 42, []],
47+
["Hello, World!", "Hello, World!", nil, []],
48+
[[1, 2, 3], nil, nil, [1, 2, 3]]
49+
]
50+
51+
assert Ch.query!(
52+
conn,
53+
"SELECT v, variantElement(v, 'String'), variantElement(v, 'UInt64'), variantElement(v, 'Array(UInt64)') FROM variant_test;"
54+
).rows == [
55+
[nil, nil, nil, []],
56+
[42, nil, 42, []],
57+
["Hello, World!", "Hello, World!", nil, []],
58+
[[1, 2, 3], nil, nil, [1, 2, 3]]
59+
]
60+
end
61+
62+
test "rowbinary", %{conn: conn} do
63+
Ch.query!(conn, """
64+
CREATE TABLE variant_test (v Variant(UInt64, String, Array(UInt64))) ENGINE = Memory;
65+
""")
66+
67+
on_exit(fn -> Ch.Test.query("DROP TABLE variant_test", [], database: Ch.Test.database()) end)
68+
69+
Ch.query!(
70+
conn,
71+
"INSERT INTO variant_test FORMAT RowBinary",
72+
[[nil], [42], ["Hello, World!"], [[1, 2, 3]]],
73+
types: ["Variant(UInt64, String, Array(UInt64))"]
74+
)
75+
76+
assert Ch.query!(conn, "SELECT v FROM variant_test").rows == [
77+
[nil],
78+
[42],
79+
["Hello, World!"],
80+
[[1, 2, 3]]
81+
]
82+
end
1983
end

0 commit comments

Comments
 (0)