Getting Started
A taste of what you can do:
use ;
use Model;
use Result;
use Connection;
Exemplar is based around the Model trait, which has its own derive macro.
- See the aformentioned macro's documentation to get started.
- For handling
enums in models, check out thesql_enummacro. - For working with "anonymous" record types, look at the
recordmacro.
Features
- Works with raw SQL, not against it.
- Thin, zero-cost API.
- Most of Exemplar revolves around the
Modeltrait, which gets inlined and monomorphized away before runtime. The resulting code is roughly what you'd write by hand when using purerusqlite. - Designed to be drop-in; reuses
rusqlite's existing types where possible, including itsResulttype alias. - Supports any type that
Deref's torusqlite::Connection, such as transactions or pooled connections.
- Most of Exemplar revolves around the
- Optional test derivation for guarding against drift between your database schema and Rust model types.
- Macros for working with SQL-compatible
enums and "anonymous" record types that map to ad-hoc queries. - Some ability to reflect on/work with
dyn Models at runtime.
If you just need to CRUD some Rust data with sqlite and don't want a whole ORM or enterprise-grade DBMS, then Exemplar is for you!
FAQ
"What does Exemplar not do?"
A few key things:
- Schema generation and management. Exemplar is explicitly not an ORM, and it's difficult to represent concepts like foreign keys and migrations
without falling into ORM territory.
- If this is a "must" for you, check out
dieselorsqlx/seaorm, which both support SQLite.
- If this is a "must" for you, check out
- Query generation (excluding
INSERT.) - Interface portability. Only
rusqliteis supported.
"Is it blazing fast?"
Yes. On my machine (according to this benchmark) Exemplar can:
- Insert a non-trivial model type in ~600 nanoseconds (1.6 million rows/sec)
- Query and reconstruct the same type in ~9 microseconds (111,000 rows/sec,
SELECT *'ing from a table with ~8 million rows)
Obviously the credit for this speed goes to the SQLite and rusqlite developers, but I can confidently say that I didn't slow things down!
"How does this compare to serde-rusqlite?"
serde_rusqlite is a clever hack, but it still involved too much contorting and boilerplate for my taste - that's why I created Exemplar.
The pain points I tried to fix were:
- Needing to allocate and juggle a slice of
Stringcolumn names to efficiently deserialize rows. Exemplar statically knows what columns to expect, sofrom_rowcan extract each field in a single line. - Odd design choices for field-less
enums - they are inefficiently serialized asTEXTinstead ofINTEGER. This was nice for debugging, but I figured the faster option should be Exemplar's default. - General
serdeoverhead popping up, both at compile and runtime.
Acknowledgements
rusqlite, for providing the foundation on which this library is built.- David Tolnay, for his various proc macro
incantationscrates.