How to retrieve struct with nested structs with sqlx and Rust? - rust

I'm trying to use Rust and sqlx (v0.6.2) to retrieve from DB structs with nested structs.
I tried using the below code but I found out that there is a limit of 9 fields in tuple that I can use with FromRow.
Note:
As you can see I'm using a Box<T> for struct fields.
use sqlx::{postgres::PgRow, FromRow, QueryBuilder, Row};
#[derive(Debug, Default, sqlx::FromRow)]
pub struct PgCoach { //PgCoach has ONLY scalar values
pub team_id: String,
pub id: String,
pub created_at: Time::OffsetDateTime,
pub updated_at: Option<Time::OffsetDateTime>,
pub firstname: Option<String>,
pub lastname: Option<String>,
pub motto: Option<String>,
pub first_case: Option<String>,
pub second_case: Option<String>,
pub third_case: Option<String>,
pub birth_date: Option<Time::OffsetDateTime>,
pub sex: Option<i64>,
pub phone: Option<String>,
pub email_address: Option<String>,
pub address: Option<String>,
pub picture: Option<String>,
pub notes: Option<String>,
}
#[derive(Debug, Default)]
pub struct PgPlayer {
pub team_id: String,
pub id: String,
pub created_at: Time::OffsetDateTime,
pub updated_at: Option<Time::OffsetDateTime>,
pub score: i64,
pub birth_date: Time::OffsetDateTime,
pub code: String,
pub payed: bool,
pub coach_id: String,
pub coach: Option<Box<PgCoach>>,
pub skills: Option<Vec<PgSkill>>,
}
impl FromRow<'_, PgRow> for PgPlayer {
fn from_row(row: &PgRow) -> sqlx::Result<Self> {
let mut res = Self {
team_id: row.get("team_id"),
id: row.get("id"),
created_at: row.get("created_at"),
updated_at: row.get("updated_at"),
score: row.get("score"),
birth_date: row.get("birth_date"),
code: row.get("code"),
payed: row.get("payed"),
coach_id: row.get("coach_id"),
..Default::default()
};
if row.try_get_raw("coach").is_ok() {
res.coach = Some(Box::new(PgCoach {
id: row.try_get::<PgCoach, &str>("coach")?.1,
firstname: row.try_get::<PgCoach, &str>("coach")?.4,
lastname: row.try_get::<PgCoach, &str>("coach")?.5,
..Default::default()
}));
}
Ok(res)
}
}
The error is:
error[E0277]: the trait bound `pg::PgCoach: sqlx::Decode<'_, sqlx::Postgres>` is not satisfied
--> src\crates\project\pg.rs:656:21
|
656 | id: row.try_get::<PgCoach, &str>("coach")?.id,
| ^^^ ------- required by a bound introduced by this call
| |
| the trait `sqlx::Decode<'_, sqlx::Postgres>` is not implemented for `pg::PgCoach`
|
= help: the following other types implement trait `sqlx::Decode<'r, DB>`:
<&'r [u8] as sqlx::Decode<'r, sqlx::Postgres>>
<&'r [u8] as sqlx::Decode<'r, sqlx_core::any::database::Any>>
<&'r sqlx::types::JsonRawValue as sqlx::Decode<'r, DB>>
<&'r str as sqlx::Decode<'r, sqlx::Postgres>>
<&'r str as sqlx::Decode<'r, sqlx_core::any::database::Any>>
<() as sqlx::Decode<'r, sqlx::Postgres>>
<(T1, T2) as sqlx::Decode<'r, sqlx::Postgres>>
<(T1, T2, T3) as sqlx::Decode<'r, sqlx::Postgres>>
and 43 others
note: required by a bound in `sqlx::Row::try_get`
--> C:\Users\Fred\.cargo\registry\src\github.com-1ecc6299db9ec823\sqlx-core-0.6.2\src\row.rs:114:12
|
114 | T: Decode<'r, Self::Database> + Type<Self::Database>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `sqlx::Row::try_get`
I can use whatever SQL query the method needs.
I can return tuple from DB or single columns, I don't care. I only need a way to query with sqlx structs with nested Box<Struct> and I cannot understand how.
How can I retrieve these structs?

