3个不稳定版本
0.3.1 | 2020年1月12日 |
---|---|
0.3.0 | 2020年1月10日 |
0.2.0 | 2020年1月1日 |
在#execute-query中排名5
每月下载量42次
用于postgres_query
41KB
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?;
注意Book
字段中的#[row(split = "id")]
属性。为了正确提取值,我们首先需要将行拆分成更小的段。我们通过指定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 许可证 之一许可。
除非您明确声明,否则您有意提交以包含在 rust-postgres-query 中的任何贡献,根据 Apache-2.0 许可证定义,将双重许可如上所述,不附加任何额外条款或条件。
依赖关系
~1.5MB
~35K SLoC