Thanks for your interest in improving ElyraSQL! This document explains how to set up, make changes, and get them merged.
By participating you agree to abide by our Code of Conduct, and your contributions are licensed under the project's MIT License.
- Report bugs — open an issue with a minimal reproduction.
- Request features — describe the problem you want solved, not just a solution.
- Improve docs — everything under
docs/and the crate rustdoc. - Write code — fixes, features, tests, performance work.
Before starting significant work, please open an issue to discuss the design — it saves everyone time.
ElyraSQL is a Cargo workspace. See docs/architecture.md for the full picture.
| Crate | Responsibility |
|---|---|
elyra-core |
value/type model, errors, comparison, date/decimal, privileges |
elyra-storage |
single-file ACID engine (redb), Db, MVCC Snapshot |
elyra-engine |
parse → plan → execute, sessions/transactions, catalog, indexes |
elyra-olap |
streaming group-aggregation kernel |
elyra-vector |
vector metrics + HNSW index |
elyra-server |
MySQL wire protocol, auth, TLS, prepared statements |
elyra-cli |
the elyrasql binary |
Requires Rust 1.88+.
git clone https://github.com/kwhorne/ElyraSQL.git
cd ElyraSQL
cargo build
cargo testCI runs formatting, linting, build, tests, and an end-to-end smoke test. Run the same locally — they must pass:
cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo build --workspace
cargo test --workspaceThe root justfile provides the same checks and common local workflows without
replacing the underlying Cargo commands:
just # list grouped recipes
just check # formatting, Clippy, and workspace tests
just check-all # also check docs and the isolated testbench
just run # build and run the native release binary
just docker-run # build and run the local Docker imagejust is optional; the documented Cargo, shell, and Docker commands remain the
source of truth.
testbench/sql-dump/ contains a manual differential stress tool that generates
deterministic MySQL dumps and compares imports between MySQL 8.4 and ElyraSQL.
It is intentionally excluded from the root Cargo workspace and CI because it is
a local correctness and investigation tool rather than a product feature or a
stable benchmark.
Run the default schema comparison with just stress, a generated-data run with
just stress-data, or its standalone checks with just stress-check.
See the testbench guide for prerequisites,
profiles, artifacts, and raw commands that do not require just.
Many features are best validated against a real MySQL client. Start the server
and connect with PyMySQL or the mysql CLI:
cargo run --release -p elyra-cli -- serve --listen 127.0.0.1:3307 &
python3 - <<'PY'
import pymysql
c = pymysql.connect(host="127.0.0.1", port=3307, user="root", password="", autocommit=True)
cur = c.cursor()
cur.execute("CREATE TABLE t (id BIGINT PRIMARY KEY, v TEXT)")
cur.execute("INSERT INTO t VALUES (1, 'hi')")
cur.execute("SELECT * FROM t")
print(cur.fetchall())
PY- Branding. Keep user-facing surfaces branded ElyraSQL. Internal engine crate/dependency names (redb, sqlparser, opensrv, …) must not leak into SQL, error messages, the CLI, or the wire handshake.
- Correctness first. Prefer a correct, simple implementation over a fast, subtly wrong one. Add fast paths deliberately and keep a correct fallback.
- Bounded memory. Favor streaming/batched processing over materializing whole tables where practical.
- Be honest in docs. If a feature has limits, document them (see docs/limitations.md). Do not overclaim.
- Tests. Add unit tests for kernels (encoding, aggregation, HNSW, …) and, for user-visible behavior, an end-to-end check against the MySQL protocol.
- Docs. Update
docs/for any user-visible change.
- Write focused commits with clear, imperative messages
(e.g.
Add range index scans). - Explain the why in the body when it isn't obvious.
- Keep PRs scoped to one logical change; open separate PRs for unrelated work.
- Fill out the pull request template and link the issue it addresses.
- Rebase on
mainand ensure CI is green.
The release workflow validates that the tag, Cargo.toml and the CHANGELOG
agree, so a mismatch fails before anything is published. Three things it does
not check, and which have each been forgotten at least once:
-
SERVER_VERSIONincrates/elyra-core/src/lib.rs— the string clients see fromSELECT VERSION(). Nothing fails if it lags; the server just misreports itself. -
testbench/sql-dump/Cargo.lock. The testbench is a separate Cargo workspace, so its lockfile records the workspace crate versions and goes stale on every version bump.scripts/run.shinvokescargo run --locked, so a stale lock means the nightly SQL dump differential fails before it does any work. Refresh it with:(cd testbench/sql-dump && cargo check --offline)This went unnoticed from 1.9.1 to 1.9.4 because nothing ran the harness.
-
Version strings in prose:
README.md(the stable-release line, theSELECT VERSION()example and the Docker tags),docs/installation.md,docs/deployment.md,docs/mysql-compatibility.md.
If a release tightens validation or changes anything a working deployment can
depend on, say so in an upgrade block in docs/installation.md. The version
number alone is not a warning.
Please do not open public issues for vulnerabilities. See SECURITY.md.
The release workflow includes a fail-safe PGO step that generates a
merged.profdata training profile and feeds it to the linker. If anything
fails — missing llvm-tools-preview, training data not generated, benchmark
scripts changed — the step fails visibly and the run is annotated with a
warning, but the build still falls back to [profile.dist] without PGO.
A release built without a valid profile is a supported release. Do not let a profiling hiccup block a release.
The profile is generated by scripts/pgo-build.sh --profile-only using:
- Pre-generated SQL dumps from
scripts/generate-training-sql.py - The OLTP / OLAP / late-materialisation benchmark suite under
bench/
Profile quality tracks training size. The defaults (50k-row SQL dumps,
BENCH_ROWS=50000, OLAP_ROWS=500000, LATEMAT_ROWS=100000) are the
smallest workload at which PGO has been measured to beat a plain dist
build; a thinner profile can make the binary slower on unrepresented
paths, and nothing detects that at build time. Do not shrink them to save
CI minutes.
When something breaks:
- If
scripts/pgo-build.shexits with "no training SQL dumps found", runpython3 scripts/generate-training-sql.py --allto regenerate them. - If a benchmark script signature changed (new flags, renamed arguments),
update the corresponding
python3 bench/*.pyinvocation in the script. - If
llvm-profdatais not found, install the LLVM tools:rustup component add llvm-tools-preview. - If the profile format changes across Rust toolchain upgrades, delete
target/pgo/and re-run the pipeline.
# Generate training SQL data
python3 scripts/generate-training-sql.py --all
# Full pipeline: instrument → train → merge → rebuild
./scripts/pgo-build.sh
# Benchmark the rebuilt binary against a fresh data directory (not the
# training directory, to avoid warm-cache bias):
mkdir -p /tmp/pgo-bench
elyrasql serve --data /tmp/pgo-bench/bench.edb --listen 127.0.0.1:3309 --password "" &
python3 bench/benchmark.py --port 3309 --rows 50000 --password ""For a cold A/B comparison of a baseline vs PGO binary (fresh server and
fresh data directory per run, data deleted afterwards), use
scripts/bench-cold.py.
By contributing, you agree that your contributions are licensed under the MIT License, and that you have the right to submit them.