Drop-in shim over :esqlite3 that mirrors the subset of the Exqlite.Sqlite3 API
used by Connection and Query.
Only Connection and Query hold an alias to this module. All other code
continues to call those two modules unchanged, so swapping the underlying SQLite
NIF is fully contained here.
API mapping
| Exqlite.Sqlite3 | :esqlite3 / notes |
|---|---|
| open(path, opts) | open(uri) – opts encoded as URI params |
| close(conn) | close(conn) |
| execute(conn, sql) | exec(conn, sql) |
| prepare(conn, sql) | prepare(conn, sql) |
| release(conn, stmt) | no-op – GC'd by esqlite |
| bind(stmt, binds) | bind(stmt, binds) |
| step(conn, stmt) | step(stmt) – conn arg dropped |
| reset(stmt) | reset(stmt) |
| fetch_all(conn, stmt) | fetchall(stmt) |
| changes(conn) | |
| multi_step(conn, stmt) | step loop; returns {:rows, rows}/{:done, rows} |
| enable_load_extension(conn, bool) | not supported – always returns error |
| bind_parameter_count(stmt) | column_names heuristic (explain only) |
How esqlite manages prepared statements
When prepare is called, the NIF allocates an esqlite3_stmt resource,
immediately calls enif_release_resource to drop the C-side reference, and
returns the resource wrapped in an Erlang term. From that point the BEAM
garbage collector is the sole owner: when no Erlang process holds a reference
to the term, the GC calls the registered destructor which runs
sqlite3_finalize. The NIF also holds an enif_keep_resource reference from the
statement back to its connection, ensuring the connection is never finalized
before all its statements are. There is no explicit finalize or release call
exposed — lifetime is entirely GC- driven.
Hence the release/1 function is a no-op.
Summary
Functions
Bind positional or named parameters to a prepared statement.
Return the number of bind parameters in a prepared statement.
Build a file: URI from a path with the given opts as query params
Return {:ok, n} for the number of rows changed by the last DML statement.
Enable or disable SQLite extension loading.
Execute a raw SQL statement (no results returned).
Fetch all remaining rows from a prepared statement.
Step through a prepared statement in chunks.
Opens a SQLite database.
Release a prepared statement. esqlite relies on GC; this is a no-op.
Step a prepared statement once.
Types
@type connection() :: :esqlite3.esqlite3()
@type statement() :: :esqlite3.esqlite3_stmt()
Functions
Bind positional or named parameters to a prepared statement.
Accepts the exqlite bind list format including {:blob, value} tagged tuples,
plain integers, binaries, and named {"@name", value} pairs.
@spec bind_parameter_count(statement()) :: non_neg_integer()
Return the number of bind parameters in a prepared statement.
Used only by the explain/2 diagnostic path. esqlite does not expose
sqlite3_bind_parameter_count directly, so we derive it from column names
of the statement. For EXPLAIN QUERY PLAN usage the count just needs to
be non-negative; we fall back to 0.
Build a file: URI from a path with the given opts as query params
See: https://sqlite.org/uri.html#uri_filenames_in_sqlite
Examples
iex> build_uri(":memory:", [])
"file:memory?mode=memory"
iex> build_uri("/my/path/here", [])
"file:/my/path/here?mode=rwc"
iex> build_uri("/my/path/here", mode: :readonly)
"file:/my/path/here?mode=ro"
iex> build_uri("/my/#path?/is-here", mode: :readonly)
"file:/my/%23path%3F/is-here?mode=ro"
iex> build_uri("/my//path//here", mode: :readwrite)
"file:/my/path/here?mode=rwc"
@spec changes(connection()) :: {:ok, non_neg_integer()}
Return {:ok, n} for the number of rows changed by the last DML statement.
@spec close(connection()) :: :ok | {:error, term()}
@spec enable_load_extension(connection(), boolean()) :: :ok | {:error, :not_supported}
Enable or disable SQLite extension loading.
esqlite does not expose sqlite3_enable_load_extension.
Returns {:error, :not_supported} so callers can handle gracefully.
@spec execute(connection(), String.t()) :: :ok | {:error, term()}
Execute a raw SQL statement (no results returned).
@spec fetch_all(connection(), statement()) :: {:ok, [list()]} | {:error, term()}
Fetch all remaining rows from a prepared statement.
Step through a prepared statement in chunks.
Returns {:rows, rows} when there are more rows to fetch, or
{:done, rows} when the cursor is exhausted.
The conn argument is accepted for API compatibility but ignored.
The chunk size matches exqlite's default (50 rows per call).
@spec open( String.t(), keyword() ) :: {:ok, connection()} | {:error, term()}
Opens a SQLite database.
opts follows the exqlite convention:
[mode: [:readonly, :nomutex]]→ opens asfile:<path>?mode=ro[](default) → opens asfile:<path>?mode=rwc
The :memory: path is passed through unchanged.
@spec prepare(connection(), String.t()) :: {:ok, statement()} | {:error, term()}
@spec release(connection(), statement()) :: :ok | {:error, term()}
Release a prepared statement. esqlite relies on GC; this is a no-op.
@spec step(connection(), statement()) :: {:row, list()} | :done | {:error, term()}
Step a prepared statement once.
Returns {:row, row} or :done (matching the exqlite contract).
The conn argument is accepted for API compatibility but ignored.