8个版本
0.3.3 | 2020年1月12日 |
---|---|
0.3.2 | 2020年1月12日 |
0.2.1 | 2020年1月2日 |
0.1.1 | 2019年9月10日 |
#2494 in 数据库接口
70KB
1K SLoC
postgres-query
此crate提供方便的宏和特性,帮助编写SQL查询并将结果收集到静态类型结构中。
示例
// Connect to the database
let client: Client = connect(/* ... */);
// Construct the query
let query = query!(
"SELECT name, age 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?;
// Use the results
for person in people {
println!("{} is {} years young", person.name, person.age);
}
特性
提取器
此crate允许您通过给结构体标记 #[derive(FromSqlRow)]
属性来简单地提取查询的结果
#[derive(FromSqlRow)]
struct Book {
id: i32,
title: String,
genre: String,
}
let books: Vec<Book> = query!("SELECT * FROM books")
.fetch(&client)
.await?;
多映射
您还可以从单行中提取多个结构。当您正在连接两个表时,这可能很有用。作为一个激励的例子,我们可以在一个 Book
实例中存储一个 Author
实例,这可以更容易地处理
#[derive(FromSqlRow)]
#[row(split)]
struct Book {
#[row(split = "id")]
id: i32,
title: String,
genre: String,
#[row(flatten, split = "id")]
author: Author,
}
#[derive(FromSqlRow)]
struct Author {
id: i32,
name: String,
birthyear: i32,
}
let books: Vec<Book> = query!(
"SELECT books.*, authors.*
FROM books
INNER JOIN authors ON authors.id = books.id"
)
.fetch(&client)
.await?;
注意 #[row(split = "id")]
属性在 Book
的字段上。为了正确提取值,我们首先将行拆分成更小的段。我们通过指定第一个 id
出现是书籍的一部分,第二个 id
是作者的一部分来实现。其余的由您来完成。
拆分/段将看起来像这样
Splits: id id
Columns: id, title, genre, id, name, birthyear
Segments: +-----Book-----+ +-----Author------+
如果我们想重用已经存在的 Book
,我们可以轻松地这样做
#[derive(FromSqlRow)]
#[row(split)]
struct Listings {
#[row(flatten, split = "id")]
book: Book
#[row(flatten, split = "id")]
author: Author,
}
一对一关系
在前面的示例中,我们有一个包含 Author
的 Book
。这被称为多对一关系,因为一本书只有一个作者,但许多书可能共享同一个作者(或者至少我们这样假设)。如果相反,你有一个包含许多 Book
的 Author
呢?我们知道一个作者可能写许多书,所以这是一个一对多关系。我们也可以为这种情况编写一个提取器。
#[derive(FromSqlRow)]
#[row(split, group)]
struct Author {
#[row(split = "id", key)]
id: i32,
name: String,
birthyear: i32,
#[row(split = "id", merge)]
books: Vec<Book>,
}
#[derive(FromSqlRow)]
struct Book {
id: i32,
title: String,
genre: String,
}
let authors: Vec<Author> = query!(
"SELECT authors.*, books.*
INNER JOIN books ON books.author = authors.id
GROUP BY authors.id"
)
.fetch(&client)
.await?;
动态查询
查询可以由字符串字面量或任何其他 String
动态构建,这在运行时进行。参数绑定也是如此,在后一种情况下可以动态添加。
假设我们想动态地向我们的查询添加过滤器
// We have the query we want to execute
let mut sql = "SELECT * FROM people".to_string();
// and some filters we got from the user.
let age_filter: Option<i32> = Some(32);
let name_filter: Option<&str> = None;
// Then we dynamically build a list of filters and bindings to use:
let mut filters = Vec::new();
let mut bindings = Vec::new();
// We add the filters as needed.
if let Some(age) = age_filter.as_ref() {
filters.push("age > $min_age");
bindings.push(("min_age", age as Parameter));
}
if let Some(name) = name_filter.as_ref() {
filters.push("name LIKE $name");
bindings.push(("name", name as Parameter));
}
// And append them to the query.
if filters.len() > 0 {
sql += &format!(" WHERE {}", filters.join(" AND "));
}
// Then we can use it as normal.
let query: Query = query_dyn!(&sql, ..bindings)?;
许可证
根据您的选择,许可协议为 Apache License, Version 2.0 或 MIT license。
除非您明确声明,否则您有意提交给 rust-postgres-query 的任何贡献,根据 Apache-2.0 许可证定义,将作为上述双重许可,没有任何附加条款或条件。
依赖项
~11MB
~220K SLoC