Recommended postgres bindings

I am looking for Rust/postgres bindings that optimize for the following. Is Diesel the way to go, or is something else recommended? (lower # = higher priority)

[higher priority]

  1. Security. Make it difficult to accidentally write SQL injection attacks.
  2. Performance.
  3. Avoid ORM. Make it blatantly obvious what SQL statements we are executing.
  4. Minimal (rust) macro magic. Don't break IntelliJ features; make it easy to see what the Rust side of the data / functions look like.
  5. Easy to "rapidly" develop applications.

[lower priority]

Thanks!

If you are using an async runtime I feel like sqlx ticks all the boxes.

1 Like

Diesel should fulfill your list quite good, even if it is an "ORM".

  1. Security. Make it difficult to accidentally write SQL injection attacks.

Diesels query DSL is checked at compile time. In addition diesel uses prepared statements internally, which makes it almost impossible to pass escaped parameters to the database. (Technically there are a few exceptions here: You could use the diesel::sql_query and diesel::dsl::sql functions in combination with format!, which might result in an SQL injection. You can easily avoid that by just using the provided bind functions on the return types of these functions.)

  1. Performance.

It's always hard to answer questions for performance in a general way, as that really depends on your workload. According to our Benchmarks, diesel provides the best performance for all the tested rust database crates. The relevant source code is here. Be sure to make your own measurements based on your own workload to decide whether or not these numbers are valid for your use-case.

  1. Avoid ORM. Make it blatantly obvious what SQL statements we are executing.

While diesel provides ORM like features it also makes clear which SQL statements are executed. After all it provides a DSL that closely mirrors SQL.

  1. Minimal (rust) macro magic. Don't break IntelliJ features; make it easy to see what the Rust side of the data / functions look like.

This should be mostly given with diesel. It does not require a built time connection to the database system or other macro magic. That written: Diesel uses some quite complicated trait setup, which might break type inference in some cases (for more complex queries). This is mostly due to the fact that IDE's tend to have an incomplete type solver implementation.

  1. Easy to "rapidly" develop applications.

That's mostly a matter of experience with the corresponding framework.

Arguably the following points are only partially full filled by sqlx:

  1. Security: Due to the fact that sqlx assumes to know queries at compile time it cannot verify dynamic queries. That might include things like queries that build conditional where clauses, queries that include IN expressions and insert statements that perform batch insert statements with a variable number of rows. All of these queries tend to be build via the "dynamic" sqlx::query() function, which just accepts a string, which then encourages string building via format! and similar methods. Overall I feel that this encourages code patterns that can lead to SQL injections if not handled with care.

  2. Performance: See the benchmarks posted above. Sqlx is significantly (by the factor of 2-10) slower than diesel for certain types of queries.

1 Like

I do not fully understand your argument here. I'm going to make a guess an interpolate what you wrote. Please correct me where I am wrong.

  1. There exists a diesel query DSL of the form QueryDsl in diesel::prelude - Rust

  2. Rust/Diesel queries are written by calling functions defined in that trait.

  3. There is a "dumb compiler" (dumb enough to execute mentally) from QueryDSL to SQL.

  4. Therefore, with a little practice, one can read Rust/Diesel and mentally translate to SQL ?

^-- Is this the gist of your argument ? If not, please let me know where I got it wrong.

Yes that's basically what I wanted to say, with the minor difference that there are a few more traits and a few more free standing functions. Also it's possible to use operators like + and - directly in your query.

As for naming/translating to SQL: Almost all methods are named like there SQL equivalent. The only notable exceptions are those SQL features that are reserved keywords in rust. That is mostly IN (ExpressionMethods::eq_any) and WHERE (QueryDsl::filter).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.