Skip to content

Instantly share code, notes, and snippets.

@YetAnotherMinion
Last active June 18, 2017 16:35
Show Gist options
  • Save YetAnotherMinion/4d6d9207182848b9ef297c26b269a3f0 to your computer and use it in GitHub Desktop.
Save YetAnotherMinion/4d6d9207182848b9ef297c26b269a3f0 to your computer and use it in GitHub Desktop.
Composite Foreign Key implementation attempt with Diesel
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
table! {
table_a (tennant_id, part_number) {
tennant_id -> Int8,
part_number -> Int8,
}
}
table! {
table_b (id) {
id -> Int8,
foo_tennant_id -> Int8,
foo_part_number -> Int8,
order_quantity -> Int8,
}
}
#[derive(Queryable, Insertable, Identifiable)]
#[table_name = "table_a"]
#[primary_key(tennant_id, part_number)]
pub struct TableA {
pub tennant_id : i64,
pub part_number : i64,
}
impl diesel::JoinTo<table_b::table> for table_a::table {
type JoinOnClause =
diesel::expression::helper_types::Eq<
diesel::expression::nullable::Nullable<(table_b::foo_tennant_id, table_b::foo_part_number)>,
diesel::expression::nullable::Nullable<
<table_a::table as diesel::query_source::Table>::PrimaryKey>
>;
fn join_on_clause() -> Self::JoinOnClause {
use diesel::ExpressionMethods;
// original code:
// table_b::foo_tennant_id.nullable().eq(diesel::Table::primary_key(&table_a::table).nullable())
(table_b::columns::foo_tennant_id, table_b::columns::foo_part_number).nullable().eq(diesel::Table::primary_key(&table_a::table).nullable())
}
}
#[derive(Identifiable)]
#[table_name = "table_b"]
#[primary_key(id)]
pub struct TableB {
pub id : i64,
pub foo_tennant_id : i64,
pub foo_part_number : i64,
pub order_quantity : i64,
// storage for computed foreign key required because foreign_key() returns
// a reference to a field on the struct
pub parent_id : (i64, i64),
}
impl <__DB, __ST> diesel::Queryable<__ST, __DB> for TableB where
__DB: diesel::backend::Backend + diesel::types::HasSqlType<__ST>,
(i64, i64, i64, i64): diesel::types::FromSqlRow<__ST, __DB>
{
type Row = (i64, i64, i64, i64);
fn build((id, foo_tennant_id, foo_part_number, order_quantity):
Self::Row) -> Self {
TableB{id: id,
foo_tennant_id: foo_tennant_id,
foo_part_number: foo_part_number,
order_quantity: order_quantity,
parent_id : (foo_tennant_id, foo_part_number),}
}
}
use diesel::insertable::{ColumnInsertValue};
use diesel::expression::helper_types::AsNullableExpr;
type TableBRow<'insert> =
(ColumnInsertValue<table_b::id, AsNullableExpr<&'insert i64, table_b::id>>,
ColumnInsertValue<table_b::foo_tennant_id, AsNullableExpr<&'insert i64, table_b::foo_tennant_id>>,
ColumnInsertValue<table_b::foo_part_number, AsNullableExpr<&'insert i64, table_b::foo_part_number>>,
ColumnInsertValue<table_b::order_quantity, AsNullableExpr<&'insert i64, table_b::order_quantity>>);
impl <'insert, DB> diesel::insertable::Insertable<table_b::table, DB> for
&'insert TableB where DB: diesel::backend::Backend, TableBRow<'insert> : diesel::insertable::InsertValues<DB>
{
type Values = TableBRow<'insert>;
#[allow(non_shorthand_field_patterns)]
fn values(self) -> Self::Values {
use diesel::expression::{AsExpression, Expression};
use diesel::insertable::ColumnInsertValue;
use diesel::types::IntoNullable;
let TableB {
id: ref id,
foo_tennant_id: ref foo_tennant_id,
foo_part_number: ref foo_part_number,
order_quantity: ref order_quantity,
parent_id : (_, _ ) } = *self;
(ColumnInsertValue::Expression(table_b::id,
AsExpression::<<<table_b::id as
Expression>::SqlType as
IntoNullable>::Nullable>::as_expression(id)),
ColumnInsertValue::Expression(table_b::foo_tennant_id,
AsExpression::<<<table_b::foo_tennant_id
as Expression>::SqlType
as
IntoNullable>::Nullable>::as_expression(foo_tennant_id)),
ColumnInsertValue::Expression(table_b::foo_part_number,
AsExpression::<<<table_b::foo_part_number
as Expression>::SqlType
as
IntoNullable>::Nullable>::as_expression(foo_part_number)),
ColumnInsertValue::Expression(table_b::order_quantity,
AsExpression::<<<table_b::order_quantity
as Expression>::SqlType
as
IntoNullable>::Nullable>::as_expression(order_quantity)))
}
}
pub struct FakeForeignKey((table_b::columns::foo_tennant_id, table_b::columns::foo_part_number));
impl diesel::Column for FakeForeignKey {
type Table = table_b::table;
fn name() -> &'static str { "my_foreign_key" }
}
impl diesel::Expression for FakeForeignKey {
type SqlType = (diesel::types::Int8, diesel::types::Int8);
}
impl diesel::associations::BelongsTo<TableA> for TableB {
type ForeignKey = (i64, i64);
type ForeignKeyColumn = FakeForeignKey; //(table_b::columns::foo_tennant_id, table_b::columns::foo_part_number);
fn foreign_key(&self) -> Option<&Self::ForeignKey> {
Some(&self.parent_id)
}
fn foreign_key_column() -> Self::ForeignKeyColumn {
FakeForeignKey((table_b::columns::foo_tennant_id, table_b::columns::foo_part_number))
}
}
fn main() {
println!("Hello, world!");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment