-
Notifications
You must be signed in to change notification settings - Fork 44
/
values.ex
123 lines (95 loc) · 3 KB
/
values.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
123
defmodule Clickhousex.Codec.Values do
@moduledoc """
Routines for [Values][1] input/output format.
**NB**: This module does not implement `Clickhousex.Codec` behaviour.
[1]: https://clickhouse.tech/docs/en/interfaces/formats/#data-format-values
"""
alias Clickhousex.Query
def encode(%Query{param_count: 0, type: :insert}, _, []) do
# An insert query's arguments go into the post body and the query part goes into the query string.
# If we don't have any arguments, we don't have to encode anything, but we don't want to return
# anything here because we'll duplicate the query into both the query string and post body
""
end
def encode(%Query{param_count: 0, statement: statement}, _, []) do
statement
end
def encode(%Query{param_count: 0}, _, _) do
raise ArgumentError, "Extra params! Query doesn't contain '?'"
end
def encode(%Query{param_count: param_count} = query, query_text, params) do
if length(params) != param_count do
raise ArgumentError,
"The number of parameters does not correspond to the number of question marks!"
end
query_parts = String.split(query_text, "?")
weave(query, query_parts, params)
end
defp weave(query, query_parts, params) do
weave(query, query_parts, params, [])
end
defp weave(_query, [part], [], acc) do
Enum.reverse([part | acc])
end
defp weave(query, [part | parts], [param | params], acc) do
weave(query, parts, params, [encode_param(query, param), part | acc])
end
@doc false
defp encode_param(query, param) when is_list(param) do
values = Enum.map_join(param, ",", &encode_param(query, &1))
case query.type do
:select ->
# We pass lists to in clauses, and they shouldn't have brackets around them.
values
_ ->
"[" <> values <> "]"
end
end
defp encode_param(_query, param) when is_integer(param) do
Integer.to_string(param)
end
defp encode_param(_query, true) do
"1"
end
defp encode_param(_query, false) do
"0"
end
defp encode_param(_query, param) when is_float(param) do
to_string(param)
end
defp encode_param(_query, param) when is_float(param) do
to_string(param)
end
defp encode_param(_query, nil) do
"NULL"
end
defp encode_param(_query, %DateTime{} = datetime) do
iso_date =
datetime
|> DateTime.truncate(:second)
|> DateTime.to_iso8601()
|> String.replace("Z", "")
"'#{iso_date}'"
end
defp encode_param(_query, %NaiveDateTime{} = naive_datetime) do
naive =
naive_datetime
|> NaiveDateTime.truncate(:second)
|> NaiveDateTime.to_iso8601()
"'#{naive}'"
end
defp encode_param(_query, %Date{} = date) do
"'#{Date.to_iso8601(date)}'"
end
defp encode_param(_query, param) do
"'" <> escape(param) <> "'"
end
defp escape(s) do
s
|> String.replace("_", "\_")
|> String.replace("'", "\'")
|> String.replace("%", "\%")
|> String.replace(~s("), ~s(\\"))
|> String.replace("\\", "\\\\")
end
end