Packages
pillar
0.38.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.1
0.34.0
0.33.1
0.33.0
0.32.0
0.31.0
0.30.0
0.29.1
0.29.0
0.28.0
0.27.0
0.26.1
0.26.0
0.25.1
0.25.0
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.2
0.16.1
0.16.0
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.0
Elixir client for ClickHouse, a fast open-source Online Analytical Processing (OLAP) database management system.
Current section
Files
Jump to
Current section
Files
lib/pillar/connection.ex
defmodule Pillar.Connection do
@moduledoc """
Structure with connection config, such as host, port, user, password and other
"""
require Logger
@boolean_to_clickhouse %{
true => 1,
false => 0
}
@type t() :: %{
host: String.t(),
port: integer,
scheme: String.t(),
password: String.t(),
user: String.t(),
database: String.t(),
max_query_size: integer() | nil,
allow_suspicious_low_cardinality_types: boolean() | nil,
version: Version.t()
}
defstruct host: nil,
port: nil,
scheme: nil,
password: nil,
user: nil,
database: nil,
max_query_size: nil,
allow_suspicious_low_cardinality_types: nil,
version: nil
@doc """
Generates Connection from typical connection string:
```
%Pillar.Connection{} = Pillar.Connection.new("https://user:password@localhost:8123/some_database")
# in this case "default" database is used
%Pillar.Connection{} = Pillar.Connection.new("https://localhost:8123")
```
"""
@spec new(String.t()) :: Pillar.Connection.t()
def new(str) do
uri = URI.parse(str)
info = uri.userinfo
[user, password] =
cond do
is_nil(info) -> [nil, nil]
not String.contains?(info, ":") -> [info, nil]
:else -> String.split(info, ":")
end
params = URI.decode_query(uri.query || "")
%__MODULE__{
host: uri.host,
port: uri.port,
scheme: uri.scheme,
database: Path.basename(uri.path || "default"),
user: user,
password: password,
max_query_size: nil_or_string_to_int(params["max_query_size"])
}
|> add_version()
end
def url_from_connection(%__MODULE__{} = connect_config, options \\ %{}) do
params =
reject_nils(%{
password: connect_config.password,
user: connect_config.user,
database: connect_config.database,
max_query_size: connect_config.max_query_size,
allow_suspicious_low_cardinality_types:
@boolean_to_clickhouse[connect_config.allow_suspicious_low_cardinality_types]
})
params = parse_options(params, options)
uri_struct = %URI{
host: connect_config.host,
scheme: connect_config.scheme,
port: connect_config.port,
query: URI.encode_query(params),
path: "/"
}
URI.to_string(uri_struct)
end
defp add_version(conn) do
case Pillar.query(conn, "select version();") do
{:ok, version} ->
version =
String.replace(version, "\n", "")
|> String.split(".")
|> Enum.take(3)
|> Enum.join(".")
%{conn | version: Version.parse!(version)}
{:error, msg} ->
Logger.error("Failed to get version of clickhouse database #{inspect(msg)}")
conn
end
end
defp parse_options(params, %{db_side_batch_insertions: true} = options) do
Map.put(params, "async_insert", 1)
|> parse_options(Map.delete(options, :db_side_batch_insertions))
end
defp parse_options(params, %{allow_experimental_object_type: true} = options) do
Map.put(params, "allow_experimental_object_type", 1)
|> parse_options(Map.delete(options, :allow_experimental_object_type))
end
defp parse_options(params, %{input_format_json_read_numbers_as_strings: true} = options) do
Map.put(params, "input_format_json_read_numbers_as_strings", 1)
|> parse_options(Map.delete(options, :input_format_json_read_numbers_as_strings))
end
defp parse_options(params, _options), do: params
defp nil_or_string_to_int(value) do
if is_nil(value) do
nil
else
String.to_integer(value)
end
end
defp reject_nils(map) do
map
|> Enum.reject(fn {_k, value} -> is_nil(value) end)
|> Map.new()
end
end