postgres_query 0.2.1

Define SQL queries with human-readable parameter names
Documentation

Helper macros and traits built around tokio-postgres to define queries with human readable parameters and return values.

Example

# use tokio_postgres::Client;
# use postgres_query::{query, FromSqlRow, Result};
# fn connect() -> Client { unimplemented!() }
# async fn foo() -> Result<()> {
// Connect to the database
let client: Client = connect(/* ... */);

// Construct the query
let query = query!(
"SELECT age, name FROM people WHERE age >= $min_age",
min_age = 18
);

// Define the structure of the data returned from the query
#[derive(FromSqlRow)]
struct Person {
age: i32,
name: String,
}

// Execute the query
let people: Vec<Person> = query.fetch(&client).await?;

for person in people {
println!("{} is {} years young", person.name, person.age);
}
# Ok(())
# }

Queries

The preferred way of constructing a new Query is through the query! macro. It uses a syntax similar to the format!(...) family of macros from the standard library. The first parameter is the SQL query and is always given as a string literal (this might be relaxed in the future). This string literal may contain parameter bindings on the form $ident where ident is any valid Rust identifier ($abc, $value_123, etc.).

# use postgres_query::query;
let age = 42;
let insert_person = query!(
"INSERT INTO people VALUES ($age, $name)",
name = "John Wick", // Binds "$name" to "John Wick"
age,                // Binds "$age" to the value of `age`
);

During compilation the query is converted into the format expected by PostgreSQL: parameter bindings are converted to using numbers ($1, $2, etc.) and the actual parameter values are put into a 1-indexed array. The code snippet above would be expanded into the following:

# use postgres_query::*;
let age = 42;
let insert_person = Query {
sql: "INSERT INTO people VALUES ($1, $2)",
parameters: vec![&age, &"John Wick"],
};

Data Extraction

In addition to helping you define new queries this crate provides the FromSqlRow trait which makes it easy to extract typed values from the resulting rows. The easiest way to implement this trait for new structs is to use the included derive(FromSqlRow) macro.

  • If used on a tuple struct, values will be extracted from the corresponding columns based on their position in the tuple.
  • If used on a stuct with named fields, values will be extracted from the column with the same name as the field.
# use postgres_query::*;
#[derive(FromSqlRow)]
struct TupleData(i32, String);

#[derive(FromSqlRow)]
struct NamedData {
age: i32,
name: String,
};

Caching queries

From time to time you probably want to execute the same query multiple times, but with different parameters. In times like these we can decrease the load on the database by preparing our queries before executing them. By wrapping a client in a Caching struct this behaviour is automatically provided for all queries that originate from this crate:

# use tokio_postgres::Client;
# use postgres_query::{query, Result, Caching};
# fn connect() -> Client { unimplemented!() }
# async fn foo() -> Result<()> {
// Connect to the database
let client: Client = connect(/* ... */);

// Wrap the client in a query cache
let cached_client = Caching::new(client);

for age in 0..100i32 {
let query = query!("SELECT name, weight FROM people WHERE age = $age", age);

// The query is prepared and cached the first time it's executed.
// All subsequent fetches will use the cached Statement.
let people: Vec<(String, i32)> = query.fetch(&cached_client).await?;

/* Do something with people */
}
# Ok(())
# }