首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在向量中存储不同的SQLX类型

在向量中存储不同的SQLX类型
EN

Stack Overflow用户
提问于 2022-01-11 02:28:20
回答 1查看 363关注 0票数 1

正如标题所示,我希望将各种SQLX类型存储在一个向量中,以便手动构建查询。这是我最好的尝试:

代码语言:javascript
复制
use sqlx::{Database, Encode, Type};
use sqlx::database::HasArguments;
use sqlx::query::QueryScalar;

pub type SqlVec<'q, DB: Database> = Vec<Box<dyn SqlTrait<'q, DB>>>;

pub trait SqlTrait<'q, DB: Database>: Send + Encode<'q, DB> + Type<DB> {}
impl<'q, DB: Database, U> SqlTrait<'q, DB> for U where U:Send + Encode<'q, DB> + Type<DB> {}

pub trait SqlVecTrait<'q, DB: Database> {
    fn add<T: SqlTrait<'q, DB>>(&mut self, value: T);
}

impl<'q, DB: Database> SqlVecTrait<'q, DB> for SqlVec<'q, DB> {
    fn add<T: SqlTrait<'q, DB>>(&mut self, value: T) {
        self.push(Box::new(value))
    }
}

代码的使用方式如下:

代码语言:javascript
复制
let mut query = "SELECT * FROM stuff WHERE num = $1".to_string();
let mut args = SqlVec::new();
args.add(3);
if condition {
  query += ", code = $2";
  args.add("s");
}
let mut query = sqlx::query(&query);
for value in args.iter() {
   query = query.bind(value);
}
let rows = query.fetch(&mut db_pool);

编译器抱怨说,由于几个不使用DB的方法,无法将self变成对象。在文档中和我无数的搜索中,我还没有找到一种方法将不同的类型存储在一个向量中,这个向量可以在SQLX中传递给bind

EN

回答 1

Stack Overflow用户

发布于 2022-01-12 15:04:39

从概念上来说,我认为你尝试的方法是不可能的。例如,要实现信息,您需要能够从稀薄的空气中返回类型信息,而无需访问可以对其执行动态调度的实际对象。因此,您永远不能将任何dyn …enumish传递给bind,它希望能够在编译时获得该TypeInfo。

我能想到的唯一实际方法是,对所有要绑定的具体参数都有单独的bind调用。这意味着您需要使用您想要的所有类型的枚举,然后将每个变体绑定到一个单独的匹配臂中,例如:

代码语言:javascript
复制
use sqlx::{
    database::HasArguments, query::Query, Connection, Database, Encode, SqliteConnection, Type,
};

enum QueryParameter {
    String(String),
    I64(i64),
    Bool(bool),
    // Problem: Do you want to support all of rusts integer types?
    // And what about tuples?
}

pub trait SqlTrait<'q, DB: Database>: Send + Sync {}

macro_rules! query_parameter_from {
    ($t:ty, $v:ident) => {
        impl From<$t> for QueryParameter {
            fn from(f: $t) -> Self {
                Self::$v(f)
            }
        }
    };
}
query_parameter_from! {String, String}
query_parameter_from! {i64, I64}
query_parameter_from! {bool, Bool}

// just an abbreviation
trait TyEnc<'q, DB: Database>: Type<DB> + Encode<'q, DB> {}
impl<'q, DB: Database, U: Type<DB> + Encode<'q, DB>> TyEnc<'q, DB> for U {}

// Changed from a type declaration to a struct because trait+impl is too much typing
pub struct SqlVec(Vec<QueryParameter>);

impl SqlVec {
    fn add<T: Into<QueryParameter>>(&mut self, value: T) {
        self.add(value.into())
    }

    fn bind_to<'q, 'p, DB: Database>(
        &'q self,
        mut query: Query<'q, DB, <DB as HasArguments<'q>>::Arguments>,
    ) -> Query<'q, DB, <DB as HasArguments<'q>>::Arguments>
    where
        // This may look weird, but there might be a DB that, e.g., doesn't support strings
        String: TyEnc<'q, DB>,
        i64: TyEnc<'q, DB>,
        bool: TyEnc<'q, DB>,
    {
        for value in self.0.iter() {
            query = match value {
                QueryParameter::String(s) => query.bind(s),
                QueryParameter::I64(u) => query.bind(u),
                QueryParameter::Bool(b) => query.bind(b),
            }
        }
        query
    }

    fn new() -> Self {
        Self(Vec::new())
    }
}

async fn foo() -> Result<(), sqlx::Error> {
    // Adding some stuff to make your example actually compile
    let mut conn = SqliteConnection::connect("sqlite::memory:").await?;
    let condition = true;
    
    let mut query = "SELECT * FROM stuff WHERE num = $1".to_string();
    let mut args = SqlVec::new();
    args.add(3);
    if condition {
        query += ", code = $2";
        args.add("s".to_string());
    }
    let query = sqlx::query(&query);
    let query = args.bind_to(query);
    let _rows = query.fetch(&mut conn);
    Ok(())
}

似乎在内部,类似的东西是正在进行…如果您只想支持一种类型的数据库,那么甚至可以重用它。否则,您可能可以通过使用sqlx提供的一些任何-stuff来减轻这种痛苦,但我不知道如何做到这一点。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70660975

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档