正如标题所示,我希望将各种SQLX类型存储在一个向量中,以便手动构建查询。这是我最好的尝试:
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))
}
}代码的使用方式如下:
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。
发布于 2022-01-12 15:04:39
从概念上来说,我认为你尝试的方法是不可能的。例如,要实现信息,您需要能够从稀薄的空气中返回类型信息,而无需访问可以对其执行动态调度的实际对象。因此,您永远不能将任何dyn …或enumish传递给bind,它希望能够在编译时获得该TypeInfo。
我能想到的唯一实际方法是,对所有要绑定的具体参数都有单独的bind调用。这意味着您需要使用您想要的所有类型的枚举,然后将每个变体绑定到一个单独的匹配臂中,例如:
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来减轻这种痛苦,但我不知道如何做到这一点。
https://stackoverflow.com/questions/70660975
复制相似问题