-
-
Notifications
You must be signed in to change notification settings - Fork 503
Description
It is a bit unclear to me what the intended way to use query_raw
is with a prepared statement where the different parameters have different types, so this issue might be in the wrong path.
Consider the following contrived example
pub async fn query_wrapper(client: &tokio_postgres::Client) -> Result<u64, tokio_postgres::Error> {
let statement = {
let statement = "SELECT table.id FROM table WHERE table.one >= $1 AND table.two >= $2";
client.prepare(statement).await?
};
let params = [&1i32 as &(dyn ToSql), &2i32 as &(dyn ToSql)];
let rows = client.query_raw(&statement, params).await?;
Ok(rows.fold(0, |x, _| async move {x + 1}).await)
}
This is all fine and the query works, but unfortunately the resulting Future is not Send and consequently the following fails to compile
pub async fn background_wrapper(
client: tokio_postgres::Client,
) -> tokio::task::JoinHandle<Result<u64, tokio_postgres::Error>> {
tokio::spawn(async move { query_wrapper(&client).await })
}
Replacing the query_wrapper
with
pub async fn query_wrapper(client: &tokio_postgres::Client) -> Result<u64, tokio_postgres::Error> {
let statement = {
let statement = "SELECT table.id FROM table WHERE table.one >= $1 AND table.two >= $2";
client.prepare(statement).await?
};
let params = [&1i32 as &i32, &2i32 as &i32];
let rows = client.query_raw(&statement, params).await?;
Ok(rows.fold(0, |x, _| async move {x + 1}).await)
}
makes everything work. This is all understandable. The state machine produced by the first query_wrapper
contains a value of type [&dyn ToSql; 2]
, which is not Send.
As far as I can see the only way to pass parameters of different types to query_raw
is to use trait objects.
There appear to be two problems here. One is that there is a value of a non-Send type. This could in principle be solved by using
let params = [&1i32 as &(dyn ToSql + Sync), &2i32 as &(dyn ToSql + Sync)];
and on its own that would work but it does not in combination with query_raw
. We get the following typechecking error
error[E0277]: the size for values of type `dyn ToSql + Sync` cannot be known at compilation time
--> src/lib.rs:11:45
|
11 | let rows = client.query_raw(&statement, params).await?;
| ^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn ToSql + Sync`
= note: required because of the requirements on the impl of `ToSql` for `&dyn ToSql + Sync`
= note: required because of the requirements on the impl of `BorrowToSql` for `&dyn ToSql + Sync`
The reason for this is that the compiler is trying to find out whether &(dyn ToSql + Sync)
implements BorrowToSql
, and it is doing so via the instance
impl<T> BorrowToSql for T
where
T: ToSql
which then does not work.
One solution to this issue is to add another instance of BorrowToSql
,
impl sealed::Sealed for &(dyn ToSql + Sync) {}
impl BorrowToSql for &(dyn ToSql + Sync) {
#[inline]
fn borrow_to_sql(&self) -> &dyn ToSql {
*self
}
}
I am happy to add a PR implementing this if that is desirable. I don't have an overview of the design of the library so I do not know whether this would prejudice any future extensions.