if Code.ensure_loaded?(Ecto.Adapters.SQL) do
defmodule AyeSQL.EctoBehaviour do
@moduledoc false
@callback query(
Ecto.Repo.t(),
binary(),
[term()],
keyword()
) ::
{:ok, result}
| {:error, Exception.t()}
when result: %{
:rows => nil | [[term()] | binary()],
:num_rows => non_neg_integer(),
optional(atom()) => any()
}
defmacro __using__(_) do
quote do
@behaviour AyeSQL.EctoBehaviour
@impl AyeSQL.EctoBehaviour
defdelegate query(repo, stmt, args, options), to: Ecto.Adapters.SQL
defoverridable query: 4
end
end
end
defmodule AyeSQL.Ecto do
@moduledoc false
use AyeSQL.EctoBehaviour
end
defmodule AyeSQL.Runner.Ecto do
@moduledoc """
This module defines `Ecto` default adapter.
Can be used as follows:
```elixir
defmodule MyQueries do
use AyeSQL, repo: MyRepo
defqueries("query/my_queries.sql")
end
```
"""
use AyeSQL.Runner
alias AyeSQL.Query
alias AyeSQL.Runner
@impl true
def run(%Query{statement: stmt, arguments: args}, options) do
module = Application.get_env(:ayesql, :ecto_module, AyeSQL.Ecto)
query_options = Keyword.drop(options, [:repo, :into])
repo = get_repo(options)
with {:ok, result} <- module.query(repo, stmt, args, query_options) do
result = Runner.handle_result(result, options)
{:ok, result}
end
end
#########
# Helpers
# Gets repo module.
@spec get_repo(keyword()) :: module() | no_return()
defp get_repo(options) do
repo = options[:repo]
case Code.ensure_loaded(repo) do
{:module, ^repo} ->
repo
_ ->
raise ArgumentError, "Invalid module for Ecto repo: #{inspect(repo)}"
end
end
end
end