Related

Rust Async Graphql Json InputObject Type

I'm trying to use Async Graphql, I want to use sqlx json type in model. In normal api operations the code is running. But when I want to use the async graphql InputObject macro, I get an error. The codes I used are as follows, I couldn't find a solution for the problem.
#[derive(Serialize, Deserialize, Debug, Clone, sqlx::Type)]
#[sqlx(transparent)]
pub struct Meta(Map<String, Value>);
scalar!(Meta);
#[derive(Debug, Serialize, Deserialize, Clone, FromRow, SimpleObject)]
pub struct User {
#[serde(skip_serializing)]
#[graphql(skip)]
pub id: Uuid,
pub name: String,
pub email: String,
#[serde(skip_serializing)]
#[graphql(skip)]
pub password: String,
#[serde(skip_serializing)]
#[graphql(skip)]
pub meta: Json<Meta>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
No problem so far. But the code below gives an error.
#[derive(Debug, Deserialize, Validate, InputObject)]
pub struct RegisterInput {
#[validate(length(min = 4, max = 10))]
pub name: String,
#[validate(email)]
pub email: String,
#[validate(length(min = 6))]
pub password: String,
pub meta: Json<Meta>,
}
InputObject error.
error[E0277]: the trait bound `sqlx::types::Json<Meta>: InputType` is not satisfied
--> src/dto.rs:13:40
|
13 | #[derive(Debug, Deserialize, Validate, InputObject)]
| ^^^^^^^^^^^ the trait `InputType` is not implemented for `sqlx::types::Json<Meta>`
|
= help: the following other types implement trait `InputType`:
Arc<T>
Arc<[T]>
Arc<str>
BTreeMap<K, V>
BTreeSet<T>
Box<T>
Box<[T]>
Box<str>
and 49 others
= note: this error originates in the derive macro `InputObject` (in Nightly builds, run with -Z macro-backtrace for more info)
Can you help on the subject?

How to serialize trait object with rocket::serde?

I've got the following code:
use std::fmt::Debug;
use rocket::serde::{Serialize};
#[serde(crate = "rocket::serde")]
pub trait ComponentDto: Debug + Send + Sync + Serialize {}
use rocket::serde::{Deserialize, Serialize};
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct ParagraphDto {
pub id: i32,
pub uuid: String,
pub user_id: i32,
pub project_id: i32,
pub position: i32,
pub component: Box<dyn ComponentDto>,
pub status: String,
pub created_at: String,
pub updated_at: String,
}
impl ParagraphDto {
How do I serialize trait object using rocket::serde?
Using rockett::serde as a normal struct to trait object results in the following error:
Compiling testa v0.1.0 (/mnt/repository/repository/projects/rust/testa)
error[E0038]: the trait `MyTrait` cannot be made into an object
--> src/main.rs:21:20
|
21 | fn getStruct() -> Box<dyn MyTrait> {
| ^^^^^^^^^^^^^^^^^ `MyTrait` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits. html#object-safety>

How do I store a timezone aware date using Diesel (Rust)?

I would like to store timezone-aware dates in Postgres, but I am struggling figure out how to do so. I have included the chrono feature for diesel in my cargo.toml.
Here is the relevant portion of my schema.rs:
table! {
budgets (id) {
id -> Uuid,
is_shared -> Bool,
is_private -> Bool,
is_deleted -> Bool,
name -> Varchar,
description -> Nullable<Text>,
start_date -> Timestamptz,
end_date -> Timestamptz,
latest_entry_time -> Timestamp,
modified_timestamp -> Timestamp,
created_timestamp -> Timestamp,
}
}
And my model:
#[derive(Debug, Serialize, Deserialize, Associations, Identifiable, Queryable, QueryableByName)]
#[table_name = "budgets"]
pub struct Budget {
pub id: uuid::Uuid,
pub is_shared: bool,
pub is_private: bool,
pub is_deleted: bool,
pub name: String,
pub description: Option<String>,
pub start_date: DateTime<FixedOffset>,
pub end_date: DateTime<FixedOffset>,
pub latest_entry_time: NaiveDateTime,
pub modified_timestamp: NaiveDateTime,
pub created_timestamp: NaiveDateTime,
}
#[derive(Debug, Insertable)]
#[table_name = "budgets"]
pub struct NewBudget<'a> {
pub id: uuid::Uuid,
pub is_shared: bool,
pub is_private: bool,
pub is_deleted: bool,
pub name: &'a str,
pub description: Option<&'a str>,
pub start_date: DateTime<FixedOffset>,
pub end_date: DateTime<FixedOffset>,
pub latest_entry_time: NaiveDateTime,
pub modified_timestamp: NaiveDateTime,
pub created_timestamp: NaiveDateTime,
}
When I try to build, I get this error when I try to use the model:
error[E0277]: the trait bound `chrono::DateTime<chrono::FixedOffset>: FromSql<diesel::sql_types::Timestamptz, Pg>` is not satisfied
--> src/utils/db/budget.rs:20:42
|
20 | let budget = budgets.find(budget_id).first::<Budget>(db_connection)?;
| ^^^^^ the trait `FromSql<diesel::sql_types::Timestamptz, Pg>` is not implemented for `chrono::DateTime<chrono::FixedOffset>`
|
= help: the following implementations were found:
<chrono::DateTime<Utc> as FromSql<diesel::sql_types::Timestamptz, Pg>>
= note: required because of the requirements on the impl of `diesel::Queryable<diesel::sql_types::Timestamptz, Pg>` for `chrono::DateTime<chrono::FixedOffset>`
= note: 2 redundant requirements hidden
= note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Uuid, diesel::sql_types::Bool, diesel::sql_types::Bool, diesel::sql_types::Bool, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>, diesel::sql_types::Timestamptz, diesel::sql_types::Timestamptz, diesel::sql_types::Timestamp, diesel::sql_types::Timestamp, diesel::sql_types::Timestamp), Pg>` for `Budget`
= note: required because of the requirements on the impl of `LoadQuery<_, Budget>` for `diesel::query_builder::SelectStatement<budgets::table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<diesel::expression::operators::Eq<budgets::columns::id, diesel::expression::bound::Bound<diesel::sql_types::Uuid, &uuid::Uuid>>>, query_builder::order_clause::NoOrderClause, query_builder::limit_clause::LimitClause<diesel::expression::bound::Bound<BigInt, i64>>>`
note: required by a bound in `first`
--> /Users/tanner/.cargo/registry/src/github.com-1ecc6299db9ec823/diesel-1.4.8/src/query_dsl/mod.rs:1343:22
|
1343 | Limit<Self>: LoadQuery<Conn, U>,
| ^^^^^^^^^^^^^^^^^^ required by this bound in `first`
Here is the function that the error refers to:
pub fn get_budget_by_id(
db_connection: &DbConnection,
budget_id: &Uuid,
) -> Result<OutputBudget, diesel::result::Error> {
let budget = budgets.find(budget_id).first::<Budget>(db_connection)?;
let loaded_categories = Category::belonging_to(&budget).load::<Category>(db_connection)?;
let output_budget = OutputBudget {
id: budget.id,
is_shared: budget.is_shared,
is_private: budget.is_private,
is_deleted: budget.is_deleted,
name: budget.name,
description: budget.description,
categories: loaded_categories,
start_date: budget.start_date,
end_date: budget.end_date,
latest_entry_time: budget.latest_entry_time,
modified_timestamp: budget.modified_timestamp,
created_timestamp: budget.created_timestamp,
};
Ok(output_budget)
}
I would like to be able to store timestamps from multiple timezones. Can I only use chrono::DateTime<Utc> or am I missing something?

the trait `Queryable<BigInt, _>` is not implemented for `&i64` when using diesel query data

I am learning use rust diesel to select rows from PostgreSQL 13, here is my code:
#[get("/v1/detail/<songId>")]
pub fn detail(songId: &str) -> io::Result<String> {
use schema::songs::dsl::*;
let connection = config::establish_connection();
let results = songs.filter(source_id.eq(songId))
.limit(5)
.load::<Songs>(&connection)
.expect("Error loading posts");
let result_value = serde_json::to_string(&results).unwrap();
let res = ApiResponse {
body: result_value,
..Default::default()
};
let response_json = serde_json::to_string(&res).unwrap();
return Ok(response_json);
}
when I build this code, shows error like this:
error[E0277]: the trait bound `&i64: Queryable<BigInt, _>` is not satisfied
--> src/biz/music/songs.rs:23:10
|
23 | .load::<Songs>(&connection)
| ^^^^ the trait `Queryable<BigInt, _>` is not implemented for `&i64`
the Songs define like this:
#[derive(Insertable,Serialize,Queryable)]
#[table_name="songs"]
pub struct Songs<'a> {
pub id: &'a i64,
pub name: &'a str,
pub artists: &'a str,
pub album_id: &'a i64,
pub publishtime: &'a i64,
pub status: &'a i32,
pub duration: &'a i32,
pub source_id: &'a str,
pub source: &'a i32,
pub created_time: &'a i64,
pub updated_time: &'a i64
}
this is my database dml:
CREATE TABLE public.songs (
id int8 NOT NULL GENERATED ALWAYS AS IDENTITY,
"name" varchar NOT NULL,
artists varchar NOT NULL,
album_id int8 NOT NULL,
publishtime int8 NULL,
status int4 NOT NULL DEFAULT 1,
duration int4 NULL,
source_id varchar NOT NULL,
"source" int4 NOT NULL,
created_time int8 NULL,
updated_time int8 NULL,
CONSTRAINT songs_id_seq_pk PRIMARY KEY (id),
CONSTRAINT unique_songs UNIQUE (name, artists, album_id),
CONSTRAINT unique_songs_source UNIQUE (source, source_id)
);
what should I do to fix this problem?
Queryable is expecting a tuple that looks like (i64, str, str, ...) no (&i64, &str, ..), see https://github.com/diesel-rs/diesel/blob/master/guide_drafts/trait_derives.md.
You have to define Songs like this
#[derive(Serialize,Queryable)]
#[table_name="songs"]
pub struct Songs {
pub id: i64,
pub name: str,
pub artists: str,
pub album_id: i64,
pub publishtime: i64,
pub status: i32,
pub duration: i32,
pub source_id: str,
pub source: i32,
pub created_time: i64,
pub updated_time: i64
}

Using Option<T> with Diesel's Insertable trait

I have the following model:
use diesel::prelude::*;
use crate::schema::category;
#[derive(Debug, Identifiable, Queryable)]
#[table_name = "category"]
pub struct Category {
pub id: i64,
pub name: String,
pub description: String,
pub parent_id: Option<i64>,
}
#[derive(Debug, Insertable)]
#[table_name = "category"]
pub struct NewCategory<'a> {
pub name: &'a str,
pub description: &'a str,
pub parent_id: &'a Option<i64>,
}
and schema.rs:
table! {
category (id) {
id -> Integer,
name -> Text,
description -> Text,
parent_id -> Nullable<Integer>,
}
}
However, when I try to compile this code, I get the following errors:
error[E0277]: the trait bound `std::option::Option<i64>: diesel::Expression` is not satisfied
--> src/models/categories.rs:15:17
|
15 | #[derive(Debug, Insertable)]
| ^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `std::option::Option<i64>`
|
= note: required because of the requirements on the impl of `diesel::Expression` for `&std::option::Option<i64>`
error[E0277]: the trait bound `std::option::Option<i64>: diesel::Expression` is not satisfied
--> src/models/categories.rs:15:17
|
15 | #[derive(Debug, Insertable)]
| ^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `std::option::Option<i64>`
|
= note: required because of the requirements on the impl of `diesel::Expression` for `&'a std::option::Option<i64>`
What do I need to get this to work? I've looked around, but the only similar issue that I've found is when someone had more than 16 columns in their table, which is not the case here.
Modify pub parent_id: &'a Option<i64> to place the &'a inside of the Option: pub parent_id: Option<&'a i64>.

Resources