Related
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?
I am trying to make an Actix API with Diesel, and in making the first endpoint (/books/create), I am having trouble trying to get the inserted value back into my code.
This is my insert:
use diesel::prelude::*;
use crate::models::Book;
use crate::schema::books;
use crate::db;
pub fn create_book(book: &Book) -> Book {
let mut connection: SqliteConnection = db::establish_connection();
let result: QueryResult<Book> = diesel::insert_into(books::table)
.values([book])
.get_result::<Book>(&mut connection);
result.unwrap()
}
This is the error, which I wrapped in a pastebin since it's too long for Stackoverflow: https://pastebin.com/Evq6tUYq
I wonder if it can be a problem of the schema or model, but they seem to be okay'ish and don't have mismatching values, as far as I know:
use diesel::prelude::*;
use serde::{Serialize, Deserialize};
use crate::schema::books;
#[derive(Queryable, Serialize, Deserialize, Insertable)]
#[diesel(table_name = books)]
pub struct Book {
pub id: i32,
pub title: String,
pub author: String,
pub creation_date: Option<String>,
pub publishing_house: Option<String>,
pub release_date: Option<String>,
pub cover_image: Option<String>
}
diesel::table! {
books (id) {
id -> Integer,
title -> Text,
author -> Text,
publishing_house -> Nullable<Text>,
release_date -> Nullable<Text>,
cover_image -> Nullable<Text>,
creation_date -> Nullable<Text>,
}
}
Where is the error? What's missing?
It's impossible to answer this question as it is missing important details like the used diesel version, and the enabled feature flags for diesel.
The underlying problem is that .get_result() uses INSERT INTO … RETURNING SQL statements internally. These are only supported by Sqlite 3.35 or newer. As of this support for such statements is behind a separate diesel feature flag. You need to enable the returning_clauses_for_sqlite_3_35 feature flag.
That by itself will not fix the issue. .values([book]) creates a array with one element and tries to insert that. This will cause diesel to use a batch insert statement, which normally allows to insert more than one element at once. While this is supported for the sqlite backend it cannot be used in combination with the returning support described above, as SQLite is missing another SQL feature here (using the DEFAULT keyword to signal that a certain value should be replaced by the corresponding database default value). In your case this can be fixed by just calling .values(&book), which constructs an ordinary single row insert.
You cant use Queryable and Insertable trait in the same Book struct you must separate them into 2 different structs.
#[derive(Queryable, Serialize, Deserialize)]
pub struct Book {
pub id: i32,
pub title: String,
pub author: String,
pub creation_date: Option<String>,
pub publishing_house: Option<String>,
pub release_date: Option<String>,
pub cover_image: Option<String>
}
#[derive(Insertable)]
#[diesel(table_name = books)]
pub struct BookInsert {
pub id: i32,
pub title: String,
pub author: String,
pub creation_date: Option<String>,
pub publishing_house: Option<String>,
pub release_date: Option<String>,
pub cover_image: Option<String>
}
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.
If I have a struct I need Default implemented on, if all the field's types have Default implemented themsevles, then I can use the derive macro, otherwise I need to implement Default manually. However, there are some situations where I have a large struct where almost all the fields would be derivable, except there is a small number of fields with types which do not have Default implemented, and I can impl myself because the type is external. This ends up with the situation in the below example. I would prefer to avoid this as it means I need to keep the fields in sync between two places, which is more time consuming, error prone, and verbose. I'm hoping there might be some syntactical suagr to avoid this, but I'm new to rust and can't think of any. I'm thinking something like:
impl Default for Data2 {
fn default() -> Self {
Self {
external_data: ExternalType::One,
..Default::default(),
}
}
}
Example
#[derive(Default)]
struct Data {
name: Option<String>,
flag: bool,
selected: Vec<String>,
show_reference: Option<String>,
index: Option<usize>,
create_name: String,
create_type: String,
}
enum ExternalType {
One,
Two,
}
// #[derive(Default)]
struct Data2 {
name: Option<String>,
flag: bool,
selected: Vec<String>,
show_reference: Option<String>,
index: Option<usize>,
create_name: String,
create_type: String,
external_data: ExternalType,
}
impl Default for Data2 {
fn default() -> Self {
Self {
name: Default::default(),
flag: Default::default(),
selected: Default::default(),
show_reference: Default::default(),
index: Default::default(),
create_name: Default::default(),
create_type: Default::default(),
external_data: ExternalType::One,
}
}
}
I think the smart-default crate can handle this. It lets you derive SmartDefault, which derives Default for all fields except those with a #[default(my_default_value)] attribute.
Example:
use smart_default::SmartDefault;
#[derive(SmartDefault)]
enum Foo {
Bar,
#[default]
Baz {
#[default = 12]
a: i32,
b: i32,
#[default(Some(Default::default()))]
c: Option<i32>,
#[default(_code = "vec![1, 2, 3]")]
d: Vec<u32>,
#[default = "four"]
e: String,
},
Qux(i32),
}
There's also the more general derivative crate which lets you customize how all derives work, not just Default, but is a tad more verbose (because it's more general).
Example:
use derivative::Derivative;
#[derive(Derivative)]
#[derivative(Default)]
pub struct RegexOptions {
pub pats: Vec<String>,
#[derivative(Default(value="10 * (1 << 20)"))]
pub size_limit: usize,
#[derivative(Default(value="2 * (1 << 20)"))]
pub dfa_size_limit: usize,
pub case_insensitive: bool,
pub multi_line: bool,
pub dot_matches_new_line: bool,
pub swap_greed: bool,
pub ignore_whitespace: bool,
#[derivative(Default(value="true"))]
pub unicode: bool,
}
As with most Orphan rule problems the only option available is the New Type pattern but I'm not sure this is really an improvement over a manual default impl.
struct ExternalWithDefault(ExternalType);
impl Default for ExternalWithDefault {
fn default() -> Self {
Self(ExternalType::One)
}
}
...
#[derive(Default)]
struct Data2 {
name: Option<String>,
flag: bool,
selected: Vec<String>,
show_reference: Option<String>,
index: Option<usize>,
create_name: String,
create_type: String,
external_data: ExternalWithDefault,
}
I'm trying to create a simple program in Rust that will add, retrieve, and delete a Person.
main.rs
mod person;
fn main {
person::add("Mr", "Wang", "Li", "Lou");
}
person.rs
#[derive(Debug)]
pub struct Person {
prefix: String,
first_name: String,
middle_name: String,
last_name: String,
}
pub fn add(prefix: String, first_name: String, middle_name: String, last_name: String) {
let new_person: Person = Person {
prefix,
first_name,
middle_name,
last_name,
};
println!("New person added: {:?}", new_person);
}
pub fn list() {
// will print list of Person
}
Inside the person::add() I wanted to create a collection of persons where I can push the new data new_person which I can also use to retrieve it from the pub fn list().
You generally don't want to declare a global variable in Rust. It's quite different from what you asked but this is generally how you write in Rust.
// main.rs
mod person;
use person::Person;
fn main() {
let mut people = Vec::new();
people.push(Person::new("Mr", "Wang", "Li", "Lou"));
people.push(Person::new("Mr", "Johann", "Sebastian", "Bach"));
people.push(Person::new("Mr", "Winston", "S.", "Churchill"));
println!("{:?}", people);
}
// person.rs
#[derive(Debug)]
pub struct Person {
prefix: String,
first_name: String,
middle_name: String,
last_name: String,
}
impl Person {
pub fn new(prefix: &str, first_name: &str, middle_name: &str, last_name: &str) -> Self {
Person {
prefix: prefix.to_string(),
first_name: first_name.to_string(),
middle_name: middle_name.to_string(),
last_name: last_name.to_string(),
}
}
}
Click to see how these codes actually works in the Rust Playground
Probabily these links will help you further
https://www.rust-lang.org/learn
https://doc.rust-lang.org/book/
https://doc.rust-lang.org/rust-by-example/