defmodule Prestige do
@moduledoc """
An elixir client for [Prestodb](http://prestodb.github.io/).
"""
alias Prestige.Client
alias Prestige.Session
defmodule Error do
@moduledoc false
defexception [:message, :code, :location, :name, :type, :stack]
end
defmodule BadRequestError do
@moduledoc false
defexception [:message]
end
defmodule ConnectionError do
@moduledoc false
defexception [:message, :code]
end
@type transaction_return :: :commit | {:commit, term} | :rollback | {:rollback, term}
@type query_opts :: [
name: String.t()
]
@spec new_session(keyword) :: Session.t()
defdelegate new_session(opts), to: Session, as: :new
@spec prepare(session :: Session.t(), name :: String.t(), statement :: String.t()) ::
{:ok, Session.t()} | {:error, term}
def prepare(%Session{} = session, name, statement) do
new_session = prepare!(session, name, statement)
{:ok, new_session}
rescue
error -> {:error, error}
end
@spec prepare!(session :: Session.t(), name :: String.t(), statement :: String.t()) :: Session.t()
def prepare!(%Session{} = session, name, statement) do
Client.prepare_statement(session, name, statement)
end
@spec execute(session :: Session.t(), name :: String.t(), args :: list) :: {:ok, Prestige.Result.t()} | {:error, term}
def execute(%Session{} = session, name, args) do
result = execute!(session, name, args)
{:ok, result}
rescue
error -> {:error, error}
end
@spec execute!(session :: Session.t(), name :: String.t(), args :: list) :: Prestige.Result.t()
def execute!(%Session{} = session, name, args) do
Client.execute_statement(session, name, args)
|> Enum.to_list()
|> collapse_results()
end
@spec execute(session :: Session.t(), statement :: String.t()) :: {:ok, Prestige.Result.t()} | {:error, term}
def execute(%Session{} = session, statement) do
result = execute!(session, statement)
{:ok, result}
rescue
error -> {:error, error}
end
@spec execute!(session :: Session.t(), statement :: String.t()) :: Prestige.Result.t()
def execute!(%Session{} = session, statement) do
Client.execute(session, statement)
|> Enum.to_list()
|> collapse_results()
end
@spec close(session :: Session.t(), name :: String.t()) :: {:ok, Session.t()} | {:error, term}
def close(%Session{} = session, name) do
new_session = close!(session, name)
{:ok, new_session}
rescue
error -> {:error, error}
end
@spec close!(session :: Session.t(), name :: String.t()) :: Session.t()
def close!(%Session{} = session, name) do
Client.close_statement(session, name)
end
@spec query(session :: Session.t(), statement :: String.t(), args :: list, opts :: query_opts) ::
{:ok, Prestige.Result.t()} | {:error, term}
def query(%Session{} = session, statement, args \\ [], opts \\ []) do
result = query!(session, statement, args, opts)
{:ok, result}
rescue
error -> {:error, error}
end
@spec query!(session :: Session.t(), statement :: String.t(), args :: list, opts :: query_opts) :: Prestige.Result.t()
def query!(%Session{} = session, statement, args \\ [], opts \\ []) do
name = Keyword.get(opts, :name, "stmt")
Client.execute(session, name, statement, args)
|> Enum.to_list()
|> collapse_results()
end
@spec stream!(session :: Session.t(), statement :: String.t(), args :: list, opts :: query_opts) :: Enumerable.t()
def stream!(%Session{} = session, statement, args \\ [], opts \\ []) do
name = Keyword.get(opts, :name, "stmt")
Client.execute(session, name, statement, args)
end
@spec transaction(session :: Session.t(), function :: (session :: Session.t() -> transaction_return())) :: term
def transaction(%Session{} = session, function) when is_function(function, 1) do
session = Client.start_transaction(session)
result =
try do
function.(session)
rescue
e ->
Client.rollback(session)
reraise e, __STACKTRACE__
end
case result do
:commit ->
Client.commit(session)
:ok
{:commit, value} ->
Client.commit(session)
value
:rollback ->
Client.rollback(session)
:ok
{:rollback, value} ->
Client.rollback(session)
value
end
end
defp collapse_results(results) do
columns = List.first(results).columns
rows = flatten(results)
%Prestige.Result{
columns: columns,
rows: rows,
presto_headers: []
}
end
defp flatten(results) do
Enum.reduce(results, [], fn result, acc ->
Enum.reduce(result.rows, acc, fn row, acc ->
[row | acc]
end)
end)
|> Enum.reverse()
end
end