I've got a case where I need to inner join from a table block_headers
to itself due to a parent-child relationship which exists in the same table. I have tried a number of different combinations of schema/model/attributes etc. using Diesel's ORM functionality but haven't found a way to make it work/compile yet.
I've reduced things down to the bare minimum for this question.
The table definition in question is:
table! {
// Note: This is not the real PK of the table, but Diesel seems to require one.
// I'm only querying this table so I simply chose a column.
block_headers (block_height) {
block_height -> Integer, // INTEGER NOT NULL
index_block_hash -> Text, // TEXT NOT NULL
parent_block_id -> Text // TEXT NOT NULL
}
}
In this case the parent_block_id
of the child points to the index_block_hash
of the parent. The reason for the inner join to itself is due to the fact that there may be multiple blocks at a given block_height
, but only one is valid (the one which has a child block pointing back to itself). Here's a short example of the results from the query below:
0|55c9861be5cff984a20ce6d99d4aa65941412889bdc665094136429b84f8c2ee|ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1|918697ef63f9d8bdf844c3312b299e72a231cde542f3173f7755bb8c1cdaf3a7|55c9861be5cff984a20ce6d99d4aa65941412889bdc665094136429b84f8c2ee
2|ca4b40509fa64c3676038b2c0f868559d1583c76617dee7b958028b17cc141b1|918697ef63f9d8bdf844c3312b299e72a231cde542f3173f7755bb8c1cdaf3a7
3|48edeb155557b9f301eea81343df18110ce642d093c4d689223492a57b9f3f0d|ca4b40509fa64c3676038b2c0f868559d1583c76617dee7b958028b17cc141b1
4|d2ffe2e8d03755ebe8a8285ac075de76cac4888ee9f8fce4b03c5a651b0eac54|48edeb155557b9f301eea81343df18110ce642d093c4d689223492a57b9f3f0d
5|537b9aaa545ed03635a58d092c5b9d585d78f8cb1e1214a8a7cfd20aa96d4718|d2ffe2e8d03755ebe8a8285ac075de76cac4888ee9f8fce4b03c5a651b0eac54
6|02006dc91b18f1d6eadf909e81e8fce5894591fd56030adc5868f62a6d005083|537b9aaa545ed03635a58d092c5b9d585d78f8cb1e1214a8a7cfd20aa96d4718
The SQL I'm trying to achieve for this example is:
SELECT
bh1.block_height,
bh1.index_block_hash
FROM
block_headers bh1
INNER JOIN
block_headers bh2 ON bh2.parent_block_id = bh1.index_block_hash
ORDER BY bh1.block_height ASC
The model in my latest attempt looks like:
#[derive(Queryable, Selectable, Identifiable, PartialEq, Eq, Debug, Clone, Associations)]
#[diesel(primary_key(block_height))]
#[diesel(belongs_to(BlockHeader, foreign_key = parent_block_id))]
#[diesel(table_name = block_headers)]
pub struct BlockHeader {
pub block_height: i32,
pub index_block_hash: String,
pub parent_block_id: String
}
NOTE: I've tried a number of variations with belongs_to
and the foreign_key
etc. but nothing I've tried yet compiles.
And finally, what I'm trying to do is something like the following, using Diesel:
let result = block_headers::table
.inner_join(block_headers::table.on(block_headers::parent_block_id.eq(block_headers::index_block_hash)))
.filter(block_headers::index_block_hash.eq(&self.block_hash))
.first::<BlockHeader>()
.optional();
Which results in the following wall of text:
type mismatch resolving `<Once as Plus<Once>>::Output == Once`
2 redundant requirements hidden
required for `((block_height, index_block_hash, parent_block_id), (block_height, index_block_hash, parent_block_id))` to implement `diesel::AppearsOnTable<diesel::query_source::joins::Join<schema::block_headers::table, schema::block_headers::table, diesel::query_source::joins::Inner>>`
the full type name has been written to '/home/cylwit/Code/github.com/stacks-network/clarity-wasm/target/debug/deps/clarity_ab_tester-313c89c783028c52.long-type-10333939363849624741.txt'
required for `diesel::query_source::joins::Join<schema::block_headers::table, schema::block_headers::table, diesel::query_source::joins::Inner>` to implement `diesel::QuerySource`
1 redundant requirement hidden
required for `JoinOn<Join<table, table, Inner>, Grouped<Eq<parent_block_id, index_block_hash>>>` to implement `diesel::QuerySource`
the full type name has been written to '/home/cylwit/Code/github.com/stacks-network/clarity-wasm/target/debug/deps/clarity_ab_tester-313c89c783028c52.long-type-9677256780905495163.txt'
required for `diesel::query_builder::select_statement::SelectStatement<diesel::query_builder::from_clause::FromClause<schema::block_headers::table>>` to implement `diesel::query_dsl::InternalJoinDsl<schema::block_headers::table, diesel::query_source::joins::Inner, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<schema::block_headers::columns::parent_block_id, schema::block_headers::columns::index_block_hash>>>`
1 redundant requirement hidden
required for `schema::block_headers::table` to implement `diesel::query_dsl::InternalJoinDsl<schema::block_headers::table, diesel::query_source::joins::Inner, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<schema::block_headers::columns::parent_block_id, schema::block_headers::columns::index_block_hash>>>`
required for `schema::block_headers::table` to implement `diesel::query_dsl::JoinWithImplicitOnClause<diesel::query_source::joins::OnClauseWrapper<schema::block_headers::table, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<schema::block_headers::columns::parent_block_id, schema::block_headers::columns::index_block_hash>>>, diesel::query_source::joins::Inner>`rustcClick for full compiler diagnostic
context.rs(233, 14): required by a bound introduced by this call
schema.rs(5, 9): required for `schema::block_headers::columns::block_height` to implement `diesel::AppearsOnTable<diesel::query_source::joins::Join<schema::block_headers::table, schema::block_headers::table, diesel::query_source::joins::Inner>>`
mod.rs(521, 15): required by a bound in `diesel::QueryDsl::inner_join`
type mismatch resolving `<Once as Plus<Once>>::Output == Once`
2 redundant requirements hidden
required for `((block_height, index_block_hash, parent_block_id), (block_height, index_block_hash, parent_block_id))` to implement `diesel::AppearsOnTable<diesel::query_source::joins::Join<schema::block_headers::table, schema::block_headers::table, diesel::query_source::joins::Inner>>`
the full type name has been written to '/home/cylwit/Code/github.com/stacks-network/clarity-wasm/target/debug/deps/clarity_ab_tester-b6173cfc79c11f16.long-type-15833938815560068956.txt'
required for `diesel::query_source::joins::Join<schema::block_headers::table, schema::block_headers::table, diesel::query_source::joins::Inner>` to implement `diesel::QuerySource`
1 redundant requirement hidden
required for `JoinOn<Join<table, table, Inner>, Grouped<Eq<parent_block_id, index_block_hash>>>` to implement `diesel::QuerySource`
the full type name has been written to '/home/cylwit/Code/github.com/stacks-network/clarity-wasm/target/debug/deps/clarity_ab_tester-b6173cfc79c11f16.long-type-11396126599150083555.txt'
required for `diesel::query_builder::select_statement::SelectStatement<diesel::query_builder::from_clause::FromClause<schema::block_headers::table>>` to implement `diesel::query_dsl::InternalJoinDsl<schema::block_headers::table, diesel::query_source::joins::Inner, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<schema::block_headers::columns::parent_block_id, schema::block_headers::columns::index_block_hash>>>`
1 redundant requirement hidden
required for `schema::block_headers::table` to implement `diesel::query_dsl::InternalJoinDsl<schema::block_headers::table, diesel::query_source::joins::Inner, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<schema::block_headers::columns::parent_block_id, schema::block_headers::columns::index_block_hash>>>`
required for `schema::block_headers::table` to implement `diesel::query_dsl::JoinWithImplicitOnClause<diesel::query_source::joins::OnClauseWrapper<schema::block_headers::table, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<schema::block_headers::columns::parent_block_id, schema::block_headers::columns::index_block_hash>>>, diesel::query_source::joins::Inner>`rustcClick for full compiler diagnostic
context.rs(233, 14): required by a bound introduced by this call
schema.rs(5, 9): required for `schema::block_headers::columns::block_height` to implement `diesel::AppearsOnTable<diesel::query_source::joins::Join<schema::block_headers::table, schema::block_headers::table, diesel::query_source::joins::Inner>>`
mod.rs(521, 15): required by a bound in `diesel::QueryDsl::inner_join`
I'm not really sure how the Diesel query dsl I've tried would handle the bh1
vs. bh2
aliases in the SQL at the top of this question... I've also tried separating things out using separate variables for bh1 and bh2, but end up with pretty much the same result.
Maybe it's not possible to do using Diesel's ORM functionality and I'll need to use raw SQL?
This error message essentially says that one of your tables appears more than once in your from clause. That's an hard error for obvious reasons as it wouldn't be clear anymore to which of the tables a certain column refers. You need to use an alias for at least one of your tables in this case. That can be done via the alias!
macro provided by diesel.
You query would look like:
let (bh1, bh2) = diesel::alias!(block_headers as bh1, block_headers as bh2);
let result = bh1.inner_join(bh2.on(bh1.field(block_headers::parent_block_id).eq(bh2.field(block_headers::index_block_hash))))
.filter(bh1.field(block_headers::index_block_hash).eq(&self.block_hash))
.select(bh1.fields((block_headers::block_height, block_headers::index_block_hash, block_headers::parent_block_id)))
.first::<BlockHeader>()
.optional();
I've also added an explicit select so that what your query returns matches the structure of your BlockHeader
type.