SQLx

Running SQL queries is an important part of data science and engineering work, so in this section, you will learn the basics of the SQLx crate, a Rust crate with support to PostgreSQL, SQLite and MySQL. My recommendation is SQLite for simple projects running in a single machine and PostgreSQL for anything beyond that.

Setup database connection

First, begin by installing sqlx-cli tools with cargo install sqlx-cli.

next create a .env at the top level of your Rust project (same folder that Cargo.toml is) containing the configuration for connecting to database, if using SQLite, this is basically the location of the database file, e.g., to store it in the db folder:

DATABASE_URL=sqlite://db/db.sqlite3

For Postgresql this can be:

DATABASE_URL=postgres://pg_user:password@server_address/db_name

Then, you can run sqlx database create to test the configuration and create the database if does not exist yet.

Create your database schema

If you database doesn't have a schema (tables, etc), SQLx can do that for you and keep a record which ones it had created.

For that, create a folder called migrations, include your desired schema (see examples here and here) and when ready run:

sqlx migrate run && cargo run --release --locked

As SQLx keeps track of which schemas it had already applied this command (as well as the database create) is idempotent.

Setup your Rust project to use SQLx

Configure your project to use sqlx by adding the following to your Cargo.toml:

1[package]
2name = "sqlx-example"
3version = "0.1.0"
4edition = "2021"
5
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
8[dependencies]
9sqlx = { version = "0.7", features = [
10 "postgres",
11 "runtime-tokio",
12 "tls-native-tls",
13] }
14dotenvy = "0.15"
15tokio = { version = "1", features = ["full"] }
16futures-lite = "2"

If using SQLite, all you need to do is change postgres with sqlite in the block. The dotenvy is not strictly necessary but will be handy in the section to read the .env file.

SQLx usage examples

Finally, let's see some examples of CRUD SQL queries using SQLx.

1use dotenvy::var;
2use futures_lite::stream::StreamExt;
3use sqlx::postgres::PgPoolOptions;
4use sqlx::Row;
5use tokio::time::{sleep, Duration};
6
7#[tokio::main]
8async fn main() -> Result<(), Box<dyn std::error::Error>> {
9 let database_url = var("DATABASE_URL")
10 .map_err(|e| format!("Failed to get DATABASE_URL: {}", e))?;
11
12 let db_pool = PgPoolOptions::new()
13 .max_connections(5)
14 .connect(database_url.as_str())
15 .await?;
16
17 let some_var = "some_value";
18
19 // get a single row
20 let (some_timestamp,): (i64,) = sqlx::query_as(
21 r#"SELECT MAX(timestamp)
22 FROM trades_raw WHERE security = $1;"#,
23 )
24 .bind(some_var)
25 .fetch_one(&db_pool)
26 .await?;
27
28 let price = 13.4;
29 let security = "AAPL";
30
31 // insert/upsert
32 while let Err(err) = sqlx::query(
33 "INSERT INTO trades_resampled
34 (price, security, timestamp)
35 VALUES ($1, $2, $3)
36 ON CONFLICT (security, timestamp)
37 DO UPDATE SET price = EXCLUDED.price;
38 ;",
39 )
40 .bind(price)
41 .bind(security)
42 .bind(some_timestamp)
43 .execute(&db_pool)
44 .await
45 {
46 println!("Error while inserting data: {:?}", err);
47 // sleep for a while and retry
48 sleep(Duration::from_millis(300)).await;
49 }
50
51 let session_token = "some_token";
52
53 // a simple delete
54 let _ = sqlx::query(
55 "DELETE FROM user_sessions
56 WHERE session_token = ?",
57 )
58 .bind(session_token)
59 .execute(&db_pool)
60 .await;
61
62 // get many rows
63 let sql_query = r#"
64 SELECT price,timestamp FROM some_table
65 WHERE security = $1
66 LIMIT 10 OFFSET 5
67 "#;
68 let mut rows = sqlx::query(sql_query).bind(security).fetch(&db_pool);
69 let mut prices = Vec::new();
70 let mut timestamps = Vec::new();
71 while let Some(row) = rows.try_next().await? {
72 let price: i64 = row.try_get("price")?;
73 let timestamp: i64 = row.try_get("timestamp")?;
74 prices.push(price);
75 timestamps.push(timestamp);
76 }
77
78 Ok(())
79}

Vector search with Postgres and pgvector

See rust-pgvector-example for a didactic example of using Rust SQLx with Postgres for vector search with embeddings generated from Google API.


If you found this project helpful, please consider making a donation.