Using the customized struct to map the SQL query result with Rust Diesel

Sometimes we need to use Diesel1 to do raw SQL query and map the result to a customized struct. To show the usage of this scenario, I have provided an example here:

As the above PR shown, the query is like this:

let q = format!("SELECT UPPER(username) as upper_username, meta, LENGTH(username) as len_username FROM users");

As we can see, the sql query returns some fields that are not corresponding to the table structure, but we want to map the result to a Rust struct. To do so, we can define a customized rust to do the mapping:

#[derive(QueryableByName, Default, Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct SqlUser {
    #[diesel(sql_type = diesel::sql_types::Varchar)]
    upper_username: String,
    #[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Jsonb >)]
    meta: Option<Meta>,
    #[diesel(sql_type = diesel::sql_types::Int4)]
    len_username: i32,
}

As the above code shown, we need to derive the structure with the QueryableByName macro. Here is part of the doc of QueryableByName:

Implements QueryableByName for untyped sql queries, such as that one generated by sql_query To derive this trait, Diesel needs to know the SQL type of each field. It can get the data from the corresponding table type. It uses the snake_case type name with an added s. It is possible to change this default by using #[diesel(table_name = something)]. If you define use the table type, the SQL type will be diesel::dsl::SqlTypeOf<table_name::column_name>. In cases which there are no table type, you can do the same by annotating each field with #[diesel(sql_type = SomeType)].

As the doc says, we need to define the sql_type of each field manually for the Diesel to understand how to map the result to the Rust type, and by default we need to keep the field name same within the returned column names in SQL query.

To run the example, start the project firstly:

 cargo run                                                                                                                                                                                                                                                                                                                                                        01:04:45
    Finished dev [unoptimized + debuginfo] target(s) in 0.11s
     Running `target/debug/axum-playground`
2024-02-24 01:04:48.286 DEBUG main src/main.rs:821: listening on 127.0.0.1:3000

And then access the service like this:

➤ curl http://localhost:3000/find_all_sql_users                                                                                                                                  00:54:46

Here is the reformatted response of the above request:

{
  "r": true,
  "d": [
    {
      "upper_username": "LIWEINAN42",
      "meta": {
        "meta": null,
        "data": {
          "foo": "1",
          "bar": "1"
        }
      },
      "len_username": 10
    },
    {
      "upper_username": "LIWEINAN67",
      "meta": {
        "meta": null,
        "data": {
          "bar": "1",
          "foo": "1"
        }
      },
      "len_username": 10
    }
  ],
  "e": null
}

Above is the description of this feature usage of Diesel.

References

My Github Page: https://github.com/liweinan

Powered by Jekyll and Theme by solid

If you have any question want to ask or find bugs regarding with my blog posts, please report it here:
https://github.com/liweinan/liweinan.github.io/issues