I am trying to create a struct which references the same table twice. The purpose of this is to create a kind of hierarchy of categories. Here is what I am trying to do for following tables:
create table product_category_rollup(
id serial primary key,
upper_category_id integer not null,
lower_category_id integer not null,
foreign key (upper_category_id) references product_category(id),
foreign key (lower_category_id) references product_category(id)
);
create table product_category(
id serial primary key,
name varchar unique not null
);
I am trying to create the matching structs as in :
#[derive(Identifiable, Queryable)]
#[table_name = "product_category"]
pub struct ProductCategory {
id: i32,
name: String,
}
#[derive(Queryable, Identifiable, Associations)]
#[belongs_to(ProductCategory, foreign_key="upper_category_id")]
#[belongs_to(ProductCategory, foreign_key="lower_category_id")]
#[table_name = "product_category_rollup"]
pub struct ProductCategoryRollup {
id: i32,
upper_category_id: i32,
lower_category_id: i32,
}
I am getting an error saying:
error[E0119]: conflicting implementations of trait `diesel::associations::BelongsTo<entities::ProductCategory>` for type `entities::ProductCategoryRollup`:
--> src/entities.rs:29:35
|
29 | #[derive(Queryable, Identifiable, Associations)]
| ^^^^^^^^^^^^
| |
| first implementation here
| conflicting implementation for `entities::ProductCategoryRollup`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
What is the proper way to have multiple foreign keys referencing the same table? Is this some inherent limitation in Diesel which was not worked out yet?
The BelongsTo trait definition is:
pub trait BelongsTo<Parent> {
type ForeignKey: Hash + Eq;
type ForeignKeyColumn: Column;
fn foreign_key(&self) -> Option<&Self::ForeignKey>;
fn foreign_key_column() -> Self::ForeignKeyColumn;
}
As a result of ForeignKey (and ForeignKeyColumn) being associated types, rather than generic parameters, a given Child can only have one implementation of BelongsTo<Parent>.
In general, it seems that BelongsTo is fairly limited; note that it's also limited to a single column.
So I have been researching and looking into Diesel, and as already noted in the answer above this problem arises due to way in which the BelongsTo<Parent> trait is defined.
A way to circumvent this is to do something like following:
// This trait contains the behavior common to all types
// representing the product category
trait ProductCategory{
fn new(id: i32, name: String) -> Self;
}
#[derive(Identifiable)]
#[table_name = "product_category"]
pub struct RawProductCategory {
id: i32,
name: String,
}
#[derive(Identifiable)]
#[table_name = "product_category"]
pub struct UpperProductCategory {
pub id: i32,
pub name: String,
}
#[derive(Identifiable)]
#[table_name = "product_category"]
pub struct LowerProductCategory {
pub id: i32,
pub name: String
}
impl ProductCategory for RawProductCategory {
fn new(id: i32, name: String) -> Self {
RawProductCategory {
id,
name
}
}
}
impl ProductCategory for UpperProductCategory {
fn new(id: i32, name: String) -> Self {
UpperProductCategory {
id,
name
}
}
}
impl ProductCategory for LowerProductCategory {
fn new(id: i32, name: String) -> Self {
LowerProductCategory {
id,
name
}
}
}
impl Queryable<product_category::SqlType, diesel::pg::Pg> for RawProductCategory {
type Row = (i32, String);
fn build(row: Self::Row) -> Self {
ProductCategory::new(row.0, row.1)
}
}
impl Queryable<product_category::SqlType, diesel::pg::Pg> for UpperProductCategory {
type Row = (i32, String);
fn build(row: Self::Row) -> Self {
ProductCategory::new(row.0, row.1)
}
}
impl Queryable<product_category::SqlType, diesel::pg::Pg> for LowerProductCategory {
type Row = (i32, String);
fn build(row: Self::Row) -> Self {
ProductCategory::new(row.0, row.1)
}
}
Now I have noticed that that I have quite large code duplication with respect to implementations of Queryable, but I had no desire to reduce it by introducing yet another struct which contains a single field implementing the ProductCategory trait.
Now here comes the fun part. I have noticed why this arises, and have made opened an issue in diesel Github repository. Should this issue be resolved I will update this answer accordingly to show the nicer way of achieving same thing.
Related
I am trying to create a struct which references the same table twice. The purpose of this is to create a kind of hierarchy of categories. Here is what I am trying to do for following tables:
create table product_category_rollup(
id serial primary key,
upper_category_id integer not null,
lower_category_id integer not null,
foreign key (upper_category_id) references product_category(id),
foreign key (lower_category_id) references product_category(id)
);
create table product_category(
id serial primary key,
name varchar unique not null
);
I am trying to create the matching structs as in :
#[derive(Identifiable, Queryable)]
#[table_name = "product_category"]
pub struct ProductCategory {
id: i32,
name: String,
}
#[derive(Queryable, Identifiable, Associations)]
#[belongs_to(ProductCategory, foreign_key="upper_category_id")]
#[belongs_to(ProductCategory, foreign_key="lower_category_id")]
#[table_name = "product_category_rollup"]
pub struct ProductCategoryRollup {
id: i32,
upper_category_id: i32,
lower_category_id: i32,
}
I am getting an error saying:
error[E0119]: conflicting implementations of trait `diesel::associations::BelongsTo<entities::ProductCategory>` for type `entities::ProductCategoryRollup`:
--> src/entities.rs:29:35
|
29 | #[derive(Queryable, Identifiable, Associations)]
| ^^^^^^^^^^^^
| |
| first implementation here
| conflicting implementation for `entities::ProductCategoryRollup`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
What is the proper way to have multiple foreign keys referencing the same table? Is this some inherent limitation in Diesel which was not worked out yet?
The BelongsTo trait definition is:
pub trait BelongsTo<Parent> {
type ForeignKey: Hash + Eq;
type ForeignKeyColumn: Column;
fn foreign_key(&self) -> Option<&Self::ForeignKey>;
fn foreign_key_column() -> Self::ForeignKeyColumn;
}
As a result of ForeignKey (and ForeignKeyColumn) being associated types, rather than generic parameters, a given Child can only have one implementation of BelongsTo<Parent>.
In general, it seems that BelongsTo is fairly limited; note that it's also limited to a single column.
So I have been researching and looking into Diesel, and as already noted in the answer above this problem arises due to way in which the BelongsTo<Parent> trait is defined.
A way to circumvent this is to do something like following:
// This trait contains the behavior common to all types
// representing the product category
trait ProductCategory{
fn new(id: i32, name: String) -> Self;
}
#[derive(Identifiable)]
#[table_name = "product_category"]
pub struct RawProductCategory {
id: i32,
name: String,
}
#[derive(Identifiable)]
#[table_name = "product_category"]
pub struct UpperProductCategory {
pub id: i32,
pub name: String,
}
#[derive(Identifiable)]
#[table_name = "product_category"]
pub struct LowerProductCategory {
pub id: i32,
pub name: String
}
impl ProductCategory for RawProductCategory {
fn new(id: i32, name: String) -> Self {
RawProductCategory {
id,
name
}
}
}
impl ProductCategory for UpperProductCategory {
fn new(id: i32, name: String) -> Self {
UpperProductCategory {
id,
name
}
}
}
impl ProductCategory for LowerProductCategory {
fn new(id: i32, name: String) -> Self {
LowerProductCategory {
id,
name
}
}
}
impl Queryable<product_category::SqlType, diesel::pg::Pg> for RawProductCategory {
type Row = (i32, String);
fn build(row: Self::Row) -> Self {
ProductCategory::new(row.0, row.1)
}
}
impl Queryable<product_category::SqlType, diesel::pg::Pg> for UpperProductCategory {
type Row = (i32, String);
fn build(row: Self::Row) -> Self {
ProductCategory::new(row.0, row.1)
}
}
impl Queryable<product_category::SqlType, diesel::pg::Pg> for LowerProductCategory {
type Row = (i32, String);
fn build(row: Self::Row) -> Self {
ProductCategory::new(row.0, row.1)
}
}
Now I have noticed that that I have quite large code duplication with respect to implementations of Queryable, but I had no desire to reduce it by introducing yet another struct which contains a single field implementing the ProductCategory trait.
Now here comes the fun part. I have noticed why this arises, and have made opened an issue in diesel Github repository. Should this issue be resolved I will update this answer accordingly to show the nicer way of achieving same thing.
When using a Arc<Mutex<>> wrapper for a struct, is it possible to deref the wrapper into one of the internal structs fields? In this case I want to deref my PersonWrapper struct into a String from the inner PersonStruct "name" field.
use std::sync::{Mutex, Arc};
pub struct PersonWrapper{
inner: Arc<Mutex<Person>>
}
impl PersonWrapper{
pub fn new(name: String, age: i32,)->Self{
PersonWrapper{
inner: Arc::new(Mutex::new(Person::new(name, age)))
}
}
}
pub struct Person{
name: String,
age: i32,
}
impl Person{
pub fn new(name: String, age: i32)->Self{
Person{
name,
age,
}
}
}
impl std::ops::Deref for PersonWrapper{
type Target = String;
fn deref(&self) -> &Self::Target {
&self.inner.into_inner().unwrap().name
// &self.inner.lock().unwrap().node_id.clone()
}
}
fn main(){
let wrapped_person = PersonWrapper::new(String::from("Zondo"), 45);
assert_eq!(*wrapped_person, String::from("Zondo"));
}
gives errors:
error[E0515]: cannot return reference to temporary value
&self.inner.into_inner().unwrap().name
^--------------------------------^^^^^
||
|temporary value created here
returns a reference to data owned by the current function
error[E0507]: cannot move out of an `Arc`
&self.inner.into_inner().unwrap().name
^^^^^^^^^^ move occurs because value has type `Mutex<Person>`, which does not implement the `Copy` trait
Playground
If you need to provide convenient access to a part of data behind a Mutex, combining the locking and "projecting" (narrowing to a field), you can use the Mutex from the parking_lot crate. Among many other optimizations and improvements, it offers MutexGuard. For example:
use std::sync::Arc;
use parking_lot::{Mutex, MutexGuard, MappedMutexGuard};
pub struct PersonWrapper {
inner: Arc<Mutex<Person>>,
}
impl PersonWrapper {
pub fn new(name: String, age: i32) -> Self {
PersonWrapper {
inner: Arc::new(Mutex::new(Person::new(name, age))),
}
}
pub fn name(&self) -> MappedMutexGuard<String> {
MutexGuard::map(self.inner.lock(), |p| &mut p.name)
}
}
Now you can use name() as if it were a locked mutex that only held the name part of the Person:
let wrapped_person = PersonWrapper::new("Zondo".to_owned(), 45);
assert_eq!(&*wrapped_person.name(), "Zondo");
*wrapped_person.name() = "Mondo".to_owned();
assert_eq!(&*wrapped_person.name(), "Mondo");
Playground
I am using the Diesel crate to perform some database work. In some tables, two columns of the table should be treated together as a single key.
This pattern is repeated in many places in the database, so it would be nice to avoid a heap of repeated copy-paste code to handle this. However, I can't convince Diesel to automatically produce a type I can use in queries or inserts.
Consider the table
table! {
records (iid) {
iid -> Integer,
id_0 -> BigInt,
id_1 -> BigInt,
data -> Text,
}
}
and the ideal types
#[derive(Debug, Copy, Clone, FromSqlRow)]
pub struct RecordId {
id_0: i64,
id_1: i64,
}
#[derive(Queryable, Debug)]
pub struct Record {
pub iid: i32,
pub id: RecordId,
pub data: String,
}
This code compiles OK, but when I try to use it I get an error, for example:
pub fn find(connection: &SqliteConnection) -> types::Record {
records
.find(1)
.get_result::<types::Record>(connection)
.unwrap()
}
produces:
error[E0277]: the trait bound `(i32, types::RecordId, std::string::String): diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` is not satisfied
--> src/main.rs:76:21
|
76 | records.find(1).get_result::<types::Record>(connection).unwrap()
| ^^^^^^^^^^ the trait `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` is not implemented for `(i32, types::RecordId, std::string::String)`
|
= help: the following implementations were found:
<(A, B, C) as diesel::Queryable<(SA, SB, SC), __DB>>
<(A, B, C) as diesel::Queryable<diesel::sql_types::Record<(SA, SB, SC)>, diesel::pg::Pg>>
= note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` for `types::Record`
= note: required because of the requirements on the impl of `diesel::query_dsl::LoadQuery<_, types::Record>` for `diesel::query_builder::SelectStatement<types::records::table, diesel::query_builder::select_clause::DefaultSelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<diesel::expression::operators::Eq<types::records::columns::iid, diesel::expression::bound::Bound<diesel::sql_types::Integer, i32>>>>`
If I create a version which does not contain the RecordId but has the sub-pieces directly, then there is no error:
pub struct RecordDirect {
pub iid: i32,
pub id_0: i64,
pub id_1: i64,
pub data: String,
}
// ...
pub fn find_direct(connection: &SqliteConnection) -> types::RecordDirect {
records
.find(1)
.get_result::<types::RecordDirect>(connection)
.unwrap()
}
Similarly, I can manually implement the Queryable trait and that works OK too,
#[derive(Debug)]
pub struct RecordManual {
pub iid: i32,
pub id: RecordId,
pub data: String,
}
impl Queryable<records::SqlType, diesel::sqlite::Sqlite> for RecordManual {
type Row = (i32, i64, i64, String);
fn build(row: Self::Row) -> Self {
RecordManual {
iid: row.0,
id: RecordId {
id_0: row.1,
id_1: row.2,
},
data: row.3,
}
}
}
// ...
pub fn find_manual(connection: &SqliteConnection) -> types::RecordManual {
records
.find(1)
.get_result::<types::RecordManual>(connection)
.unwrap()
}
This case is ugly to maintain and I could not work out how to get it working for insertion — manually implementing Insertable seems a little trickier than Queryable.
To make this easier for anyone looking at it to play with, I've created a repository containing an almost compiling small reproducer containing the code blocks from this post. (Normally I'd put it up on the rust-playground, but that doesn't support diesel). You can find that code at https://github.com/mikeando/diesel_custom_type_demo.
Is there a way to make the #[derive(Queryable)] (and #[derive(Insertable)]) work for these kinds of cases?
A minimal reproducer for the failing initial case is:
#[macro_use]
extern crate diesel;
use diesel::prelude::*;
mod types {
use diesel::deserialize::Queryable;
use diesel::sqlite::SqliteConnection;
table! {
records (iid) {
iid -> Integer,
id_0 -> BigInt,
id_1 -> BigInt,
data -> Text,
}
}
#[derive(Debug, Copy, Clone, FromSqlRow)]
pub struct RecordId {
id_0: i64,
id_1: i64,
}
// Using a RecordId in a Record compiles, but
// produces an error when used in an actual query
#[derive(Queryable, Debug)]
pub struct Record {
pub iid: i32,
pub id: RecordId,
pub data: String,
}
}
use types::records::dsl::*;
pub fn find(connection:&SqliteConnection) -> types::Record {
records.find(1).get_result::<types::Record>(connection).unwrap()
}
Is there a way to make the #[derive(Queryable)] (and #[derive(Insertable)]) work for these kinds of cases?
For #[derive(Insertable)] this is simply possible by adding a #[diesel(embedded)] to your id field and a #[derive(Insertable)] on both structs. See the documentation of Insertable for details.
For #[derive(Queryable)] this is not possible because Queryable is supposed to be a plain mapping from query result to struct, with the basic assumption that the "shape" of the output remains the same (at least for the derive).
I am trying to make an Entity interface for dynamically mapping a database result into a Rust struct:
pub trait Entity {
fn map(&self, Result<QueryResult>) -> Self;
}
pub struct DbQuery<T> {
pub sql: String,
pub params: Vec<Value>,
pub limit: i32,
pub paged: Option<Pagination>,
pub entity: T,
}
pub struct Settings {
pub name: String,
pub value: Option<String>,
}
impl Entity for Settings {
fn map(&self, result: Result<QueryResult>) -> Settings {
// ...
Settings {
name: "hello".to_string(),
value: None,
}
}
}
impl DbMapper {
// ...
pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>> {
println!("query find SQL: {}", query.sql);
let mut stmt = &self.pool.prepare(query.sql).unwrap();
let ret = Vec::new();
for row in stmt.execute(query.params).unwrap() {
ret.push(query.entity.map(row.unwrap()));
}
Some(ret)
}
}
But I get an error:
error: no method named map found for type T in the current scope
ret.push(query.entity.map(row.unwrap())); |
note: the method map exists but the following trait
bounds were not satisfied: T : std::iter::Iterator = help: items
from traits can only be used if the trait is implemented and in scope;
the following traits define an item map, perhaps you need to
implement one of them: = help: candidate #1:
models::holders::database::Entity = help: candidate #2:
std::iter::Iterator
Here is a version of your code that runs on the playground and replicates your issue:
pub struct QueryResult;
pub struct Value;
pub struct Pagination;
pub struct DbMapper;
pub trait Entity {
fn map(&self, Result<QueryResult, ()>) -> Self;
}
pub struct DbQuery<T> {
pub sql: String,
pub params: Vec<Value>,
pub limit: i32,
pub paged: Option<Pagination>,
pub entity: T,
}
pub struct Settings {
pub name: String,
pub value: Option<String>,
}
impl Entity for Settings {
fn map(&self, result: Result<QueryResult, ()>) -> Settings {
// ...
Settings {
name: "hello".to_string(),
value: None,
}
}
}
impl DbMapper {
// ...
pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>> {
println!("query find SQL: {}", query.sql);
// ########## attempt to call map()
let _ = query.entity.map(Ok(QueryResult {}));
let ret = Vec::new();
Some(ret)
}
}
fn main() {}
The problem is that T in the DbQuery<T> argument in the find method has no idea that T is an Entity type. So we need to tell it:
pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>>
where T: Entity
{
// ... code here ...
}
This now compiles and runs.
The compiler now knows that T is an Entity of some description, and it can call the map method on it.
Using derive syntax, can I implement traits like Hash or PartialEq using specific fields, not all of them?
It could look like:
#[derive(Debug, Hash, Eq, PartialEq)]
struct MyStruct {
id: i32,
name: String,
#[derive(hash_skip, eq_skip)]
aux_data1: f64,
#[derive(hash_skip, eq_skip)]
aux_data2: f64,
#[derive(hash_skip, eq_skip)]
aux_data3: String,
}
I want the hash method to only use id, and name and no others.
The serde library allows something like this for serialization.
No, there is no such feature in Rust at this moment. What I would suggest is to use the implementation for tuples available for these traits, like this:
use std::hash::{Hash, Hasher};
#[derive(Debug)]
struct MyStruct {
id: i32,
name: String,
aux_data1: f64,
aux_data2: f64,
aux_data3: String,
}
impl Hash for MyStruct {
fn hash<H>(&self, state: &mut H) where H: Hasher {
(&self.id, &self.name).hash(state);
}
}
impl PartialEq for MyStruct {
fn eq(&self, other: &Self) -> bool {
(&self.id, &self.name) == (&other.id, &other.name)
}
}
Edit: or as #Shepmaster suggested in a comment below, you can create a key function which returns a tuple of all useful fields and use it.
impl MyStruct {
fn key(&self) -> (&i32, &String) {
(&self.id, &self.name)
}
}
impl Hash for MyStruct {
fn hash<H>(&self, state: &mut H) where H: Hasher {
self.key().hash(state);
}
}
impl PartialEq for MyStruct {
fn eq(&self, other: &Self) -> bool {
self.key() == other.key()
}
}
This is possible by using derivative.
Example:
use derivative::Derivative;
#[derive(Derivative)]
#[derivative(Hash, PartialEq)]
struct Foo {
foo: u8,
#[derivative(Hash="ignore")]
#[derivative(PartialEq="ignore")]
bar: u8,
}