如何在 Rust 中使用 sqlx 构建安全的动态查询?

问题描述 投票:0回答:2

sqlx 有一个查询生成器。文档可以查看这里

我看到它支持动态构建以下形式的查询:

SELECT * FROM users WHERE (id, username) IN ((1, "test_user_1"), (2, "test_user_2"))

但我有兴趣构建更复杂的查询,例如

SELECT * from users where id = "id" AND username = "username" AND age > "10" AND age < "70" AND last_visited < 12324235435 AND last_visited > 214324324234234

Where 任何 where 子句都是可选的。所以以下也应该动态构建

SELECT * from users where id = "id" AND age > "10" AND last_visited < 12324235435

除了必须自己手动连接 where 字符串之外,我似乎无法找到使用 sqlx 执行此操作的方法

rust rust-sqlx
2个回答
20
投票

我在本地工作有以下内容。当然,我没有你的数据库,但构建的 SQL 看起来是正确的。我只是选择了 postgres,因为你没有指定你实际使用的数据库。

use sqlx::{query_builder::QueryBuilder, Execute};

struct Search {
    id: i64,
    username: Option<String>,
    min_age: Option<i8>,
    max_age: Option<i8>,
}

fn search_query(search: Search) -> String {
    let mut query = QueryBuilder::new("SELECT * from users where id = ");
    query.push_bind(search.id);

    if let Some(username) = search.username {
        query.push(" AND username = ");
        query.push_bind(username);
    }

    if let Some(min_age) = search.min_age {
        query.push(" AND age > ");
        query.push_bind(min_age);
    }

    if let Some(max_age) = search.max_age {
        query.push(" AND age < ");
        query.push_bind(max_age);
    }

    query.build().sql().into()
}

fn main() {
    dbg!(search_query(Search {
        id: 12,
        username: None,
        min_age: None,
        max_age: None,
    })); // "SELECT * from users where id = $1"
    dbg!(search_query(Search {
        id: 12,
        username: Some("Bob".into()),
        min_age: None,
        max_age: None,
    })); // "SELECT * from users where id = $1 AND username = $2"
    dbg!(search_query(Search {
        id: 12,
        username: Some("Bob".into()),
        min_age: Some(10),
        max_age: Some(70),
    })); // "SELECT * from users where id = $1 AND username = $2 AND age > $3 AND age < $4"
}

我没有将

id
设置为可选,但我相信如果没有提供参数,您可以弄清楚如何完全省略
where


0
投票

要添加到@pitaj的答案,如果您想让所有搜索字段都是可选的,您可以这样做:


use sqlx::{query_builder::QueryBuilder, Execute};

#[derive(Default)]
struct Search {
    id: Option<i64>,
    username: Option<String>,
    min_age: Option<i8>,
    max_age: Option<i8>,
}

fn search_query(search: Search) -> String {
    if let (None, None, None, None) = (search.id, &search.username, search.min_age, search.max_age) {
        return "SELECT * from users".into();
    }

    let mut query = QueryBuilder::new("SELECT * from users");

    query.push(" WHERE")

    if let Some(id) = search.id {
        query.push(" id = $1");
        query.push_bind(id);

        // WHERE id = $1
    }

    if let Some(username) = search.username {
        if search.id.is_some() {
            query.push(" AND");

            // WHERE id = $1 AND
        }

        query.push(" username = $2");
        query.push_bind(username);

        // WHERE id = $1 AND username = $2
    }

    if let Some(min_age) = search.min_age {
        if search.id.is_some() || search.username.is_some() {
            query.push(" AND");
        }

        query.push(" age > $3");
        query.push_bind(min_age);

        // WHERE id = $1 AND username = $2 AND age > $3
    }


    if let Some(max_age) = search.max_age {
        if search.id.is_some() || search.username.is_some() || search.min_age.is_some() {
            query.push(" AND");
        }

        query.push(" age < $4");
        query.push_bind(max_age);

        // WHERE id = $1 AND username = $2 AND age > $3 AND age < $4
    }

    query.build().sql().into()
}

fn main() {
    dbg!(search_query(Search::default())); // "SELECT * from users"

    dbg!(search_query(Search {
        id: Some(12),
        username: None,
        min_age: None,
        max_age: None,
    })); // "SELECT * from users where id = $1"

    dbg!(search_query(Search {
        id: Some(12),
        username: Some("Bob".into()),
        min_age: None,
        max_age: None,
    })); // "SELECT * from users where id = $1 AND username = $2"
    dbg!(search_query(Search {
        id: Some(12),
        username: Some("Bob".into()),
        min_age: Some(10),
        max_age: Some(70),
    })); // "SELECT * from users where id = $1 AND username = $2 AND age > $3 AND age < $4"
}
© www.soinside.com 2019 - 2024. All rights reserved.