what is the problem with my rust diesel pagination code - rust

I want to do a pagination query when using rust diesel diesel = { version = "1.4.7", features = ["postgres","32-column-tables"] } , this is my pagination code:
use diesel::prelude::*;
use diesel::query_dsl::methods::LoadQuery;
use diesel::query_builder::{QueryFragment, Query, AstPass};
use diesel::pg::Pg;
use diesel::sql_types::BigInt;
use diesel::QueryId;
pub trait PaginateForQueryFragment: Sized {
fn paginate(self, page: i64) -> Paginated<Self>;
}
impl<T> PaginateForQueryFragment for T
where T: QueryFragment<Pg>{
fn paginate(self, page: i64) -> Paginated<Self> {
Paginated {
query: self,
per_page: 10,
page,
is_sub_query: true,
}
}
}
#[derive(Debug, Clone, Copy, QueryId)]
pub struct Paginated<T> {
query: T,
page: i64,
per_page: i64,
is_sub_query: bool
}
impl<T> Paginated<T> {
pub fn per_page(self, per_page: i64) -> Self {
Paginated { per_page, ..self }
}
pub fn load_and_count_pages<U>(self, conn: &PgConnection) -> QueryResult<(Vec<U>, i64)>
where
Self: LoadQuery<PgConnection, (U, i64)>,
{
let per_page = self.per_page;
let results = self.load::<(U, i64)>(conn)?;
let total = results.get(0).map(|x| x.1).unwrap_or(0);
let records = results.into_iter().map(|x| x.0).collect();
let total_pages = (total as f64 / per_page as f64).ceil() as i64;
Ok((records, total_pages))
}
}
impl<T: Query> Query for Paginated<T> {
type SqlType = (T::SqlType, BigInt);
}
impl<T> RunQueryDsl<PgConnection> for Paginated<T> {}
impl<T> QueryFragment<Pg> for Paginated<T>
where
T: QueryFragment<Pg>,
{
fn walk_ast(&self, mut out: AstPass<Pg>) -> QueryResult<()> {
out.push_sql("SELECT *, COUNT(*) OVER () FROM ");
if self.is_sub_query {
out.push_sql("(");
}
self.query.walk_ast(out.reborrow())?;
if self.is_sub_query {
out.push_sql(")");
}
out.push_sql(" t LIMIT ");
out.push_bind_param::<BigInt, _>(&self.per_page)?;
out.push_sql(" OFFSET ");
let offset = (self.page - 1) * self.per_page;
out.push_bind_param::<BigInt, _>(&offset)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, QueryId)]
pub struct QuerySourceToQueryFragment<T> {
query_source: T,
}
impl<FC, T> QueryFragment<Pg> for QuerySourceToQueryFragment<T>
where
FC: QueryFragment<Pg>,
T: QuerySource<FromClause=FC>,
{
fn walk_ast(&self, mut out: AstPass<Pg>) -> QueryResult<()> {
self.query_source.from_clause().walk_ast(out.reborrow())?;
Ok(())
}
}
pub trait PaginateForQuerySource: Sized {
fn paginate(self, page: i64) -> Paginated<QuerySourceToQueryFragment<Self>>;
}
impl<T> PaginateForQuerySource for T
where T: QuerySource {
fn paginate(self, page: i64) -> Paginated<QuerySourceToQueryFragment<Self>> {
Paginated {
query: QuerySourceToQueryFragment {query_source: self},
per_page: 10,
page,
is_sub_query: false,
}
}
}
then I do the pagination query in a unit test like this way:
#[cfg(test)]
mod test {
use std::env;
use diesel::{Connection, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use rust_wheel::common::query::pagination::PaginateForQuerySource;
use crate::model::diesel::rhythm::rhythm_schema::favorites::dsl::favorites;
use crate::model::diesel::rhythm::rhythm_schema::favorites::like_status;
use crate::models::Favorites;
#[test]
fn page_test(){
use crate::model::diesel::rhythm::rhythm_schema::favorites::dsl::*;
use rust_wheel::common::query::pagination::{PaginateForQueryFragment, PaginateForQuerySource};
let conn = establish_music_connection();
let query = favorites
.filter(like_status.eq(1))
.paginate(1)
.per_page(10)
.load::<Favorites>(&conn)
.expect("query fav failed");
println!("{:?}", 1);
}
pub fn establish_music_connection() -> PgConnection {
let database_url = std::env::var("MUSIC_DATABASE_URL").expect("MUSIC_DATABASE_URL must be set");
PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
}
}
shows error like this:
error[E0277]: the trait bound `(i64, std::option::Option<i64>, i64, i64, i64, std::string::String, i32, i32, i64, i32, std::option::Option<i32>, std::option::Option<i32>): Queryable<((BigInt, diesel::sql_types::Nullable<BigInt>, BigInt, BigInt, BigInt, Text, Integer, Integer, BigInt, Integer, diesel::sql_types::Nullable<Integer>, diesel::sql_types::Nullable<Integer>), BigInt), Pg>` is not satisfied
--> src/test/app/music/fav/fav_music.rs:21:14
|
21 | .load::<Favorites>(&conn)
| ^^^^ the trait `Queryable<((BigInt, diesel::sql_types::Nullable<BigInt>, BigInt, BigInt, BigInt, Text, Integer, Integer, BigInt, Integer, diesel::sql_types::Nullable<Integer>, diesel::sql_types::Nullable<Integer>), BigInt), Pg>` is not implemented for `(i64, std::option::Option<i64>, i64, i64, i64, std::string::String, i32, i32, i64, i32, std::option::Option<i32>, std::option::Option<i32>)`
|
= help: the following implementations were found:
<(A, B, C, D, E, F, G, H, I, J, K, L) as Queryable<(SA, SB, SC, SD, SE, SF, SG, SH, SI, SJ, SK, SL), __DB>>
<(A, B, C, D, E, F, G, H, I, J, K, L) as Queryable<diesel::sql_types::Record<(SA, SB, SC, SD, SE, SF, SG, SH, SI, SJ, SK, SL)>, Pg>>
note: required because of the requirements on the impl of `Queryable<((BigInt, diesel::sql_types::Nullable<BigInt>, BigInt, BigInt, BigInt, Text, Integer, Integer, BigInt, Integer, diesel::sql_types::Nullable<Integer>, diesel::sql_types::Nullable<Integer>), BigInt), Pg>` for `Favorites`
--> src/models.rs:14:22
|
14 | #[derive( Serialize, Queryable, Deserialize,Default)]
| ^^^^^^^^^
15 | // #[table_name = "favorites"]
16 | pub struct Favorites {
| ^^^^^^^^^
= note: required because of the requirements on the impl of `LoadQuery<PgConnection, Favorites>` for `Paginated<diesel::query_builder::SelectStatement<rhythm_schema::favorites::table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<diesel::expression::operators::Eq<rhythm_schema::favorites::columns::like_status, diesel::expression::bound::Bound<Integer, i32>>>>>`
note: required by a bound in `load`
--> /Users/dolphin/.cargo/registry/src/github.com-1ecc6299db9ec823/diesel-1.4.8/src/query_dsl/mod.rs:1238:15
|
1238 | Self: LoadQuery<Conn, U>,
| ^^^^^^^^^^^^^^^^^^ required by this bound in `load`
= note: this error originates in the derive macro `Queryable` (in Nightly builds, run with -Z macro-backtrace for more info)
when I remove the pagination lines in the unit test code, it works fine. Could query data from database successfully. So I think something is going wrong with my pagination code. I read the code but could not figure out where is going wrong, what should I do to fix this problem? This is my Favorites define:
#[derive( Serialize, Queryable, Deserialize,Default)]
// #[table_name = "favorites"]
pub struct Favorites {
pub id: i64,
pub song_id: Option<i64>,
pub created_time: i64,
pub updated_time: i64,
pub user_id: i64,
pub source_id: String,
pub like_status: i32,
pub source: i32,
pub playlist_id: i64,
pub play_count: i32,
pub fetched_download_url: Option<i32>,
pub downloaded: Option<i32>
}
and this is the table DDL in the PostgreSQL 13:
-- Drop table
-- DROP TABLE public.favorites;
CREATE TABLE public.favorites (
id int8 NOT NULL GENERATED ALWAYS AS IDENTITY,
song_id int8 NULL,
created_time int8 NOT NULL,
updated_time int8 NOT NULL,
user_id int8 NOT NULL,
source_id varchar NOT NULL,
like_status int4 NOT NULL,
"source" int4 NOT NULL,
playlist_id int8 NOT NULL,
play_count int4 NOT NULL DEFAULT 1,
fetched_download_url int4 NULL DEFAULT 0,
downloaded int4 NULL DEFAULT 0,
CONSTRAINT favorites_id_seq_pk PRIMARY KEY (id),
CONSTRAINT unique_idx UNIQUE (source_id, user_id)
);
CREATE UNIQUE INDEX fav_uniq_idx ON public.favorites USING btree (source_id, user_id);

The pagination code transforms your query from one returning Favorites to one returning (Favorites, i64). It adds a column via SELECT *, COUNT(*) to keep track of total counts.
You should either use the provided .load_and_count_pages() instead of .load() or you can probably get just the original columns by using a .select(). The code could possibly be modified to avoid adding the column if you don't need the counts and only need limit and offset.

Related

Relating two rows of a table via an associative table [duplicate]

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.

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
}

Multiple foreign keys referencing same table in Diesel

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.

subquery with eq_any doesn't compile

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.

How can I derive Queryable for a type with a custom field that maps to more than one column with diesel?

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).

Resources