-
Notifications
You must be signed in to change notification settings - Fork 44
/
json.ex
122 lines (96 loc) · 2.58 KB
/
json.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
defmodule Clickhousex.Codec.JSON do
@moduledoc """
`Clickhousex.Codec` implementation for JSON output format.
See [JSON][1], [JSONCompact][2].
[1]: https://clickhouse.tech/docs/en/interfaces/formats/#json
[2]: https://clickhouse.tech/docs/en/interfaces/formats/#jsoncompact
"""
alias Clickhousex.Codec
@behaviour Codec
@impl Codec
defdelegate encode(query, replacements, params), to: Codec.Values
@impl Codec
def request_format do
"Values"
end
@impl Codec
def response_format do
"JSONCompact"
end
@impl Codec
def new do
[]
end
@impl Codec
def append(state, data) do
[state, data]
end
@impl Codec
def decode(response) do
case Jason.decode(response) do
{:ok, %{"meta" => meta, "data" => data, "rows" => row_count}} ->
column_names = Enum.map(meta, & &1["name"])
column_types = Enum.map(meta, & &1["type"])
rows =
for row <- data do
for {raw_value, column_type} <- Enum.zip(row, column_types) do
to_native(column_type, raw_value)
end
|> List.to_tuple()
end
{:ok, %{column_names: column_names, rows: rows, count: row_count}}
end
end
defp to_native(_, nil) do
nil
end
defp to_native(<<"Nullable(", type::binary>>, value) do
type = String.replace_suffix(type, ")", "")
to_native(type, value)
end
defp to_native(<<"Array(", type::binary>>, value) do
type = String.replace_suffix(type, ")", "")
Enum.map(value, &to_native(type, &1))
end
defp to_native("Float" <> _, value) when is_integer(value) do
1.0 * value
end
defp to_native("Int64", value) do
String.to_integer(value)
end
defp to_native("Date", value) do
{:ok, date} = to_date(value)
date
end
defp to_native("DateTime", value) do
[date, time] = String.split(value, " ")
with {:ok, date} <- to_date(date),
{:ok, time} <- to_time(time),
{:ok, naive} <- NaiveDateTime.new(date, time) do
naive
end
end
defp to_native("UInt" <> _, value) when is_bitstring(value) do
String.to_integer(value)
end
defp to_native("Int" <> _, value) when is_bitstring(value) do
String.to_integer(value)
end
defp to_native(_, value) do
value
end
defp to_date(date_string) do
[year, month, day] =
date_string
|> String.split("-")
|> Enum.map(&String.to_integer/1)
Date.new(year, month, day)
end
defp to_time(time_string) do
[h, m, s] =
time_string
|> String.split(":")
|> Enum.map(&String.to_integer/1)
Time.new(h, m, s)
end
end