Packages
exqlite
0.5.1
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.1
0.33.0
0.32.1
0.32.0
0.31.0
0.30.1
0.30.0
0.29.0
0.28.0
0.27.1
0.27.0
0.26.0
0.25.0
0.24.2
0.24.1
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.2
0.16.1
0.16.0
0.15.0
0.14.0
0.13.15
0.13.14
0.13.13
0.13.12
0.13.11
0.13.10
0.13.9
0.13.8
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.9
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.2
0.4.1
0.4.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
An Elixir SQLite3 library
Current section
Files
Jump to
Current section
Files
lib/exqlite/sqlite3.ex
defmodule Exqlite.Sqlite3 do
@moduledoc """
The interface to the NIF implementation.
"""
#
# TODO: If the database reference is closed, any prepared statements should be
# dereferenced as well. It is entirely possible that an application does
# not properly remove a stale reference.
#
# Will need to add a test for this and think of possible solution.
#
# TODO: Need to figure out if we can just stream results where we use this
# module as a sink.
alias Exqlite.Sqlite3NIF
@type db() :: reference()
@type statement() :: reference()
@type reason() :: atom() | String.t()
@doc """
Opens a new sqlite database at the Path provided.
If `path` can be `":memory"` to keep the sqlite database in memory.
"""
@spec open(String.t()) :: {:ok, db()} | {:error, reason()}
def open(path), do: Sqlite3NIF.open(String.to_charlist(path))
@spec close(nil) :: :ok
def close(nil), do: :ok
@doc """
Closes the database and releases any underlying resources.
"""
@spec close(db()) :: :ok | {:error, reason()}
def close(conn), do: Sqlite3NIF.close(conn)
@doc """
Executes an sql script. Multiple stanzas can be passed at once.
"""
@spec execute(db(), String.t()) :: :ok | {:error, reason()}
def execute(conn, sql) do
case Sqlite3NIF.execute(conn, String.to_charlist(sql)) do
:ok -> :ok
{:error, reason} -> {:error, reason}
_ -> {:error, "unhandled error"}
end
end
@doc """
Get the number of changes recently.
**Note**: If triggers are used, the count may be larger than expected.
See: https://sqlite.org/c3ref/changes.html
"""
@spec changes(db()) :: {:ok, integer()}
def changes(conn), do: Sqlite3NIF.changes(conn)
@spec prepare(db(), String.t()) :: {:ok, statement()} | {:error, reason()}
def prepare(conn, sql) do
Sqlite3NIF.prepare(conn, String.to_charlist(sql))
end
@spec bind(db(), statement(), nil) :: :ok | {:error, reason()}
def bind(conn, statement, nil), do: bind(conn, statement, [])
@spec bind(db(), statement(), []) :: :ok | {:error, reason()}
def bind(conn, statement, args) do
Sqlite3NIF.bind(conn, statement, Enum.map(args, &convert/1))
end
@spec columns(db(), statement()) :: {:ok, []} | {:error, reason()}
def columns(conn, statement), do: Sqlite3NIF.columns(conn, statement)
@spec step(db(), statement()) :: :done | :busy | {:row, []}
def step(conn, statement), do: Sqlite3NIF.step(conn, statement)
@spec last_insert_rowid(db()) :: {:ok, integer()}
def last_insert_rowid(conn), do: Sqlite3NIF.last_insert_rowid(conn)
@spec transaction_status(db()) :: {:ok, :idle | :transaction}
def transaction_status(conn), do: Sqlite3NIF.transaction_status(conn)
@doc """
Causes the database connection to free as much memory as it can. This is
useful if you are on a memory restricted system.
"""
@spec shrink_memory(db()) :: :ok | {:error, reason()}
def shrink_memory(conn) do
Sqlite3NIF.execute(conn, String.to_charlist("PRAGMA shrink_memory"))
end
@spec fetch_all(db(), statement()) :: {:ok, []} | {:error, reason()}
def fetch_all(conn, statement) do
# TODO: Should this be done in the NIF? It can be _much_ faster to build a
# list there, but at the expense that it could block other dirty nifs from
# getting work done.
#
# For now this just works
fetch_all(conn, statement, [])
end
defp fetch_all(conn, statement, result) do
case step(conn, statement) do
:busy -> {:error, "Database busy"}
{:error, reason} -> {:error, reason}
:done -> {:ok, result}
{:row, row} -> fetch_all(conn, statement, result ++ [row])
end
end
defp convert(%Date{} = val), do: Date.to_iso8601(val)
defp convert(%DateTime{} = val), do: DateTime.to_iso8601(val)
defp convert(%Time{} = val), do: Time.to_iso8601(val)
defp convert(%NaiveDateTime{} = val), do: NaiveDateTime.to_iso8601(val)
defp convert(val), do: val
end