Current section
Files
Jump to
Current section
Files
README.md
# Gluex
Elixir bindings for the GlueSQL Rust engine, independent of Ecto.
Current release: `0.1.1`.
The Ecto adapter will live in a separate `ecto_gluex` package.
## Installation
Add Gluex to `mix.exs`:
```elixir
defp deps do
[{:gluex, "~> 0.1.1"}]
end
```
## Usage
```elixir
storage = Gluex.Storages.Memory.new()
database = Gluex.new(storage)
Gluex.query(database, "CREATE TABLE users (id INT, name TEXT)")
Gluex.query(database, "INSERT INTO users VALUES (1, 'Jan')")
Gluex.query(database, "SELECT * FROM users")
# [%{"type" => "SELECT", "columns" => ["id", "name"],
# "rows" => [[1, "Jan"]]}]
```
Values are converted directly to BEAM terms by the Rustler NIF. The binding does
not use JSON or Jason at the Elixir API boundary.
## Storage support
| Storage | Status | Notes |
| --- | --- | --- |
| Memory | Supported | Native GlueSQL storage. |
| JSON/JSONL | Supported | Persistent file storage. |
| ETS | Supported | Volatile BEAM storage through the batch NIF bridge. |
| Mnesia | Supported | Transactional BEAM storage through the batch NIF bridge. |
| Composite | Supported | Routes JSON tables to JSON and new tables to ETS or Mnesia. |
| CSV | Not included in `0.1.1` | Requires a dedicated `Store` implementation and write policy. |
GlueSQL is storage-agnostic. Additional adapters can be implemented either as
a native Rust `Store` or as a BEAM bridge using the same callback protocol as
ETS and Mnesia. CSV import can also be handled by loading rows into ETS or
Mnesia when a full SQL/write-capable backend is required.
## JSON storage
```elixir
database = Gluex.new(Gluex.Storages.Json.new("priv/data"))
Gluex.query(database, "CREATE TABLE users (id INT, name TEXT)")
Gluex.query(database, "INSERT INTO users VALUES (1, 'Jan')")
```
This creates `priv/data/users.jsonl` and, for schemaful tables, the table schema
file as well.
## ETS and Mnesia storage
ETS is volatile and local to the bridge process. Mnesia uses a configurable
Mnesia table and defaults to `:ram_copies`:
```elixir
ets_db = Gluex.new(Gluex.Storages.Ets.new())
mnesia_db = Gluex.new(Gluex.Storages.Mnesia.new(copy_type: :ram_copies))
```
Both adapters execute SQL through the same GlueSQL engine and support schema
changes, joins, indexes, and explicit transactions.
## ETS/Mnesia bridge benchmark
The repository includes a reproducible baseline for the cost of crossing from
direct BEAM storage access into the current bridge process:
```bash
mix run bench/ets_mnesia_overhead.exs
```
It compares direct `:ets.tab2list` / `:mnesia.match_object` with the equivalent
bridge operation over 10,000 rows. The row count can be changed with
`GLUEX_BENCH_ROWS=100000`, and the number of timed repetitions with
`GLUEX_BENCH_RUNS=100`. The benchmark warms up each path and reports the
median, avoiding misleading one-shot cold-start results. The reported
percentage is calculated as:
```text
(bridge_time / direct_time - 1) * 100
```
This is a BEAM bridge baseline. SQL execution adds planning and result
materialization on top of these bridge timings.
Both bridges can execute full SQL, including joins between tables in the same
storage:
```elixir
db = Gluex.new(Gluex.Storages.Ets.new())
Gluex.query(db, "CREATE TABLE users (id INT, name TEXT)")
Gluex.query(db, "CREATE TABLE orders (id INT, user_id INT, total INT)")
Gluex.query(db, "INSERT INTO users VALUES (1, 'Jan')")
Gluex.query(db, "INSERT INTO orders VALUES (10, 1, 50)")
Gluex.query(db, """
SELECT users.name, orders.total
FROM users JOIN orders ON users.id = orders.user_id
""")
```
`Gluex.Storages.Composite` routes existing JSON tables to JSON and newly created
tables to ETS (or Mnesia). For Mnesia, pass `backend: :mnesia`. This allows a
normal GlueSQL JOIN across both backends.
ETS and Mnesia also support `CREATE INDEX` and `DROP INDEX`. Indexed scans are
currently implemented in the adapter and use the bridge-backed rows; a later
optimization can move the index lookup itself into native ETS/Mnesia tables.
Both bridges support explicit GlueSQL transactions:
```elixir
Gluex.query(db, "START TRANSACTION")
Gluex.query(db, "INSERT INTO orders VALUES (10, 1, 50)")
Gluex.query(db, "COMMIT") # or ROLLBACK
```