Open-source project
KipData/KiteSQL avatar
KipData/KiteSQL

KiteSQL is a Rust database you call as a function

Embedded relational database and native Rust data API.

754 stars54 forksRustApache-2.0

At a glance

What is it?
KiteSQL is an embedded relational database written in Rust with SQL as its API, four storage backends, a typed ORM, and the unusual distinction that it can be driven from Rust, from a browser through WebAssembly, or from Python.
Who is it for?
KiteSQL suits a Rust project that wants a database inside the process with a plain SQL surface and no service to operate, and it suits a browser or Python workflow that needs the same engine in both places.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 15 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The API is a query, and the model derives the rest

KiteSQL describes itself as a lightweight embedded relational database written in Rust, inspired by MyRocks and SQLite, running inside the application with no external service, and the tagline is SQL as a Function for Rust. The usage model is unusual in one specific way. A struct derives a Model, and the derive takes table and column attributes, so the schema lives next to the Rust type rather than in a migration file.

rust
#[derive(Default, Debug, PartialEq, Model)]
#[model(table = "users")]
struct User {
    #[model(primary_key)]
    id: i32,
    #[model(varchar = 64)]
    name: String,
    #[model(default = "18", index)]
    age: Option<i32>,
}

From there the database is opened through a builder, the model is migrated in, and queries are written either as a typed builder chain or as plain SQL, with the readme showing both in the same function. The typed path is a bind closure where a context builds a from, filter, project and order chain, and the plain path is a run call with a SQL string.

The builder is the same call in Rust, in the browser and in Python

The example function shows the whole flow, and the interesting part is that the plain SQL call sits next to the typed one in the same function.

