#query #sql-query #execute-query #write #macro #ease

进程宏 postgres_query_macro

轻松编写和执行SQL查询

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

MIT/Apache

41KB
1K SLoC

postgres-query

Crates.io Build Status License Minimum Rust Version Documentation

该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,
}

一对一关系

在之前的例子中,我们有一个包含 AuthorBook。这被称为多对一关系,因为一本书只有一个作者,但很多书可能拥有同一个作者(或者我们至少这样假设)。那么,如果你有一个包含很多 BookAuthor 呢?我们知道一个作者可以写很多本书,所以这是一个一对多关系。我们也可以为这种情况编写一个提取器。

#[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