How do I store a timezone aware date using Diesel (Rust)? - 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?

Related

How to retrieve struct with nested structs with sqlx and 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?

Diesel derive Insertable causes ambiguous associated type error

So I'm trying to build a rust powered API closely following this tutorial.
My models.rs looks like this:
use diesel;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection as slc;
use crate::schema::{Recipe as RecipeTable,
RecipeMatchIngredient as RecipeMatchIngredientTable,
AllergyMatchIngredient as AllergyMatchIngredientTable,
DietMatchIngredient as DietMatchIngredientTable,
Ingredient as IngredientTable};
use crate::schema::{
Recipe::dsl::Recipe as AllRecipe,
RecipeMatchIngredient::dsl::RecipeMatchIngredient as AllRecipeMatchIngredient,
AllergyMatchIngredient::dsl::AllergyMatchIngredient as AllAllergyMatchIngredient,
DietMatchIngredient::dsl::DietMatchIngredient as AllDietMatchIngredient,
Ingredient::dsl::Ingredient as AllIngredient
};
#[derive(Queryable)]
pub struct Recipe {
pub id: u64,
pub associated_author_id: u64,
pub meal_type: String,
pub cuisine: String,
pub estimated_ttc: u64,
pub difficulty: u64,
pub count_upvotes: u64,
pub count_reports: u64,
}
impl Recipe {
}
#[derive(Insertable)]
#[table_name="Recipe"]
pub struct NewRecipe {
pub associated_author_id: u64,
pub meal_type: String,
pub cuisine: String,
pub estimated_ttc: u64,
pub difficulty: u64,
pub count_upvotes: u64,
pub count_reports: u64,
}
//==================================================================================================
#[derive(Queryable)]
pub struct Ingredient {
pub id: u64,
pub name: String,
pub cost_eur_cents: u64,
pub calories: u64,
}
#[derive(Insertable)]
#[table_name="Ingredient"]
pub struct NewIngredient {
pub name: String,
pub cost_eur_cents: u64,
pub calories: u64,
}
//==================================================================================================
#[derive(Queryable)]
pub struct RecipeMatchIngredient {
pub link_id: u64, // diesel enforces to have primary key
pub recipe_id: u64,
pub ingredient_id: u64
}
#[derive(Insertable)]
#[table_name="RecipeMatchIngredient"]
pub struct NewRecipeMatchIngredient {
pub recipe_id: u64,
pub ingredient_id: u64
}
//==================================================================================================
#[derive(Queryable)]
pub struct AllergyMatchIngredient {
pub link_id: u64, // diesel enforces to have primary key
pub allergy: String,
pub ingredient_id: u64
}
#[derive(Insertable)]
#[table_name="AllergyMatchIngredient"]
pub struct NewAllergyMatchIngredient {
pub allergy: String,
pub ingredient_id: u64
}
//==================================================================================================
#[derive(Queryable)]
pub struct DietMatchIngredient {
pub link_id: u64, // diesel enforces to have primary key
pub diet: String,
pub ingredient_id: u64,
}
#[derive(Insertable)]
#[table_name="DietMatchIngredient"]
pub struct NewDietMatchIngredient {
pub diet: String,
pub ingredient_id: u64,
}
and my up.sql is like this:
-- Your SQL goes here
CREATE TABLE Recipe (
id INTEGER PRIMARY KEY NOT NULL,
associated_user_id INTEGER NOT NULL,
meal_type VARCHAR NOT NULL,
cuisine VARCHAR NOT NULL,
estimated_ttc INTEGER NOT NULL,
difficulty INTEGER NOT NULL,
count_upvotes INTEGER NOT NULL,
count_reports INTEGER NOT NULL
);
CREATE TABLE Ingredient (
id INTEGER PRIMARY KEY NOT NULL,
name VARCHAR NOT NULL,
cost_eur_cents INTEGER NOT NULL,
calories INTEGER NOT NULL
);
CREATE TABLE RecipeMatchIngredient (
link_id INTEGER PRIMARY KEY NOT NULL,
recipe_id INTEGER NOT NULL,
ingredient_id INTEGER NOT NULL,
FOREIGN KEY(ingredient_id) REFERENCES Ingredient(id),
FOREIGN KEY(recipe_id) REFERENCES Recipe(id)
);
CREATE TABLE AllergyMatchIngredient (
link_id INTEGER PRIMARY KEY NOT NULL,
allergy VARCHAR NOT NULL,
ingredient_id INTEGER NOT NULL,
FOREIGN KEY(ingredient_id) REFERENCES Ingredient(id)
);
CREATE TABLE DietMatchIngredient (
link_id INTEGER PRIMARY KEY NOT NULL,
diet VARCHAR NOT NULL,
ingredient_id INTEGER NOT NULL,
FOREIGN KEY(ingredient_id) REFERENCES Ingredient(id)
);
However it produces following error:
error[E0223]: ambiguous associated type
--> src\models.rs:38:10
|
38 | #[derive(Insertable)]
| ^^^^^^^^^^ help: use fully-qualified syntax: `<models::Recipe as Trait>::table`
|
= note: this error originates in the derive macro `Insertable` (in Nightly builds, run with -Z macro-backtrace for more info)
I thought that perhaps the error implies something shadows the items in #[table_name=] macro, but quick test with a new database schema didn't give any results.
The correct variant would be the use the table module comming from your schema.rs file. According to your imports that would result in the following adjusted struct definition:
#[derive(Insertable)]
#[table_name="RecipeTable"]
pub struct NewRecipe {
pub associated_author_id: u64,
pub meal_type: String,
pub cuisine: String,
pub estimated_ttc: u64,
pub difficulty: u64,
pub count_upvotes: u64,
pub count_reports: u64,
}
Checkout the "All about inserts" guide for more details about how inserts are work using diesel.

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
}

Rust function return type with lifetime value

I'd like to implement the Responder trait for my Hero structure, but the following method signature (respond_to):
use rocket::{http::Status, response::Responder, Request, Response};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Hero {
pub id: Option<i32>,
pub name: String,
pub identity: String,
pub hometown: String,
pub age: i32,
}
impl Hero {
pub fn new(
num: Option<i32>,
name: String,
identity: String,
hometown: String,
age: i32,
) -> Hero {
Hero {
id: num,
name,
identity,
hometown,
age,
}
}
}
impl<'r> Responder<'r> for Hero {
fn respond_to(self, _request: &Request) -> Result<Response, Status> {
unimplemented!()
}
}
Throws a compilation error:
error[E0106]: missing lifetime specifier
--> src/main.rs:32:55
|
32 | fn respond_to(self, _request: &Request) -> Result<Response, Status> {
| ^^^^^^^^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say which one of `_request`'s 2 lifetimes it is borrowed from
Dependencies:
[dependencies]
rocket = "0.4.2"
serde = {version = "1.0.99", features=["derive"]}
The documentation doesn't provide an example of how to provide lifetimes when a type is returned. How to specify lifetime when a type is returned?
The Responder trait is defined as:
pub trait Responder<'r> {
fn respond_to(self, request: &Request) -> response::Result<'r>;
}
The response::Result<'r> is defined as:
pub type Result<'r> = ::std::result::Result<self::Response<'r>, ::http::Status>;
Your method signature is:
fn respond_to(self, request: &Request) -> Result<Response, Status>;
As you can see, you just forgot to specify lifetime for the Response. The correct method signature is:
impl<'r> Responder<'r> for Hero {
fn respond_to(self, request: &Request) -> Result<Response<'r>, Status> {
unimplemented!()
}
}

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