Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
66 changes: 35 additions & 31 deletions lib/ch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,32 @@ defmodule Ch do
@moduledoc "Minimal HTTP ClickHouse client."
alias Ch.{Connection, Query, Result}

@typedoc """
Options shared by both connection startup and query execution.

* `:database` - Database, defaults to `"default"`
* `:username` - Username
* `:password` - User password
* `:settings` - Keyword list of ClickHouse settings
* `:timeout` - HTTP request/receive timeout in milliseconds
"""
@type common_option ::
{:database, String.t()}
| {:username, String.t()}
| {:password, String.t()}
| {:settings, Keyword.t()}
| {:timeout, timeout}

@typedoc """
Options for starting the connection pool.

Includes all keys from `t:common_option/0` and `t:DBConnection.start_option/0` plus:

* `:scheme` - HTTP scheme, defaults to `"http"`
* `:hostname` - server hostname, defaults to `"localhost"`
* `:port` - HTTP port, defaults to `8123`
* `:transport_opts` - options to be given to the transport being used. See `Mint.HTTP1.connect/4` for more info
"""
@type start_option ::
common_option
| {:scheme, String.t()}
Expand All @@ -18,38 +37,36 @@ defmodule Ch do
| DBConnection.start_option()

@doc """
Start the connection process and connect to ClickHouse.

## Options

* `:scheme` - HTTP scheme, defaults to `"http"`
* `:hostname` - server hostname, defaults to `"localhost"`
* `:port` - HTTP port, defaults to `8123`
* `:transport_opts` - options to be given to the transport being used. See `Mint.HTTP1.connect/4` for more info
* `:database` - Database, defaults to `"default"`
* `:username` - Username
* `:password` - User password
* `:settings` - Keyword list of ClickHouse settings
* `:timeout` - HTTP receive timeout in milliseconds
* `:transport_opts` - options to be given to the transport being used. See `Mint.HTTP1.connect/4` for more info
* [`DBConnection.start_option()`](https://hexdocs.pm/db_connection/DBConnection.html#t:start_option/0)
Start the connection pool process.

See `t:start_option/0` for available options.
"""
@spec start_link([start_option]) :: GenServer.on_start()
def start_link(opts \\ []) do
DBConnection.start_link(Connection, opts)
end

@doc """
Returns a supervisor child specification for a DBConnection pool.
Returns a supervisor child specification for a connection pool.

See `start_link/1` for supported options.
See `t:start_option/0` for supported options.
"""
@spec child_spec([start_option]) :: :supervisor.child_spec()
def child_spec(opts) do
DBConnection.child_spec(Connection, opts)
end

@typedoc """
Options for executing a query.

Includes all keys from `t:common_option/0` and `t:DBConnection.connection_option/0` plus:

* `:command` - Command tag for the query
* `:headers` - Custom HTTP headers for the request
* `:format` - Custom response format for the request
* `:decode` - Whether to automatically decode the response
* `:multipart` - Whether to send the query as multipart/form-data
"""
@type query_option ::
common_option
| {:command, Ch.Query.command()}
Expand All @@ -66,20 +83,7 @@ defmodule Ch do
Runs a query and returns the result as `{:ok, %Ch.Result{}}` or
`{:error, Exception.t()}` if there was a database error.

## Options

* `:database` - Database
* `:username` - Username
* `:password` - User password
* `:settings` - Keyword list of settings
* `:timeout` - Query request timeout
* `:command` - Command tag for the query
* `:headers` - Custom HTTP headers for the request
* `:format` - Custom response format for the request
* `:decode` - Whether to automatically decode the response
* `:multipart` - Whether to send the query as multipart/form-data
* [`DBConnection.connection_option()`](https://hexdocs.pm/db_connection/DBConnection.html#t:connection_option/0)

See `t:query_option/0` for available options.
"""
@spec query(DBConnection.conn(), iodata, params, [query_option]) ::
{:ok, Result.t()} | {:error, Exception.t()}
Expand Down
9 changes: 9 additions & 0 deletions lib/ch/error.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
defmodule Ch.Error do
@moduledoc "Error struct wrapping ClickHouse error responses."
defexception [:code, :message]

@typedoc """
The Error struct.

## Fields

* `:code` - The ClickHouse numeric error code
* `:message` - The error message returned by the server
"""
@type t :: %__MODULE__{code: pos_integer | nil, message: String.t()}
end
17 changes: 17 additions & 0 deletions lib/ch/query.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ defmodule Ch.Query do
@moduledoc "Query struct wrapping the SQL statement."
defstruct [:statement, :command, :encode, :decode, :multipart]

@typedoc """
The Query struct.

## Fields

* `:statement` - The SQL statement to be executed (as `t:iodata/0`).
* `:command` - The detected or enforced SQL command type (e.g., `:select`, `:insert`).
* `:encode` - Whether to encode parameters (defaults to `true`).
* `:decode` - Whether to decode the response (defaults to `true`).
* `:multipart` - Whether to use `multipart/form-data` for the request (defaults to `false`).
"""
@type t :: %__MODULE__{
statement: iodata,
command: command,
Expand Down Expand Up @@ -65,6 +76,12 @@ defmodule Ch.Query do
|> Enum.map(fn {_, command} -> command end)
|> Enum.reduce(&{:|, [], [&1, &2]})

@typedoc """
Atom representing the type of SQL command.

Derived automatically from the start of the SQL statement (e.g., `"SELECT ..."` -> `:select`),
or provided explicitly via options.
"""
@type command :: unquote(command_union)

defp extract_command(statement)
Expand Down
21 changes: 13 additions & 8 deletions lib/ch/result.ex
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
defmodule Ch.Result do
@moduledoc """
Result struct returned from any successful query. Its fields are:

* `command` - An atom of the query command, for example: `:select`, `:insert`
* `columns` - A list of column names
* `rows` - A list of lists, each inner list corresponding to a row, each element in the inner list corresponds to a column
* `num_rows` - The number of fetched or affected rows
* `headers` - The HTTP response headers
* `data` - The raw iodata from the response
Result struct returned from any successful query.
"""

defstruct [:command, :num_rows, :columns, :rows, :headers, :data]

@typedoc """
The Result struct.

## Fields

* `:command` - An atom of the query command, for example: `:select`, `:insert`
* `:columns` - A list of column names
* `:rows` - A list of lists (each inner list corresponding to a row, each element in the inner list corresponds to a column)
* `:num_rows` - The number of fetched or affected rows
* `:headers` - The HTTP response headers
* `:data` - The raw iodata from the response
"""
@type t :: %__MODULE__{
command: Ch.Query.command() | nil,
num_rows: non_neg_integer | nil,
Expand Down