defmodule Adbc.Database do
@moduledoc """
Documentation for `Adbc.Database`.
Databases are modelled as processes. They required
a driver to be started.
"""
use GenServer
import Adbc.Helper, only: [error_to_exception: 1]
@doc """
Starts a database process.
## Options
* `:driver` (required) - the driver to use for this database.
See `Adbc` module documentation for support drivers and information
* `:process_options` - the options to be given to the underlying
process. See `GenServer.start_link/3` for all options
All other options are given as database options to the underlying driver.
## Examples
Adbc.Database.start_link(
driver: :sqlite,
process_options: [name: MyApp.DB]
)
In your supervision tree it would be started like this:
children = [
{Adbc.Database,
driver: :sqlite,
process_options: [name: MyApp.DB]},
]
"""
def start_link(opts \\ []) do
{driver, opts} = Keyword.pop(opts, :driver, nil)
unless driver do
raise ArgumentError, ":driver option must be specified"
end
{process_options, opts} = Keyword.pop(opts, :process_options, [])
with {:ok, ref} <- Adbc.Nif.adbc_database_new(),
:ok <- init_driver(ref, driver),
:ok <- init_options(ref, opts),
:ok <- Adbc.Nif.adbc_database_init(ref) do
GenServer.start_link(__MODULE__, ref, process_options)
else
{:error, reason} -> {:error, error_to_exception(reason)}
end
end
## Callbacks
@impl true
def init(db) do
Process.flag(:trap_exit, true)
{:ok, db}
end
@impl true
def handle_call({:initialize_connection, conn_ref}, {pid, _}, db) do
case Adbc.Nif.adbc_connection_init(conn_ref, db) do
:ok ->
Process.link(pid)
{:reply, :ok, db}
{:error, reason} ->
{:reply, {:error, reason}, db}
end
end
@impl true
def handle_info(_msg, state) do
{:noreply, state}
end
@impl true
def terminate(_, db) do
Adbc.Nif.adbc_database_release(db)
end
defp init_driver(ref, driver) do
case Adbc.Driver.so_path(driver) do
{:ok, path} -> Adbc.Nif.adbc_database_set_option(ref, "driver", path)
{:error, reason} -> {:error, reason}
end
end
defp init_options(ref, opts) do
Enum.reduce_while(opts, :ok, fn {key, value}, :ok ->
case Adbc.Nif.adbc_database_set_option(ref, to_string(key), to_string(value)) do
:ok -> {:cont, :ok}
{:error, _} = error -> {:halt, error}
end
end)
end
end