Description
I was trying to implement inserting a lot of rows in batches and I think I found a limitation in the way parameters are passed to the *_raw
methods. Here's some code to explain what I was trying to do (the code is incomplete, I've added just enough to ilustrate the issue):
struct Data {
id: u16,
data: serde_json::Value,
}
async fn insert_data(cnn:&tokio_postgres::Client, xs:&[Data]) -> Result<()> {
let sql = ... // sql to insert xs.len() rows
let params = ... // create some iterator from xs
cnn.query_raw(sql, prams).await?;
Ok(())
}
Next I tried to implement an interator that returns all the parameters for all rows from xs
. So I created the following:
struct DataParams<'a> {
xs:&'a [Data]
}
impl<'a> Iterator for DataParams<'a> {
type Item = &'a dyn tokio_postgres::types::ToSql;
...
}
But this doesn't work because u16
and serde_json::Value
don't implement ToSql
and I need to convert or wrap them in a type that does implement it, so i cannot return an element with lifetime 'a
. Then I had the following idea:
enum DataParam<'a> {
Id(i32),
Data(tokio_postgres::types::Json(&'a serde_json::Value)),
}
impl<'a> Iterator for DataParams<'a> {
type Item = DataParam<'a>;
...
}
impl<'a> tokio_postgres::types::ToSql for DataParam<'a> {
...
}
but also ends up not working because query_raw
needs an iterator that returns references to dyn tokio_postgres::types::ToSql
, and I don't think that can be achieved with this strategy.
As a workaround I ended up creating a Vec<DataParam<'a>>
and creating the iterator from it, but i started looking at the rust-postgres code to see if query_raw
could be changed to receive an iterator to some type that implements ToSql
. After some investigation I think that can be done but it requires adding:
impl ToSql for &dyn ToSql {
fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
(*self).to_sql_checked(ty, w)
}
fn accepts(_ty: &Type) -> bool {
true
}
to_sql_checked!();
}
that feels a bit wrong.
Is this something that's worth investigating? or maybe I missed something, or the workaround of using a Vec
is good enough?