<p><em>Or: how we put the whole RAG stack into one file — and kept it honest.</em></p><p>There's a certain kind of tired that comes from wiring together a "modern" data stack. You want a database. You also want vector search, so you add pgvector. You want good full-text, so you bolt on Elasticsearch. Then a reranker to make the two agree. Then a queue, a cache, a cron. By the time you've got a working app, you're running six services and a small YAML novel just to answer "find me documents that are both about privacy law and semantically close to this question."</p><p>ElyraSQL started from a simple, slightly stubborn idea: what if that all lived in one file?</p><h2>So what is ElyraSQL?</h2><p>ElyraSQL is a robust, MySQL-compatible SQL server written in Rust. Three things make it a little different from the databases you already know:</p><ul><li><p>It speaks the MySQL wire protocol. Connect with <code>mysql</code>, DBeaver, Workbench, sqlx, PyMySQL, JDBC — any MySQL driver, in any language. No custom client, no special SDK. <code>SELECT VERSION()</code> even reports a familiar-looking <code>8.0.0-ElyraSQL-0.9.3</code>.</p></li><li><p>The entire database is a single ACID file (<code>*.edb</code>), built on an MVCC key-value core. Backup is <code>cp</code>. Deployment is one binary. Dev/prod parity is free. Think of SQLite's "one file, no server to babysit" ergonomics — but with a real wire protocol, a query planner, transactions, and replication.</p></li><li><p>It's built for the AI era, not retrofitted for it. Vector columns, HNSW nearest-neighbour, full-text, and OLAP aggregation are all first-class parts of the same engine — not four bolted-on extensions that have to be kept in sync.</p></li></ul><p>It targets the SMB / single-box sweet spot: the enormous number of applications that don't need a five-node cluster and a full-time DBA, but do need something fast, correct, and pleasant to operate.</p><h2>How fast is it, honestly?</h2><p>Let's be upfront, because the project is: these are ElyraSQL's own reproducible numbers, not a controlled head-to-head against MySQL or Postgres. Those systems are mature and heavily tuned; benchmarketing against them with a laptop and a favorable query would be dishonest. What we can show you is that the fast paths are genuinely fast — and you can re-run every number on your own hardware with <code>bench/benchmark.py</code>.</p><p>1,000,000 rows, single client, medians (inside a Docker container):</p><p>Workload Median Bulk insert 1M rows ~5.3 s (~190,000 rows/s) PK point lookup 0.2 ms Selective join (index nested-loop) 0.3 ms Vector ANN, cached (HNSW top-10) 0.4 ms Full scan COUNT (no index) ~205 ms GROUP BY (full aggregation) ~273 ms</p><p>Point lookups, selective joins, and cached vector search are sub-millisecond; bulk ingest sustains six figures of rows per second thanks to batched group-commit. The honest soft spot is large full-table aggregation — which is exactly what the columnar-OLAP follow-up is for. We keep <code>docs/limitations.md</code> current on purpose: single-writer (inherent to the storage core), in-memory materialization for big joins, and so on. No asterisks hidden in the footer.</p><p>The philosophy here matters more than any single number: robustness first, then speed. We'd rather ship a correct feature a release later than a fast, subtly-wrong one.</p><h2>The headline of 0.9.3: AI-native search</h2><p>Here's the part we're genuinely excited about. As of 0.9.3, ElyraSQL can do hybrid search — fusing keyword relevance and vector similarity — and it can generate embeddings in SQL. Together, that's the core of a RAG application in a single MySQL-compatible file.</p><h3>1. HYBRID(...) — full-text + vector, fused</h3><p>The best retrieval systems don't choose between keyword search and semantic search — they combine both. ElyraSQL makes that a first-class primitive:</p><pre><code class="language-sql">SELECT id, title,
       HYBRID(body, 'data privacy law', embedding, '[0.12, 0.03, ...]') AS score
