Axum tokio postgres error on using vec

Hi, I'm got error when doing dynamic update using tokio postgres .

here's the code

#[derive(Debug, Deserialize, Validate)]
pub struct ProfileChange {
    #[serde(default, with = "::serde_with::rust::double_option")]
    pub first_name: Option<Option<String>>,
    #[serde(default, with = "::serde_with::rust::double_option")]
    #[validate(length(min = 3, max = 50, message = "invalid field length"))]
    pub last_name: Option<Option<String>>,
}


#[debug_handler(state=ConnectionPool)]
pub async fn edit_user(
    DatabaseConnection(conn): DatabaseConnection,
    Path(user_id): Path<String>,
    Json(payload): Json<ProfileChange>,
) -> Result<impl IntoResponse> {
    let mut idx = 1;
    let mut fields = String::new();
    let mut user_data: Vec<Box<dyn ToSql + Sync>> = vec![];
    if let Some(first_name) = payload.first_name {
        fields += format!("first_name = ${}", idx).as_str();
        idx += 1;
        user_data.push(if first_name.is_none() {
            Box::new(first_name)
        } else {
            Box::new(first_name.unwrap())
        });
    }

    if let Some(last_name) = payload.last_name {
        fields += format!(
            "{}last_name = ${}",
            if fields.is_empty() { "" } else { ", " },
            idx
        )
        .as_str();
        idx += 1;
        user_data.push(if last_name.is_none() {
            Box::new(last_name)
        } else {
            Box::new(last_name.unwrap())
        });
    }

    user_data.push(Box::new(user_id));
    let query = format!("Update users SET {} WHERE id = ${}", fields, idx);
    let query_params: Vec<&(dyn ToSql + Sync)> =
        user_data.iter().map(|p| p.as_ref()).collect();

    / Error on this line
    conn.execute(query.as_str(), &query_params).await?;
    Ok("Ok")
}

Error due to query_params

conn.execute(query.as_str(), &query_params).await?;

Error message

error: future cannot be sent between threads safely
    |
94  | #[debug_handler(state=ConnectionPool)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `edit_user` is not `Send`
    |
    = help: the trait `Send` is not implemented for `dyn ToSql + Sync`, which is required by `impl std::future::Future<Output = std::result::Result<impl IntoResponse, AppError>>: Send`
note: future is not `Send` as this value is used across an await

Did I miss something when construct the query_params ?

Adding the Send bound?

1 Like

Hi, Thanks for reply

adding send

let query_params: Vec<&(dyn ToSql + Sync + Send)> =
        user_data.iter().map(|p| p.as_ref()).collect();

will raise error

let update = conn.execute(query.as_str(), &query_params).await?;
    |                       -------                 ^^^^^^^^^^^^^ expected `&[&dyn ToSql + Sync]`, found `&Vec<&dyn ToSql + Send + Sync>`

&Vec<&dyn ToSql + Send> still can be accepted (but raise first error) , while &Vec<&dyn ToSql + Send + Sync> does not match the trait

Hi, I played with the code with the Send suggestion. and found way to fix it with below changes

let mut user_data: Vec<Box<(dyn ToSql + Sync + Send)>> = Vec::new();

let query_params: Vec<&(dyn ToSql + Sync)> = user_data
        .iter()
        .map(|p| p.as_ref() as &(dyn ToSql + Sync))
        .collect();

Thank you so much!

I just wonder if this is the correct way for dynamic approach when working with tokio postgres

let mut user_data: Vec<Box<dyn ToSql + Sync>> = vec![];

then need todo the conversion again

let query_params: Vec<&(dyn ToSql + Sync)> = user_data
        .iter()
        .map(|p| p.as_ref() as &(dyn ToSql + Sync))
        .collect();

it's feel like two step for me. is it possible to reduce it to 1 step ?

Well, why do you need user_data to be dynamic in the first place? It seems that you are only dealing with Option<String>.

actually , the API above for request.patch with Option<Option<String>>.
I need it to be dynamic for proceed data with Some(None) or Some(Not None value), while not proceed None

so I can proceed data with below scenario, example of json data:

// update both fields
{"first_name": "xyz", "last_name": null}
// update only 1 field 
{"first_name": null}
{"last_name": "xxx"}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.