rust
fn main() -> Result<(), DatabaseError> {
    let mut database = DataBaseBuilder::path("./data").build_rocksdb()?;
    database.migrate::<User>()?;

    database.insert_many([
        User { id: 1, name: "Alice".to_string(), age: Some(18) },
        User { id: 2, name: "Bob".to_string(), age: Some(24) },
    ])?;

The same engine is then reachable from a browser through WebAssembly, where the crate is built for the nodejs target and the database is constructed directly in JavaScript.

js
import { WasmDatabase } from "./pkg/kite_sql.js";

const db = new WasmDatabase();
await db.ddl("create table demo(id int primary key, v int)");
console.log(db.run("select * from demo").rows());

And from Python, which requires the python feature.

python
import kite_sql

db = kite_sql.Database.in_memory()  # or Database(path, backend="lmdb")
db.execute("create table demo(id int primary key, v int)")
print(list(db.run("select * from demo")))

Having one engine with three front ends is unusual and is the reason the crate is built as a cdylib and an rlib in the same package, since the WebAssembly build needs the dynamic library form and the Rust build needs the static one.

Four storage backends, and the choice is a workload decision

All metadata and data live in a key value store, and the builder you call picks which one. RocksDB is the default feature and the readme describes it as stronger for write-heavy workloads. LMDB is behind its own feature and is described as stronger for read-heavy workloads. There is an in-memory backend for tests and temporary data, and an optimistic variant that is RocksDB with optimistic transactions. The feature flags make the trade explicit, since the default feature set in Cargo.toml is time, macros, parser and rocksdb, while lmdb, orm, shell, python, wasm, pprof and spill are all opt-in. That default is worth noting, because a project that assumes the in-memory builder is available will find it is not unless it enables the right flag, and because the orm feature is what turns on the typed model layer.

The benchmark table is the most useful thing in the README

The TPC-C numbers are published inline and they are not flattering to the engine in every row, which is a good sign. The run is 720 seconds single-threaded on an i9-13900HX with 32 GB of KIOXIA EXCERIA PLUS G3, every backend pinned to the same P-core, and the latencies are p90 in microseconds including commit.

The rows are KiteSQL with LMDB at 159616 TpmC, KiteSQL with RocksDB at 48035, SQLite balanced at 67102 and SQLite practical at 67153. On the individual transactions the picture reverses, with SQLite faster on the new-order, payment and order-status latencies, and the delivery transaction is where RocksDB falls apart, at 11055 microseconds against 435 for LMDB and 338 for SQLite.

That table is the reason the backends are described as a workload decision rather than a preference. LMDB reaches more than twice the throughput of RocksDB here and its delivery latency is two orders of magnitude better, while RocksDB loses badly on the delivery transaction. SQLite is ahead of both on the small transactions and behind on throughput. The make target is make tpcc, with a backend flag, and make tpcc-dual cross-checks every statement against SQLite, which is a much better correctness story than a benchmark number alone.

A research codebase with a real test harness

The repository contents show the shape of the work. There is a fuzz/ directory and a build.rs, and the Makefile configures cargo-fuzz with a pinned nightly toolchain, a fuzz target of sql_exec, and a dictionary file, with the arguments and the exit codes that count as a pass spelled out in comments. There is a tests/sqllogictest directory, which the Dockerfile copies into the builder, and SQLLogicTest is a standard way of checking a SQL engine against a set of expected results. There is a benchmarks/ directory with a query_bench target, a tpcc/ directory with its own readme on reproducing the numbers, a docs/ directory with a features page and a transaction isolation page, and a kite_sql_serde_macros/ directory for the derive support. The coverage setup is in the Makefile too, with grcov, llvm coverage flags, and a set of coverage test features that includes the orm. So the project is a library with a serious test story rather than a demo.

Where an embedded engine in Rust stops

The first limit is concurrency across processes. Everything here is an embedded database with a key value store underneath, so there is no server for two machines to talk to, and a project that needs several writers in different processes is looking at a different tool. The second is SQL coverage, and the project is honest about it: the roadmap lists getting SQL 2016 mostly supported as an open issue, and the readme says most of the SQL 2016 syntax is supported, which is a narrower claim. The third is that the ORM is new. The typed model layer is behind the orm feature and has its own readme, and the derive macros in kite_sql_serde_macros are versioned separately from the main crate, so the API you are coding against may move. The fourth is operational maturity. The crate is at 0.5.0, the repository publishes no GitHub releases, and while the licensing is Apache 2.0 with an explicit Open Collective and crates.io presence, the recent version history is short enough that a project with a long support horizon should plan for API churn.

Editorial conclusion

KiteSQL suits a Rust project that wants a database inside the process with a plain SQL surface and no service to operate, and it suits a browser or Python workflow that needs the same engine in both places. It is the wrong tool for a workload with concurrent writers across processes, since the backends are embedded key value stores rather than a networked server, and it is the wrong choice if you need the full SQL standard, since the roadmap still lists getting SQL 2016 mostly supported. Before adopting it, read the TPC-C numbers in the README rather than the summary, since the LMDB and RocksDB backends behave very differently on the delivery transaction, and check the feature flags in Cargo.toml, because the ORM, the shell and the Python bindings are all opt-in. The last push was on 2026-10-08 and the crate version is 0.5.0.

Frequently asked questions

Which storage backends does KiteSQL support?

Four builders are documented: build_rocksdb() for RocksDB, which is the default feature and is described as stronger for write-heavy workloads, build_lmdb() for LMDB, stronger for read-heavy workloads, build_in_memory() for tests and temporary data, and build_optimistic() which is RocksDB with optimistic transactions.

How do I use KiteSQL from a browser or from Python?

For the browser you build the crate with wasm-pack build --release --target nodejs and instantiate WasmDatabase from the generated package. For Python you enable the python feature, which pulls in pyo3, and then use kite_sql.Database.in_memory() or pass a path and backend, and run SQL with db.execute and db.run.

How fast is KiteSQL compared with SQLite?

In the published TPC-C table from a 720 second single-threaded run on an i9-13900HX, KiteSQL with LMDB reached 159616 TpmC against 67102 and 67153 for the two SQLite configurations, while KiteSQL with RocksDB reached 48035. SQLite was faster on the smaller per-transaction latencies. The make tpcc-dual target cross-checks every statement against SQLite.

Official sources

  1. Issues
  2. KipData/KiteSQL on GitHub
  3. License: Apache-2.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/kipdata-kitesql.svg)](https://hysenlabs.com/projects/kipdata-kitesql)

Was this useful?