The relevant bits of my model:
table! {
server_website {
id -> Integer,
server_database_id -> Nullable<Integer>,
server_id -> Integer,
}
}
table! {
server_database {
id -> Integer,
server_id -> Integer,
}
}
table! {
server {
id -> Integer,
}
}
joinable!(server_website -> server_database (server_database_id));
allow_tables_to_appear_in_same_query!(server_website, server_database);
#[derive(Queryable, Debug, Clone, PartialEq, Eq)]
pub struct Server {
pub id: i32,
}
#[derive(Queryable, Debug, Clone, PartialEq, Eq)]
pub struct ServerWebsite {
pub id: i32,
pub server_database_id: Option<i32>,
pub server_id: i32,
}
#[derive(Queryable, Debug, Clone, PartialEq, Eq)]
pub struct ServerDatabase {
pub id: i32,
pub server_id: i32,
}
I'm trying to express the following working SQLite query with Diesel:
select * from server_website where server_database_id in (
select id from server_database where server_id = 83
);
And here is my diesel query code:
use projectpadsql::schema::server::dsl as srv;
use projectpadsql::schema::server_database::dsl as db;
use projectpadsql::schema::server_website::dsl as srvw;
let database_ids_from_server = db::server_database
.filter(db::server_id.eq(83))
.select(db::id);
let used_websites = srvw::server_website
.filter(srvw::server_database_id.eq_any(database_ids_from_server))
.load::<ServerWebsite>(sql_conn)
.unwrap();
This fails with the error:
error[E0277]: the trait bound `diesel::query_builder::SelectStatement<projectpadsql::schema::server_database::table, diesel::query_builder::select_clause::SelectClause<projectpadsql::schema::server_database::columns::id>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<diesel::expression::operators::Eq<projectpadsql::schema::server_database::columns::server_id, diesel::expression::bound::Bound<diesel::sql_types::Integer, i32>>>>: diesel::expression::array_comparison::AsInExpression<diesel::sql_types::Nullable<diesel::sql_types::Integer>>` is not satisfied
I see that the compiler would like the inner query to have the type AsInExpression<Nullable<Integer>> which looks good to me (besides the nullable).
The inner query has more or less the type SelectStatement<server_database, SelectClause<server_database::id>>, which looks good to me since the id is Integer just like the outer query wants.
I have the feeling I'm quite close, but I can't put my finger on what the issue is.
I needed to use nullable() to get the types matching. Notice the db::id.nullable():
let database_ids_from_server = db::server_database
.filter(db::server_id.eq(server_id))
.select(db::id.nullable());
let used_websites = srvw::server_website
.filter(srvw::server_database_id.eq_any(database_ids_from_server))
.load::<ServerWebsite>(sql_conn)
.unwrap();
Georg Semmler - #weiznich on the Diesel Gitter channel answered this question.
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.
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.
I have struct from which I want to extract the data.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunResult {
pub runs: Vec<RunDetails>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunDetails {
pub details: String,
pub name: String,
pub id: String,
pub type: String,
}
I am getting the data which got inserted in above struct in the variable result which I am iterating below
let my_result:RunResult = result
.runs
.into_iter()
.filter(|line| line.details.contains(&"foo"))
.collect();
I am getting the error as
value of type `RunResult` cannot be built from `std::iter::Iterator<Item=RunDetails>`
I want to get the record where type=OK, details contains foo with highest id.
How can I achieve this?
Check out the documentation for collect here.
Your struct RunResult should implement FromIterator<RunDetails>.
Try adding this (untested by me):
impl FromIterator<RunDetails> for RunResult {
fn from_iter<I: IntoIterator<Item=RunDetails>>(iter: I) -> Self {
Self {
runs: Vec::from_iter(iter);
}
}
}
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'm trying to construct some filters at runtime which can be applied either to a table tunnel or to tunnel LEFT OUTER JOIN connection ON (tunnel.id = connection.tunnel_id).
The tables are defined like this:
// Define the tunnel table and struct
table! {
#[allow(unused_imports)]
use diesel::sql_types::*;
tunnel (id) {
id -> BigInt,
name -> Text,
}
}
#[derive(Queryable, Identifiable, Clone, Debug, PartialEq, Eq)]
#[table_name = "tunnel"]
pub struct Tunnel {
pub id: i64,
pub name: String,
}
// Define the connection table and struct
table! {
#[allow(unused_imports)]
use diesel::sql_types::*;
connection(id) {
id -> BigInt,
tunnel_id -> BigInt,
}
}
#[derive(Debug, Associations, Identifiable, Queryable)]
#[table_name = "connection"]
#[primary_key(id)]
#[belongs_to(Tunnel)]
pub struct Connection {
pub id: i64,
pub tunnel_id: i64,
}
joinable!(connection -> tunnel(tunnel_id));
allow_tables_to_appear_in_same_query!(connection, tunnel);
I can write a function that constructs dynamic for either the single table:
fn filters_t(
name: &'static str,
) -> Vec<Box<dyn BoxableExpression<tunnel::table, Pg, SqlType = Bool>>> {
let mut wheres: Vec<Box<dyn BoxableExpression<tunnel::table, Pg, SqlType = Bool>>> = Vec::new();
wheres.push(Box::new(tunnel::name.eq(name)));
wheres
}
Or for the join:
pub type TunnelJoinConnection = JoinOn<
Join<tunnel::table, connection::table, LeftOuter>,
Eq<Nullable<connection::columns::tunnel_id>, Nullable<tunnel::columns::id>>,
>;
fn filters_j(
name: &'static str,
) -> Vec<Box<dyn BoxableExpression<TunnelJoinConnection, Pg, SqlType = Bool>>> {
let mut wheres: Vec<Box<dyn BoxableExpression<TunnelJoinConnection, Pg, SqlType = Bool>>> =
Vec::new();
wheres.push(Box::new(tunnel::name.eq(name)));
wheres
}
Note that the two filter functions have the exact same function body, so I should be able to make a generic function that achieves both of them. But I get an error when I try to make it generic.
fn filters<T>(name: &'static str) -> Vec<Box<dyn BoxableExpression<T, Pg, SqlType = Bool>>>
where
T: AppearsInFromClause<tunnel::table, Count = Once>,
{
vec![Box::new(tunnel::name.eq(name))]
}
The error is
|
85 | vec![Box::new(tunnel::name.eq(name))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::SelectableExpression<T>` is not implemented for `tunnel::columns::name`
|
= note: required because of the requirements on the impl of `diesel::SelectableExpression<T>` for `diesel::expression::operators::Eq<tunnel::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>`
= note: required because of the requirements on the impl of `diesel::BoxableExpression<T, diesel::pg::Pg>` for `diesel::expression::operators::Eq<tunnel::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>`
= note: required for the cast to the object type `dyn diesel::BoxableExpression<T, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>`
Minimal example here, you can clone that minimal example and run cargo check yourself to see the error.
This can be be fixed with one small change:
fn filters<T>(name: &'static str) -> Vec<Box<dyn BoxableExpression<T, Pg, SqlType = Bool>>>
where
diesel::dsl::Eq<tunnel::name, &'static str>: BoxableExpression<T, Pg, SqlType = Bool>,
{
vec![Box::new(tunnel::name.eq(name))]
}
Basically you need to assert at compile time that your boxed expression really implements BoxableExpression<T, Pg, SqlType = Bool> for all possible T's. If there is only a specific T that is checked by rustc, for the generic case this needs to be written out explicitly. The diesel::dsl::Eq helper type is a type level constructor for the type returned by tunnel::name.eq(name). This implies you will need a similar clause for each expression you add to the list.
Another unrelated note:
pub type TunnelJoinConnection = JoinOn<
Join<tunnel::table, connection::table, LeftOuter>,
Eq<Nullable<connection::columns::tunnel_id>, Nullable<tunnel::columns::id>>,
>;
uses types that are not considered to be part of the public API of diesel. This means such a expression can break with any update. The correct way to write this type using the public API is
pub type TunnelJoinConnection = diesel::dsl::LeftJoin<tunnel::table, connection::table>;`