Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add query/4 that can receive a Req.Request #6

Merged
merged 2 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion lib/req_ch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ defmodule ReqCH do
Queries can be performed using both `Req.get/2` or `Req.post/2`, but GET
is "read-only" and commands like `CREATE` or `INSERT` cannot be used with it.
For that reason, by default we perform a `POST` request.
To change that, use `query/2` with a pre-configured `req`.
To change that, use `query/4` with a pre-configured `req`.

A plain query:

Expand Down Expand Up @@ -127,6 +127,59 @@ defmodule ReqCH do
|> Req.post(body: sql_query)
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can fully delete the version with query/3.


@doc """
Performs a query against the ClickHouse API.

This version receives a `Req.Request.t()`, so it won't
create a new one from scratch.

By default, it will use the `http://localhost:8123` as `:base_url`.
You can change that either providing in your Req request, or in passing
down in the options.
See `new/1` for the options.

## Examples

A plain query:

iex> req = ReqCH.new(database: "system")
iex> {:ok, response} = ReqCH.query(req, "SELECT number FROM numbers LIMIT 3", [], [])
iex> response.body
"0\\n1\\n2\\n"

With a specific format:

iex> req = ReqCH.new(database: "system")
iex> {:ok, response} = ReqCH.query(req, "SELECT number FROM numbers LIMIT 3", [], [format: :explorer])
iex> response.body
#Explorer.DataFrame<
Polars[3 x 1]
number u64 [0, 1, 2]
>

Passing SQL params:

iex> req = ReqCH.new(database: "system")
iex> {:ok, response} = ReqCH.query(req, "SELECT number FROM numbers WHERE number > {num:UInt8} LIMIT 3", [num: 5], [])
iex> response.body
"6\\n7\\n8\\n"
"""
@spec query(
Req.t(),
sql_query :: binary(),
sql_query_params :: Map.t() | Keyword.t(),
opts :: Keyword.t()
) :: {:ok, Req.Response.t()} | {:error, binary()}
def query(req, sql_query, sql_query_params, opts)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you delete query/3:

Suggested change
def query(req, sql_query, sql_query_params, opts)
def query(req, sql_query, sql_query_params \\ [], opts \\ [])


def query(%Req.Request{} = req, sql_query, sql_query_params, opts)
when is_binary(sql_query) and is_query_params(sql_query_params) and is_list(opts) do
req
|> attach(opts)
|> put_params(prepare_params(sql_query_params))
|> Req.post(body: sql_query)
end

@doc """
Same as `query/3`, but raises in case of error.
"""
Expand All @@ -142,6 +195,24 @@ defmodule ReqCH do
end
end

@doc """
Same as `query/4`, but raises in case of error.
"""
@spec query(
Req.t(),
sql_query :: binary(),
sql_query_params :: Map.t() | Keyword.t(),
opts :: Keyword.t()
) :: Req.Response.t()
def query!(req, sql_query, sql_query_params, opts)

def query!(%Req.Request{} = req, sql_query, sql_query_params, opts) do
case query(req, sql_query, sql_query_params, opts) do
{:ok, response} -> response
{:error, exception} -> raise exception
end
end

defp run(%Req.Request{private: %{clickhouse_format: _}} = request), do: request

defp run(%Req.Request{} = request) do
Expand Down
57 changes: 57 additions & 0 deletions test/req_ch_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,61 @@ defmodule ReqCHTest do
assert response.body =~ "UNKNOWN_TABLE"
end
end

describe "query/4" do
test "a plain query with defaults" do
req = ReqCH.new(database: "system")

assert {:ok, %Req.Response{} = response} =
ReqCH.query(req, "SELECT number FROM numbers LIMIT 3", [], [])

assert response.status == 200

assert response.body == """
0
1
2
"""
end

test "a query with params and database in opts" do
req = ReqCH.new()

assert {:ok, %Req.Response{} = response} =
ReqCH.query(
req,
"SELECT number FROM numbers WHERE number > {num:UInt8} LIMIT 3",
[num: 25],
database: "system"
)

assert response.status == 200

assert response.body == """
26
27
28
"""
end

test "with format option as :explorer" do
req = ReqCH.new()

assert {:ok, %Req.Response{} = response} =
ReqCH.query(
req,
"SELECT number, number - 2 as less_two from system.numbers LIMIT 10",
[],
format: :explorer
)

assert %Explorer.DataFrame{} = df = response.body

assert Explorer.DataFrame.to_columns(df, atom_keys: true) ==
%{
number: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
less_two: [-2, -1, 0, 1, 2, 3, 4, 5, 6, 7]
}
end
end
end