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:
defp deps do
[{:gluex, "~> 0.1.1"}]
end
Usage
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
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:
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:
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:
(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:
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:
Gluex.query(db, "START TRANSACTION")
Gluex.query(db, "INSERT INTO orders VALUES (10, 1, 50)")
Gluex.query(db, "COMMIT") # or ROLLBACK