Packages
clickhousex_test
0.4.1
ClickHouse driver for Elixir (uses HTTP). This package was created to support ecto with version > 3.0
Current section
Files
Jump to
Current section
Files
lib/clickhousex/query.ex
defmodule Clickhousex.Query do
@moduledoc """
Query struct returned from a successfully prepared query.
"""
@type t :: %__MODULE__{
name: iodata,
type: :select | :insert | :alter | :create | :drop,
param_count: integer,
params: iodata | nil,
columns: [String.t()] | nil
}
defstruct name: nil,
statement: "",
type: :select,
params: [],
param_count: 0,
columns: []
def new(statement) do
%__MODULE__{statement: statement}
|> DBConnection.Query.parse([])
end
end
defimpl DBConnection.Query, for: Clickhousex.Query do
alias Clickhousex.HTTPRequest
@values_regex ~r/VALUES/i
@select_query_regex ~r/\bSELECT\b/i
@insert_query_regex ~r/\bINSERT\b/i
@alter_query_regex ~r/\bALTER\b/i
@codec Application.get_env(:clickhousex, :codec, Clickhousex.Codec.JSON)
def parse(%{statement: statement} = query, _opts) do
param_count =
statement
|> String.codepoints()
|> Enum.count(fn s -> s == "?" end)
query = %{query | type: query_type(statement)}
%{query | param_count: param_count}
end
def describe(query, _opts) do
query
end
def encode(%{type: :insert} = query, params, _opts) do
{query_part, post_body_part} = do_parse(query)
encoded_params = @codec.encode(query, post_body_part, params)
HTTPRequest.new()
|> HTTPRequest.with_query_string_data(query_part)
|> HTTPRequest.with_post_data(encoded_params)
end
def encode(query, params, _opts) do
{query_part, _post_body_part} = do_parse(query)
encoded_params = @codec.encode(query, query_part, params)
HTTPRequest.new()
|> HTTPRequest.with_query_string_data(encoded_params)
end
def decode(_query, result, _opts) do
result
end
defp do_parse(%{type: :insert, statement: statement}) do
with true <- Regex.match?(@values_regex, statement),
[fragment, substitutions] <- String.split(statement, @values_regex),
true <- String.contains?(substitutions, "?") do
{fragment <> " FORMAT #{@codec.request_format}", substitutions}
else
_ ->
{statement, ""}
end
end
defp do_parse(%{statement: statement}) do
{statement, ""}
end
defp query_type(statement) do
with {:select, false} <- {:select, Regex.match?(@select_query_regex, statement)},
{:insert, false} <- {:insert, Regex.match?(@insert_query_regex, statement)},
{:alter, false} <- {:alter, Regex.match?(@alter_query_regex, statement)} do
:unknown
else
{statement_type, true} ->
statement_type
end
end
end
defimpl String.Chars, for: Clickhousex.Query do
def to_string(%Clickhousex.Query{statement: statement}) do
IO.iodata_to_binary(statement)
end
end