Rust-Query
lastmod 2024-12-02created 2024-12-021 edits
Safe RDB queries using the Rust Type system
About
Rust-query is a library developed to make this a reality
Rust and databases
- Existing database libraries in Rust lack compile-time guarantees, or are cumbersome to use and not as intuitive as SQL
- Databases play an important role in building crash-resistant software and supporting atomic transactions
- SQL is the standard protocol for interacting with databases, but it is better suited to being generated by computers and is inefficient for people to write by hand
Introducing Rust-query
- rust-query is a database query library deeply integrated with Rust's type system
- Designed to let you perform database operations natively in Rust
Key features and design decisions
- Explicit table aliases: after joining a table, provides a dummy object representing that table (let user = User::join(rows);)
- Null safety: optional values in queries are handled with Rust's Option type
- Intuitive aggregate functions: supports intuitive row-wise aggregation without GROUP BY
- Type-safe foreign key navigation: easily perform implicit joins based on foreign keys (track.album().artist().name())
- Type-safe unique lookups: look up rows with a specific unique constraint (returns Option<Rating>)
- Multi-version schema: all schema version differences can be seen declaratively
- Type-safe migrations: rows can be processed with arbitrary Rust code
- Type-safe unique conflict handling: returns a specific error type on unique constraint conflicts
- Row references bound to the transaction lifetime: row references are valid only while the row exists
- Encapsulated typed row IDs: row numbers are not exposed outside the API
Hacker News comments
- Safe relational database queries using the Rust type system | Hacker News
- The concern with application-defined schemas is that they are validated by the wrong system. The database is the authority on the schema, and every other application layer makes assumptions based on it. Rust's SQLx generates structs based on database types and validates them at compile time, but it doesn't guarantee the same types as the production database. If you design queries against a local Postgres v15 and run Postgres v12 in production, runtime errors can occur. Application-defined schemas give a false sense of safety and impose extra work on engineers.
- SQL isn't perfect, but it has some advantages. Most people know basic SQL, and the documentation of databases like PostgreSQL is written in SQL. External tools also use SQL, and changing a query doesn't require an expensive compile step. SQLx type-checks parameters and lets the database itself validate queries, avoiding type system problems that increase compile time. With new databases a better query language may win, but for existing SQL databases SQLx is the better choice.
- Some disagree with the opinion that SQL should be written by computers. SQL is a high-level language, higher-level than Python or Rust. SQL was designed to be easy to read and use, and is translated into several procedures at compile time. SQL sits at the bottleneck of web development, where state mutation happens. Because SQL is a high-level language, it is hard to optimize. SQL is technical debt; using SQL is 10x more efficient than developing a more suitable API.
- One commenter is glad to see exploration of typesafe-db-access in Rust. Existing libraries don't provide compile-time guarantees, and are verbose or awkward like SQL. diesel provides compile-time guarantees. In the ORM vs. non-ORM debate, they prefer typesafe query builders, and diesel belongs in this category. Rust-query seems likely to lean toward a full ORM.
- One commenter finds the approach of linking schemas and data types interesting. The absence of the Schema enum in the example is not intuitive. It would be clearer if it were defined inside the macro.
- It is confusing that actual row numbers are not exposed in the library API. In a web server, you need to pass row IDs with the data so the frontend can reference and modify the data in other requests.
- Partially agrees that SQL should be written by computers, but SQL is not the most convenient language for code generators to write. A simple plan optimization can completely change the layout of a query. Google's SQL pipe proposal is a slight improvement, but still has the problems of a new query language.
- One commenter has been using SeaQuery, but the documentation isn't sufficient for generating advanced queries. Strongly typed queries can slow down development, so they are considering going back to traditional prepared statements and value binding.
- Migrations through individual row-level manipulation can be very slow to run. For example, on a table with 1 billion rows, an ordinary update statement can take up to an hour. Row-by-row updates would take even longer.