blackbeam / mysql_async

Asyncronous Rust Mysql driver based on Tokio.
Apache License 2.0
372 stars 113 forks source link

Method Row.get(index) could not convert Null to Option(T) #305

Open manortec opened 2 months ago

manortec commented 2 months ago

I'm writing a web application,and I encountered a problem when using mysql_async. I found that the get or take method of the Row type(Row.get(index))could not correctly convert the Null value to the expected type "None", but other non-Null values ​​could be converted well. The following is my relevant code. This code can be compiled, but a panic error will occur when running this code. At the same time, I also listed a code that can run correctly for comparison. Thank you very much for any help.

#[derive(Debug, Serialize)]
pub struct UserInfo{
    pub guid_user:Option<u64>,
    pub nick_name:Option<String>,
    pub email:Option<String>,
    pub phone:Option<String>,
}

pub async fn  get_info(guid:u64)-> Result<Option<UserInfo>>{
    let mut conn = DB.get().unwrap().get_conn().await?;
    let q="select guid_user,nick_name,email,phone from fp_user where guid_user=?";
    let p=(guid,);
    // let row =conn.exec_first(q,p).await?;
    // let r=row.map(|(guid_user,nick_name,email,phone)|UserInfo{guid_user,nick_name,email,phone});
    let row:Option<Row> =conn.exec_first(q,p).await?;
    let r=row.map(|row|UserInfo{ guid_user: row.get(0), nick_name: row.get(1) ,email:row.get(2), phone: row.get(3) });

    drop(conn);
    Ok(r)
}

When I make a request calling this function, I get the following error:

thread 'tokio-runtime-worker' panicked at /home/allen/.cargo/registry/src/rsproxy.cn-0dccff568467c15b/mysql_common-0.32.1/src/value/convert/mod.rs:122:23:
Could not retrieve `alloc::string::String`: Couldn't convert the value `Null` to a desired type

It should be noted that the fields nick_name and email in my database have null values, and other fields have normal values. As a comparison test, if you replace the two lines below with the two commented out lines in the above code, it will run correctly. For another comparison, if the nick_name and email fields are filled with normal values, or the query does not contain fields with null values, the above code can also run correctly. The result of a correctly executed request is roughly like this.

{
    "code": 0,
    "msg": "OK",
    "data": {
        "guid_user": 1714805969000000,
        "nick_name": null,
        "email": null,
        "phone": "135819131xx"
    }
}

The mysql_async version used in my cargo.toml file is as follows:

mysql_async = { version = "0.34.1", default-features = false, features = ["minimal"]}

The result I expect is that if the type of a certain attribute of my structure is Option (T), then the get or take method of the Row type needs to convert the null value to None. Thank you very much for your answer.

blackbeam commented 2 months ago

Hi. This is because Row::get by itself returns an Option, so if cell type is Option<String>, then the return type of the get function is Option<Option<String>>. Btw, I would call the get function low level, try something like this instead:

let Some((guid_user, nick_name, email, phone)) = conn.exec_first(q,p).await? else {
    return Ok(None);
};
Ok(UserInfo {
    guid_user,
    nick_name,
    email,
    phone,
});
manortec commented 2 months ago

Hi. This is because Row::get by itself returns an Option, so if cell type is Option<String>, then the return type of the get function is Option<Option<String>>. Btw, I would call the get function low level, try something like this instead:

let Some((guid_user, nick_name, email, phone)) = conn.exec_first(q,p).await? else {
    return Ok(None);
};
Ok(UserInfo {
    guid_user,
    nick_name,
    email,
    phone,
});

Thank you very much! I will have a try.