Using diesel v2.1.0 to read data from an existing MySQL database.
I'm trying to load a struct in which one of the field is a enum. The enum is made of variants that either are units or single-value tuples.
I'm able to store/retrieve such an enum field in two columns of a table by using the #[diesel(embed)]
field attribute (see https://docs.diesel.rs/2.1.x/diesel_derives/derive.Selectable.html#field-attributes).
However, when I try to also define a belongs_to
relation, the compiler complains that the load_dsl::private::CompatibleType<…>
is not implemented.
What am I missing?
Here is a MWE summarizing what I tried:
use diesel::deserialize;
use diesel::mysql::Mysql;
use diesel::prelude::*;
use diesel::sql_types::{Integer, Nullable};
diesel::table! {
parents {
id -> Integer,
}
}
diesel::table! {
children {
id -> Integer,
parent_id -> Integer,
// (variant, value) to be coerced into a single field
variant -> Integer,
value -> Nullable<Integer>,
}
}
#[derive(Debug, Identifiable, Queryable, Selectable)]
#[diesel(table_name = parents)]
#[diesel(check_for_backend(Mysql))]
pub struct Parent {
id: i32,
}
#[derive(Debug)]
pub enum CustomEnum {
VariantWithValue(i32),
AnotherVariant,
YetAnotherVariant,
}
impl Selectable<Mysql> for CustomEnum {
type SelectExpression = (children::variant, children::value);
fn construct_selection() -> Self::SelectExpression {
(children::variant, children::value)
}
}
impl Queryable<(Integer, Nullable<Integer>), Mysql> for CustomEnum {
type Row = (i32, Option<i32>);
fn build(row: Self::Row) -> deserialize::Result<Self> {
use CustomEnum::*;
let (variant, value) = row;
match (variant, value) {
(1, Some(n)) => Ok(VariantWithValue(n)),
(2, _) => Ok(AnotherVariant),
(3, _) => Ok(YetAnotherVariant),
_ => Err("invalid variant".into()),
}
}
}
#[derive(Debug, Associations, Identifiable, Queryable, Selectable)]
#[diesel(
table_name = children,
belongs_to(Parent, foreign_key = parent_id)
)]
#[diesel(check_for_backend(Mysql))]
pub struct Child {
id: i32,
parent_id: i32,
#[diesel(embed)]
custom: CustomEnum,
}
fn load_children_per_parent(connection: &mut MysqlConnection) -> Vec<(Parent, Vec<Child>)> {
let parents = parents::table.load::<Parent>(connection).unwrap();
let children = Child::belonging_to(&parents)
.load::<Child>(connection)
.unwrap()
.grouped_by(&parents);
parents.into_iter().zip(children).collect()
}
This fails to compile with the following message:
$ cargo build
Compiling diesel-compatible-type v0.1.0 (/tmp/diesel-compatible-type)
error[E0277]: the trait bound `(diesel::sql_types::Integer, diesel::sql_types::Integer, diesel::sql_types::Integer, diesel::sql_types::Nullable<diesel::sql_types::Integer>): load_dsl::private::CompatibleType<Child, Mysql>` is not satisfied
--> src/lib.rs:76:24
|
76 | .load::<Child>(connection)
| ---- ^^^^^^^^^^ the trait `load_dsl::private::CompatibleType<Child, Mysql>` is not implemented for `(diesel::sql_types::Integer, diesel::sql_types::Integer, diesel::sql_types::Integer, diesel::sql_types::Nullable<diesel::sql_types::Integer>)`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `load_dsl::private::CompatibleType<U, DB>`:
(ST0, ST1)
(ST0, ST1, ST2)
(ST0, ST1, ST2, ST3)
(ST0, ST1, ST2, ST3, ST4)
(ST0, ST1, ST2, ST3, ST4, ST5)
(ST0, ST1, ST2, ST3, ST4, ST5, ST6)
(ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7)
(ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7, ST8)
and 24 others
= note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, WhereClause<Grouped<In<parent_id, Many<Integer, &i32>>>>>` to implement `LoadQuery<'_, _, Child>`
= note: the full type name has been written to '/tmp/diesel-compatible-type/target/debug/deps/diesel_compatible_type-04c41dd73697d996.long-type-17130066208825338060.txt'
note: required by a bound in `diesel::RunQueryDsl::load`
--> /<redacted>/.cargo/registry/src/index.crates.io-6f17d22bba15001f/diesel-2.1.0/src/query_dsl/mod.rs:1543:15
|
1543 | Self: LoadQuery<'query, Conn, U>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::load`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `diesel-compatible-type` (lib) due to previous error
This kind of error message always indicate a mismatch between what your query returns and what your type expects the query to return. In this specific case you already followed almost all the suggestions made by the relevant documentation. You've missed an important piece: Adding the select clause generated by Selectable
to your query. If you change your queries to:
let parents = parents::table.select(Parent::as_select()).load(connection).unwrap();
let children = Child::belonging_to(&parents)
.select(Child::as_select())
.load(connection)
.unwrap()
.grouped_by(&parents);
the errors should disappear.