defmodule Depo do @moduledoc """ Depo provides lightweight storage and querying capabilities in Elixir by providing a minimal and polished API that builds on the unique advantages of SQLite. You can [read about SQLite's architecture ](https://www.sqlite.org/arch.html) to learn about the SQLite bytecode compiler and other modules within SQLite that you can utilize. Depo provides `create/1` to create a new `Depo.DB` object with which you can interact with the database. ## Usage Example ``` # Open a new in-memory database. {:ok, db} = Depo.create(:memory) Depo.write(db, "CREATE TABLE a (n, t)") # Prepare, cache, and register named statements. Depo.teach(db, new_b: "INSERT INTO a VALUES(?1, 'b')", n2: "SELECT * FROM a WHERE n = 2", ) # Group multiple statements in nestable transactions. Depo.transact(db, fn -> # Write a named statement with bound variables. for i <- 1..4, do: Depo.write(db, :new_b, i) end) Depo.read(db, :n2) == [%{n: 2, t: "b"}] Depo.close(db) ``` """ @doc """ Open a connection to a database and return a new `Depo.DB` object to manage the database connection. There are a few ways you can open a database: - pass a `path` to open an existing on-disk database - pass `create: path` to create and open a database at the path - pass `:memory` to create a new in-memory database """ def open(:memory) do GenServer.start_link(Depo.DB, :memory) end def open(create: path) when is_binary(path) do GenServer.start_link(Depo.DB, create: path) end def open(path) when is_binary(path) do GenServer.start_link(Depo.DB, path) end @doc """ Open an existing database at `path` and return a new `Depo.DB` object to manage the database connection. """ @doc """ Asynchronously write SQL statements to the database. You can pass an atom registered to a cached statement or a string of valid SQL. """ def write(db, sql) do GenServer.cast(db, {:write, sql}) end @doc """ Write an SQL statement to the database after binding the values to the variables in the statement. """ def write(db, sql, values) do GenServer.cast(db, {:write, sql, values}) end @doc """ Synchronously read SQL queries from the database. """ def read(db, sql) do GenServer.call(db, {:read, sql}) end @doc """ Prepare, cache, and register named SQL statements for more efficient repeated use. `statements` should be a keyword list, where the keys are atoms and the values are SQL statements. """ def teach(db, stmts) do GenServer.cast(db, {:teach, stmts}) end @doc """ Wrap any operations within the given closure in a nestable transaction. If any error occurs within, the transaction will be automatically rolled back. You can [read about SQLite's transactions in depth in its documentation.](https://sqlite.org/lang_transaction.html) """ def transact(db, code) do Depo.write(db, "SAVEPOINT _depo_transaction;") code.() Depo.write(db, "RELEASE _depo_transaction;") end @doc """ Safely close the database connection. """ def close(db) do GenServer.stop(db) end end