FROM docs
WHERE lang = 'en'                 -- your ordinary structured filter still applies
ORDER BY score DESC
LIMIT 10;
</code></pre><p>Under the hood, <code>HYBRID(text_col, 'query', vec_col, vector)</code>:</p><ol><li><p>ranks documents by vector nearest-neighbour (the HNSW index),</p></li><li><p>ranks them by full-text term relevance (a FULLTEXT index when present, otherwise a scan),</p></li><li><p>fuses the two rankings with Reciprocal Rank Fusion — so a document that's strong in both signals rises to the top,</p></li><li><p>honours your WHERE filter and returns the top-LIMIT, exposing the fused relevance as a normal column.</p></li></ol><p>One query. One file. No Elasticsearch, no pgvector, no external reranker, no glue code holding three systems in an uneasy truce.</p><h3>2. ai_embed('text') — embeddings, right in the query</h3><p>The other half of RAG is making embeddings. Normally that's a round-trip in your application: call an embedding API, get a vector, pass it to the database. ElyraSQL lets you skip the detour:</p><pre><code class="language-sql">-- Generate the query vector inline
SELECT id, title
FROM docs
ORDER BY VEC_DISTANCE(embedding, ai_embed('data privacy law'))
LIMIT 10;

-- Populate embeddings at insert time
INSERT INTO docs VALUES (1, 'some text', ai_embed('some text'));

-- ...and the two features compose beautifully:
SELECT id, HYBRID(body, 'privacy', embedding, ai_embed('privacy')) AS score
FROM docs ORDER BY score DESC LIMIT 10;
</code></pre><p><code>ai_embed()</code> calls an OpenAI-compatible <code>/v1/embeddings</code> endpoint. That means it works with OpenAI and with a local model server — Ollama, LM Studio, llama.cpp, vLLM — because they all speak the same API. Point it at whatever you like:</p><pre><code class="language-bash">ELYRASQL_AI_EMBED_URL=http://localhost:11434/v1/embeddings \
ELYRASQL_AI_EMBED_MODEL=nomic-embed-text \
elyrasql serve
</code></pre><p>Each unique text is embedded once (resolved in an async pre-pass and cached), then treated as an ordinary vector literal — so everything downstream is unchanged and fast. And a small but telling detail: we wired the HTTP client with ureq + ring specifically so the static musl aarch64 build keeps working. The unglamorous stuff is where "robust" lives.</p><h2>The quiet work: it now runs your real tools</h2><p>0.9.x wasn't only headline features. A lot of this cycle was the deeply unsexy work of compatibility — the difference between "passes my tests" and "runs your actual GUI and your actual ORM." We tested against real MySQL clients and a Rust sqlx driver, and fixed what broke:</p><ul><li><p><code>LIKE</code>/<code>ILIKE</code> in WHERE (yes, really — a fundamental gap, now closed),</p></li><li><p>numeric/string comparison coercion, so bound parameters match numeric columns,</p></li><li><p>expressions over aggregates like <code>ROUND(SUM(x), 2)</code> and <code>SUM(a)/COUNT(*)</code>,</p></li><li><p>positional ORDER BY, <code>VERSION()</code>/<code>DATABASE()</code>/<code>USER()</code> as real functions,</p></li><li><p>the SHOW family and a broad <code>information_schema</code> (engines, views, routines, triggers, foreign-key metadata…) so tools can draw your whole schema tree,</p></li><li><p>and opt-in prepared-statement column descriptions (<code>ELYRASQL_STMT_DESCRIBE</code>) so drivers like sqlx can resolve result columns by name.</p></li></ul><p>None of that makes a good demo GIF. All of it is the difference between a toy and a tool.</p><h2>Try it in about 30 seconds</h2><pre><code class="language-bash">docker run -d --name elyrasql -p 3307:3307 \
  -v elyra:/var/lib/elyrasql \
  -e ELYRASQL_USER=root -e ELYRASQL_PASSWORD=secret \
  ghcr.io/kwhorne/elyrasql:0.9.3

mysql -h 127.0.0.1 -P 3307 -u root -psecret
</code></pre><p>Or grab a self-contained static binary (x86_64 or aarch64) from the 0.9.3 release — MIT licensed, Ubuntu 24.04+.</p><h2>Where this is going</h2><p>ElyraSQL is young, and we say so plainly. It isn't trying to dethrone Postgres for a thousand-connection OLTP monster. It's trying to be the database you reach for first when you're building a modern, AI-shaped application and you'd rather ship than assemble a stack: one MySQL-compatible file that does structured data, vector search, full-text, and embeddings — together.</p><p>If that resonates, come kick the tires. Re-run the benchmarks on your own box. Point <code>ai_embed</code> at your local Ollama and build a little hybrid-search demo over your notes. And tell us where it breaks — honestly, that's our favorite kind of feedback.</p><p>ElyraSQL 0.9.3 is out now. One file. MySQL on the wire. Fluent in AI. 🦀</p>