blackbeam / rust-mysql-simple

Mysql client library implemented in rust.
Apache License 2.0
661 stars 144 forks source link

In rust, mysql database is connected to mysql database. How can the query result restore the original type data #383

Open tgy3300 opened 3 months ago

tgy3300 commented 3 months ago

I used rust's mysql library to connect to the mysql database, and the result of the query was that every data item was a character type

fn get_row<T: DeserializeOwned + Default>(r: Row) -> T {
        let mut json_obj = json!({});
        for col in r.columns_ref() {
            let col_key = col.name_str().to_string();
            let col_val = if let Value::Bytes(v) = &r[col.name_str().as_ref()] {
                std::str::from_utf8(v).unwrap()
            } else {
                ""
            };

            println!("------{}------{:#?}-----", col_key, col.column_type());

            json_obj[col_key] = col_val.into();
        }
        let res_str = serde_json::to_string_pretty(&json_obj).unwrap();
        let res: T = serde_json::from_str(&res_str).unwrap();
        res
    }

fn info<T>(conn: PooledConn) -> Result<T, std::io::Error> 
where
    T: DeserializeOwned + Default,
{
        let sql = format!("select * from company");
        let result: Option<Row> = conn.query_first(sql).expect("Data query error");

        if let Some(row) = result {
            let res: T = get_row(row);
            return Ok(res);
        }

        Ok(T::default())
}

// This is the structure passed in by calling the info method
#[derive(Deserialize, Default, Debug)]
struct Company {
    id: String,
    name: String,
    status: u32,
    err_info: String,
    account_id: String,
    create_at: String,
    update_at: String,
}

The data obtained by calling the info method is as follows, as if each data is a string, through the printing type, it should be the corresponding data of different types, how to restore the original data, ask for help

Company {
    id: "2017612633062042215",
    name: "Yangyang",
    status: "100",
    err_info: "",
    account_id: "0",
    create_at: "1710993521",
    update_at: "1711000831",
}
------id------MYSQL_TYPE_LONGLONG-----
------name------MYSQL_TYPE_VAR_STRING-----
------status------MYSQL_TYPE_SHORT-----
------err_info------MYSQL_TYPE_VAR_STRING-----
------account_id------MYSQL_TYPE_LONGLONG-----
------create_at------MYSQL_TYPE_LONGLONG-----
------update_at------MYSQL_TYPE_LONGLONG-----

The following is the data table structure https://global.discourse-cdn.com/business5/uploads/rust_lang/original/3X/1/6/1626aaa494901a2b70aaca862aa722a9614a4d47.png

blackbeam commented 3 months ago

Hi. Similar question was recently asked — blackbeam/rust_mysql_common#141

All the query_* methods uses mysql so called "text protocol" (MYSQL_COM_QUERY command) — this "text protocol" works in a way that server converts all the values into their textual representation before sending them back to the client.

If you want binary values being sent to you as-is please use the so called "binary protocol" that is prepared statements.

From another perspective — if you just need properly typed values on your side without thinking about protocols then just convert them to matching rust types. In the case of your struct you can do it via an intermediate tuple or using the FromRow derive macro:

use mysql::prelude::{Queryable, FromRow};

#[derive(Debug, FromRow)]
struct Company {
    id: i64,
    name: String,
    status: i16,
    err_info: Option<String>,
    account_id: i64,
    create_at: i64,
    update_at: i64,
}

fn get_first_company(
    conn: &mut impl Queryable,
) -> Result<Option<Company>, Box<dyn std::error::Error + Send + Sync + 'static>> {
    Ok(conn.query_first("select * from company")?)
}