diff --git a/diesel/src/associations/mod.rs b/diesel/src/associations/mod.rs index dda05f0e7bd6..8144c4bec391 100644 --- a/diesel/src/associations/mod.rs +++ b/diesel/src/associations/mod.rs @@ -1,4 +1,5 @@ use std::hash::Hash; +use query_source::Column; pub trait Identifiable { type Id: Hash + Eq + Copy; @@ -6,17 +7,19 @@ pub trait Identifiable { fn id(&self) -> Self::Id; } -pub trait BelongsTo { +pub trait BelongsTo where FK: Column +{ fn foreign_key(&self) -> Parent::Id; } -pub trait GroupedBy: IntoIterator + Sized { +pub trait GroupedBy: IntoIterator + Sized { fn grouped_by(self, parents: &[Parent]) -> Vec>; } -impl GroupedBy for Vec where - Child: BelongsTo, - Parent: Identifiable, +impl GroupedBy for Vec where + FK: Column, + Child: BelongsTo, + Parent: Identifiable { fn grouped_by(self, parents: &[Parent]) -> Vec> { use std::collections::HashMap; diff --git a/diesel/src/expression/expression_methods/global_expression_methods.rs b/diesel/src/expression/expression_methods/global_expression_methods.rs index 0a25818266f3..0bda73b6ace7 100644 --- a/diesel/src/expression/expression_methods/global_expression_methods.rs +++ b/diesel/src/expression/expression_methods/global_expression_methods.rs @@ -313,15 +313,13 @@ pub trait ExpressionMethods: Expression + Sized { /// # } /// # /// # joinable!(posts -> users (user_id)); - /// # select_column_workaround!(posts -> users (id, user_id, author_name)); - /// # select_column_workaround!(users -> posts (id, name)); /// /// fn main() { /// use self::users::dsl::*; - /// use self::posts::dsl::{posts, author_name}; + /// use self::posts::dsl::{posts, author_name, user_id}; /// let connection = establish_connection(); /// - /// let data = users.inner_join(posts) + /// let data = users.inner_join(posts, user_id) /// .filter(name.nullable().eq(author_name)) /// .select(name) /// .load::(&connection); diff --git a/diesel/src/lib.rs b/diesel/src/lib.rs index de2126c0cbac..0afa21e29fe0 100644 --- a/diesel/src/lib.rs +++ b/diesel/src/lib.rs @@ -87,6 +87,7 @@ pub mod prelude { pub use query_dsl::*; pub use query_source::{QuerySource, Queryable, Table, Column, JoinTo}; pub use result::{QueryResult, TransactionError, TransactionResult, ConnectionError, ConnectionResult, OptionalExtension}; + pub use query_source::joins::{InnerJoinable, LeftJoinable}; } pub use prelude::*; diff --git a/diesel/src/macros/mod.rs b/diesel/src/macros/mod.rs index 958f8cda6f88..e08d7743bf02 100644 --- a/diesel/src/macros/mod.rs +++ b/diesel/src/macros/mod.rs @@ -13,6 +13,12 @@ macro_rules! column { #[derive(Debug, Clone, Copy)] pub struct $column_name; + impl Default for $column_name { + fn default() -> Self { + $column_name + } + } + impl $crate::expression::Expression for $column_name { type SqlType = $Type; } @@ -45,6 +51,26 @@ macro_rules! column { { } + impl $crate::expression::SelectableExpression< + $crate::query_source::joins::InnerJoinSource, + <$column_name as $crate::Expression>::SqlType, + > for $column_name where + Left: $crate::query_source::joins::JoinTo, + Right: $crate::query_source::Table, + FK: $crate::query_source::Column, + { + } + + impl $crate::expression::SelectableExpression< + $crate::query_source::LeftOuterJoinSource, + ST, + > for $column_name where + Left: $crate::query_source::Table + $crate::query_source::joins::JoinTo, + Right: $crate::query_source::Table, + FK: $crate::query_source::Column, + { + } + impl $crate::expression::NonAggregate for $column_name {} impl $crate::query_source::Column for $column_name { @@ -226,6 +252,12 @@ macro_rules! table_body { } } + impl Default for table { + fn default() -> Self { + table + } + } + impl Table for table { type PrimaryKey = columns::$pk; type AllColumns = ($($column_name,)+); @@ -288,17 +320,23 @@ macro_rules! table_body { #[macro_export] #[doc(hidden)] macro_rules! joinable { - ($child:ident -> $parent:ident ($source:ident)) => { - joinable_inner!($child::table => $parent::table : ($child::$source = $parent::table)); - joinable_inner!($parent::table => $child::table : ($child::$source = $parent::table)); + ($child:ident -> $parent:ident ($fk:ident)) => { + joinable_inner!($child::table => $parent::table : ($child::$fk = $parent::table = $child::table)); + joinable_inner!($parent::table => $child::table : ($child::$fk = $parent::table = $child::table)); } } #[macro_export] #[doc(hidden)] macro_rules! joinable_inner { - ($left_table:path => $right_table:path : ($foreign_key:path = $parent_table:path)) => { - impl $crate::JoinTo<$right_table, JoinType> for $left_table { + ($left_table:path => $right_table:path : ($foreign_key:path = $parent_table:path = $child_table:path)) => { + impl $crate::JoinTo<$right_table, $crate::query_source::joins::LeftOuter, $foreign_key> for $left_table { + type JoinSqlType = (<$parent_table as $crate::query_builder::AsQuery>::SqlType, + <<$child_table as $crate::query_builder::AsQuery>::SqlType as $crate::types::IntoNullable>::Nullable); + type JoinAllColumns = (<$parent_table as $crate::query_source::Table>::AllColumns, + <$child_table as $crate::query_source::Table>::AllColumns); + + type ParentTable = $parent_table; type JoinClause = $crate::query_builder::nodes::Join< <$left_table as $crate::QuerySource>::FromClause, <$right_table as $crate::QuerySource>::FromClause, @@ -307,10 +345,10 @@ macro_rules! joinable_inner { $crate::expression::nullable::Nullable< <$parent_table as $crate::query_source::Table>::PrimaryKey>, >, - JoinType, + $crate::query_source::joins::LeftOuter, >; - fn join_clause(&self, join_type: JoinType) -> Self::JoinClause { + fn join_clause(&self, join_type: $crate::query_source::joins::LeftOuter) -> Self::JoinClause { use $crate::QuerySource; $crate::query_builder::nodes::Join::new( @@ -320,74 +358,51 @@ macro_rules! joinable_inner { join_type, ) } - } - } -} -#[macro_export] -#[doc(hidden)] -macro_rules! select_column_workaround { - ($parent:ident -> $child:ident ($($column_name:ident),+)) => { - $(select_column_inner!($parent -> $child $column_name);)+ - select_column_inner!($parent -> $child star); - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! select_column_inner { - ($parent:ident -> $child:ident $column_name:ident) => { - impl $crate::expression::SelectableExpression< - $crate::query_source::InnerJoinSource<$child::table, $parent::table>, - > for $parent::$column_name - { + fn join_all_columns() -> Self::JoinAllColumns { + (<$parent_table as $crate::query_source::Table>::all_columns(), + <$child_table as $crate::query_source::Table>::all_columns()) + } } - impl $crate::expression::SelectableExpression< - $crate::query_source::InnerJoinSource<$parent::table, $child::table>, - > for $parent::$column_name - { - } + impl $crate::JoinTo<$right_table, $crate::query_source::joins::Inner, $foreign_key> for $left_table { + type JoinSqlType = (<$parent_table as $crate::query_builder::AsQuery>::SqlType, + <$child_table as $crate::query_builder::AsQuery>::SqlType); + type JoinAllColumns = (<$parent_table as $crate::query_source::Table>::AllColumns, + <$child_table as $crate::query_source::Table>::AllColumns); + + type ParentTable = $parent_table; + type JoinClause = $crate::query_builder::nodes::Join< + <$left_table as $crate::QuerySource>::FromClause, + <$right_table as $crate::QuerySource>::FromClause, + $crate::expression::helper_types::Eq< + $crate::expression::nullable::Nullable<$foreign_key>, + $crate::expression::nullable::Nullable< + <$parent_table as $crate::query_source::Table>::PrimaryKey>, + >, + $crate::query_source::joins::Inner, + >; - impl $crate::expression::SelectableExpression< - $crate::query_source::LeftOuterJoinSource<$child::table, $parent::table>, - <<$parent::$column_name as $crate::Expression>::SqlType - as $crate::types::IntoNullable>::Nullable, - > for $parent::$column_name - { - } + fn join_clause(&self, join_type: $crate::query_source::joins::Inner) -> Self::JoinClause { + use $crate::QuerySource; - impl $crate::expression::SelectableExpression< - $crate::query_source::LeftOuterJoinSource<$parent::table, $child::table>, - > for $parent::$column_name - { - } - } -} + $crate::query_builder::nodes::Join::new( + self.from_clause(), + $right_table.from_clause(), + $foreign_key.nullable().eq($parent_table.primary_key().nullable()), + join_type, + ) + } -#[macro_export] -#[doc(hidden)] -macro_rules! join_through { - ($parent:ident -> $through:ident -> $child:ident) => { - impl $crate::JoinTo<$child::table, JoinType> for $parent::table { - type JoinClause = < - <$parent::table as $crate::JoinTo<$through::table, JoinType>>::JoinClause - as $crate::query_builder::nodes::CombinedJoin< - <$through::table as $crate::JoinTo<$child::table, JoinType>>::JoinClause, - >>::Output; - - fn join_clause(&self, join_type: JoinType) -> Self::JoinClause { - use $crate::query_builder::nodes::CombinedJoin; - let parent_to_through = $crate::JoinTo::<$through::table, JoinType> - ::join_clause(&$parent::table, join_type); - let through_to_child = $crate::JoinTo::<$child::table, JoinType> - ::join_clause(&$through::table, join_type); - parent_to_through.combine_with(through_to_child) + fn join_all_columns() -> Self::JoinAllColumns { + (<$parent_table as $crate::query_source::Table>::all_columns(), + <$child_table as $crate::query_source::Table>::all_columns()) } } } } + /// Takes a query QueryFragment expression as an argument and returns a string /// of SQL with placeholders for the dynamic values. /// diff --git a/diesel/src/query_builder/select_statement/mod.rs b/diesel/src/query_builder/select_statement/mod.rs index 5f6027d92811..23862c9f9dcb 100644 --- a/diesel/src/query_builder/select_statement/mod.rs +++ b/diesel/src/query_builder/select_statement/mod.rs @@ -8,6 +8,7 @@ use std::marker::PhantomData; use backend::Backend; use expression::*; use query_source::*; +use query_source::joins::{InnerJoinable, LeftJoinable}; use result::QueryResult; use super::distinct_clause::NoDistinctClause; use super::group_by_clause::NoGroupByClause; @@ -65,14 +66,18 @@ impl SelectStatement { } } - pub fn inner_join(self, other: T) - -> SelectStatement, D, W, O, L, Of, G> where + pub fn inner_join(self, other: T, by: FK) + -> SelectStatement, D, W, O, L, Of, G> where T: Table, - F: Table + JoinTo, + F: Table + JoinTo + InnerJoinable, + FK: Column, + F::JoinAllColumns: SelectableExpression, + >::JoinSqlType> + { SelectStatement::new( self.select, - self.from.inner_join(other), + self.from.inner_join(other, by), self.distinct, self.where_clause, self.order, @@ -82,14 +87,18 @@ impl SelectStatement { ) } - pub fn left_outer_join(self, other: T) - -> SelectStatement, D, W, O, L, Of, G> where + pub fn left_outer_join(self, other: T, by: FK) + -> SelectStatement, D, W, O, L, Of, G> where + F: JoinTo + LeftJoinable, T: Table, - F: Table + JoinTo, + FK: Column, + T::SqlType: ::types::IntoNullable, + F::JoinAllColumns: SelectableExpression, + >::JoinSqlType> { SelectStatement::new( self.select, - self.from.left_outer_join(other), + self.from.left_outer_join(other, by), self.distinct, self.where_clause, self.order, diff --git a/diesel/src/query_dsl/belonging_to_dsl.rs b/diesel/src/query_dsl/belonging_to_dsl.rs index 473208a7f1e9..db4e07b22ca7 100644 --- a/diesel/src/query_dsl/belonging_to_dsl.rs +++ b/diesel/src/query_dsl/belonging_to_dsl.rs @@ -1,6 +1,6 @@ use query_builder::AsQuery; -pub trait BelongingToDsl { +pub trait BelongingToDsl { type Output: AsQuery; fn belonging_to(other: &T) -> Self::Output; diff --git a/diesel/src/query_dsl/filter_dsl.rs b/diesel/src/query_dsl/filter_dsl.rs index 71b71e8875ef..8949b80aec91 100644 --- a/diesel/src/query_dsl/filter_dsl.rs +++ b/diesel/src/query_dsl/filter_dsl.rs @@ -54,8 +54,8 @@ impl FilterDsl for T where } impl NotFiltered for T {} -impl NotFiltered for InnerJoinSource {} -impl NotFiltered for LeftOuterJoinSource {} +impl NotFiltered for InnerJoinSource {} +impl NotFiltered for LeftOuterJoinSource {} use expression::AsExpression; use expression::expression_methods::*; diff --git a/diesel/src/query_source/joins.rs b/diesel/src/query_source/joins.rs index 86f6b89fec08..5ac311b4cae6 100644 --- a/diesel/src/query_source/joins.rs +++ b/diesel/src/query_source/joins.rs @@ -1,118 +1,310 @@ -use expression::SelectableExpression; use query_builder::*; use result::QueryResult; -use super::{QuerySource, Table}; -use types::IntoNullable; +use super::{QuerySource, Table, Column}; +use types::{IntoNullable, NotNull}; +use query_builder::nodes::Join; +use expression::nullable::Nullable; +use expression::expression_methods::global_expression_methods::ExpressionMethods; +use expression::{Expression, SelectableExpression}; #[derive(Debug, Clone, Copy)] #[doc(hidden)] -pub struct InnerJoinSource { +pub struct InnerJoinSource { left: Left, right: Right, + fk: ::std::marker::PhantomData, } -impl InnerJoinSource { - pub fn new(left: Left, right: Right) -> Self { +// TODO: make this not conflicting to allow more than one join between two tables!! +// maybe it's possible to solve this with specialization + +// impl JoinTo for InnerJoinSource +// where OtherFK: Column>::ParentTable as Table>::PrimaryKey as Expression>::SqlType>, +// FK: Column>::ParentTable as Table>::PrimaryKey as Expression>::SqlType>, +// Right: Table, +// Other: Table, +// Left: JoinTo + JoinTo, +// <<>::ParentTable as Table>::PrimaryKey as Expression>::SqlType: NotNull, +// <<>::ParentTable as Table>::PrimaryKey as Expression>::SqlType: NotNull, +// { + +// type JoinSqlType = (>::JoinSqlType, +// ::SqlType); +// type JoinAllColumns = (>::JoinAllColumns, +// ::AllColumns); + +// type ParentTable = >::ParentTable; + +// type JoinClause = Join<>::JoinClause, +// ::FromClause, +// ::expression::helper_types::Eq, +// Nullable<<>::ParentTable as Table>::PrimaryKey>>, +// JoinType>; + +// #[doc(hidden)] +// fn join_clause(&self, join_type: JoinType) -> Self::JoinClause { +// let fk = OtherFK::default(); +// let parent_table = Self::ParentTable::default(); +// let other = Other::default(); +// Join::new(>::join_clause(&self.left, Inner), +// other.from_clause(), +// ExpressionMethods::eq(fk.nullable(), parent_table.primary_key().nullable()), +// join_type) +// } + +// fn join_all_columns() -> Self::JoinAllColumns{ +// (>::join_all_columns(), ::all_columns()) +// } +// } + +impl JoinTo for InnerJoinSource where + OtherFK: Column>::ParentTable as Table>::PrimaryKey as Expression>::SqlType>, + FK: Column>::ParentTable as Table>::PrimaryKey as Expression>::SqlType>, + Right: Table + JoinTo, + Other: Table, + Left: JoinTo, + <<>::ParentTable as Table>::PrimaryKey as Expression>::SqlType: NotNull, + <<>::ParentTable as Table>::PrimaryKey as Expression>::SqlType: NotNull, +{ + + type JoinSqlType = (>::JoinSqlType, + ::SqlType); + type JoinAllColumns = (>::JoinAllColumns, + ::AllColumns); + + type ParentTable = >::ParentTable; + + type JoinClause = Join<>::JoinClause, + ::FromClause, + ::expression::helper_types::Eq, + Nullable<<>::ParentTable as Table>::PrimaryKey>>, + JoinType>; + + #[doc(hidden)] + fn join_clause(&self, join_type: JoinType) -> Self::JoinClause { + let fk = OtherFK::default(); + let parent_table = Self::ParentTable::default(); + let other = Other::default(); + Join::new(>::join_clause(&self.left, Inner), + other.from_clause(), + ExpressionMethods::eq(fk.nullable(), parent_table.primary_key().nullable()), + join_type) + } + + fn join_all_columns() -> Self::JoinAllColumns{ + (>::join_all_columns(), + ::all_columns()) + } +} + +impl InnerJoinSource where + Left: JoinTo, + Right: Table, + FK: Column +{ + pub fn new(left: Left, right: Right, _: FK) -> Self { InnerJoinSource { left: left, right: right, + fk: ::std::marker::PhantomData, } } } -impl QuerySource for InnerJoinSource where - Left: Table + JoinTo, + +impl QuerySource for InnerJoinSource where + Left: QuerySource + JoinTo, Right: Table, + FK: Column { - type FromClause = >::JoinClause; + type FromClause = >::JoinClause; fn from_clause(&self) -> Self::FromClause { self.left.join_clause(Inner) } } -impl AsQuery for InnerJoinSource where - Left: Table + JoinTo, +impl AsQuery for InnerJoinSource where + Left: JoinTo, Right: Table, - (Left::AllColumns, Right::AllColumns): SelectableExpression< - InnerJoinSource, - (Left::SqlType, Right::SqlType), - >, + FK: Column, + >::JoinAllColumns: + SelectableExpression, + >::JoinSqlType> { - type SqlType = (Left::SqlType, Right::SqlType); - type Query = SelectStatement< - (Left::SqlType, Right::SqlType), - (Left::AllColumns, Right::AllColumns), - Self, - >; + type SqlType = >::JoinSqlType; + type Query = SelectStatement<>::JoinSqlType, + >::JoinAllColumns, Self>; fn as_query(self) -> Self::Query { - SelectStatement::simple((Left::all_columns(), Right::all_columns()), self) + SelectStatement::simple(Left::join_all_columns(), self) } } -impl_query_id!(InnerJoinSource); + +impl_query_id!(InnerJoinSource); #[derive(Debug, Clone, Copy)] #[doc(hidden)] -pub struct LeftOuterJoinSource { +pub struct LeftOuterJoinSource { left: Left, right: Right, + fk: ::std::marker::PhantomData, } -impl LeftOuterJoinSource { - pub fn new(left: Left, right: Right) -> Self { + +impl LeftOuterJoinSource where + Left: JoinTo, + Right: Table, + Right::SqlType: IntoNullable, + FK: Column, + >::JoinAllColumns: + SelectableExpression, + >::JoinSqlType,>, +{ + pub fn new(left: Left, right: Right, _: FK) -> Self { LeftOuterJoinSource { left: left, right: right, + fk: ::std::marker::PhantomData, } } } -impl QuerySource for LeftOuterJoinSource where - Left: Table + JoinTo, +impl QuerySource for LeftOuterJoinSource where + Left: QuerySource + JoinTo, Right: Table, + FK: Column { - type FromClause = >::JoinClause; + type FromClause = >::JoinClause; fn from_clause(&self) -> Self::FromClause { self.left.join_clause(LeftOuter) } } -impl AsQuery for LeftOuterJoinSource where - Left: Table + JoinTo, +impl AsQuery for LeftOuterJoinSource where + Left: JoinTo, Right: Table, Right::SqlType: IntoNullable, - (Left::AllColumns, Right::AllColumns): SelectableExpression< - LeftOuterJoinSource, - (Left::SqlType, ::Nullable), - >, + FK: Column, + >::JoinAllColumns: + SelectableExpression, + >::JoinSqlType,>, { - type SqlType = (Left::SqlType, ::Nullable); + type SqlType = >::JoinSqlType; type Query = SelectStatement< - Self::SqlType, - (Left::AllColumns, Right::AllColumns), + >::JoinSqlType, + >::JoinAllColumns, Self, >; fn as_query(self) -> Self::Query { - SelectStatement::simple((Left::all_columns(), Right::all_columns()), self) + SelectStatement::simple(Left::join_all_columns(), self) + } +} + +impl JoinTo for LeftOuterJoinSource where + OtherFK: Column>::ParentTable as Table>::PrimaryKey as Expression>::SqlType>, + FK: Column>::ParentTable as Table>::PrimaryKey as Expression>::SqlType>, + Right: Table + JoinTo, + Right::SqlType: IntoNullable, + Other: Table, + Left: JoinTo, + <<>::ParentTable as Table>::PrimaryKey as Expression>::SqlType: NotNull, + <<>::ParentTable as Table>::PrimaryKey as Expression>::SqlType: NotNull, + ::SqlType: NotNull, +{ + + type JoinSqlType = (>::JoinSqlType, + <::SqlType as IntoNullable>::Nullable); + type JoinAllColumns = (>::JoinAllColumns, + ::AllColumns); + + type ParentTable = >::ParentTable; + + type JoinClause = Join<>::JoinClause, + ::FromClause, + ::expression::helper_types::Eq, + Nullable<<>::ParentTable as Table>::PrimaryKey>>, + JoinType>; + + #[doc(hidden)] + fn join_clause(&self, join_type: JoinType) -> Self::JoinClause { + let fk = OtherFK::default(); + let parent_table = Self::ParentTable::default(); + let other = Other::default(); + Join::new(>::join_clause(&self.left, LeftOuter), + other.from_clause(), + ExpressionMethods::eq(fk.nullable(), parent_table.primary_key().nullable()), + join_type) + } + + fn join_all_columns() -> Self::JoinAllColumns{ + (>::join_all_columns(), + ::all_columns()) } } -impl_query_id!(LeftOuterJoinSource); +impl_query_id!(LeftOuterJoinSource); /// Indicates that two tables can be used together in a JOIN clause. /// Implementations of this trait will be generated for you automatically by /// the [association annotations](FIXME: Add link) from codegen. -pub trait JoinTo: Table { +pub trait JoinTo { + type ParentTable: Table; + type JoinSqlType; + type JoinAllColumns; + #[doc(hidden)] type JoinClause; #[doc(hidden)] fn join_clause(&self, join_type: JoinType) -> Self::JoinClause; + + fn join_all_columns() -> Self::JoinAllColumns; } + + + +pub trait InnerJoinable: Sized { + fn inner_join(self, other: T, by: FK) -> InnerJoinSource where + T: Table, + FK: Column, + Self: JoinTo, + Self::JoinAllColumns: SelectableExpression< + InnerJoinSource, + >::JoinSqlType> + { + InnerJoinSource::new(self, other, by) + } +} + +pub trait LeftJoinable: Sized { + fn left_outer_join(self, other: T, by: FK) -> LeftOuterJoinSource where + Self: JoinTo, + T: Table, + FK: Column, + T::SqlType: IntoNullable, + Self::JoinAllColumns: SelectableExpression< + LeftOuterJoinSource, + >::JoinSqlType> + { + LeftOuterJoinSource::new(self, other, by) + } +} + +impl InnerJoinable for T where T: Table {} +impl InnerJoinable for InnerJoinSource {} +impl InnerJoinable for LeftOuterJoinSource {} +impl LeftJoinable for T where T: Table {} +impl LeftJoinable for InnerJoinSource {} +impl LeftJoinable for LeftOuterJoinSource {} + use backend::Backend; #[doc(hidden)] @@ -138,6 +330,7 @@ impl QueryFragment for Inner { #[derive(Clone, Copy, Debug)] pub struct LeftOuter; + impl QueryFragment for LeftOuter { fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { out.push_sql(" LEFT OUTER"); diff --git a/diesel/src/query_source/mod.rs b/diesel/src/query_source/mod.rs index a7fdb76e1ee8..7b09b64ece7c 100644 --- a/diesel/src/query_source/mod.rs +++ b/diesel/src/query_source/mod.rs @@ -35,7 +35,7 @@ pub trait QuerySource { /// A column on a database table. Types which implement this trait should have /// been generated by the [`table!` macro](../macro.table!.html). -pub trait Column: Expression { +pub trait Column: Expression + Default { type Table: Table; fn name() -> &'static str; @@ -43,27 +43,13 @@ pub trait Column: Expression { /// A SQL database table. Types which implement this trait should have been /// generated by the [`table!` macro](../macro.table!.html). -pub trait Table: QuerySource + AsQuery + Sized { +pub trait Table: QuerySource + AsQuery + Sized + Default { type PrimaryKey: Column + Expression + NonAggregate; type AllColumns: SelectableExpression + NonAggregate; fn name() -> &'static str; fn primary_key(&self) -> Self::PrimaryKey; fn all_columns() -> Self::AllColumns; - - fn inner_join(self, other: T) -> InnerJoinSource where - T: Table, - Self: JoinTo, - { - InnerJoinSource::new(self, other) - } - - fn left_outer_join(self, other: T) -> LeftOuterJoinSource where - T: Table, - Self: JoinTo, - { - LeftOuterJoinSource::new(self, other) - } } impl UpdateTarget for T { diff --git a/diesel_codegen/Cargo.toml b/diesel_codegen/Cargo.toml index f33b7ca7e6b9..b93041835970 100644 --- a/diesel_codegen/Cargo.toml +++ b/diesel_codegen/Cargo.toml @@ -17,7 +17,8 @@ syntex_syntax = { version = "0.31.0", optional = true } [dependencies] syntex = { version = "0.31.0", optional = true } syntex_syntax = { version = "0.31.0", optional = true } -diesel = { git = "https://github.com/diesel-rs/diesel.git", default-features = false } +diesel = { path = "../diesel/", default-features = false } +Inflector = "0.3" [dev-dependencies] tempdir = "0.3.4" diff --git a/diesel_codegen/src/associations/belongs_to.rs b/diesel_codegen/src/associations/belongs_to.rs index 7eb57fa717e4..df6b49ea17d6 100644 --- a/diesel_codegen/src/associations/belongs_to.rs +++ b/diesel_codegen/src/associations/belongs_to.rs @@ -7,6 +7,7 @@ use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::parse::token::str_to_ident; use syntax::ptr::P; +use inflector::Inflector; use model::Model; use super::{parse_association_options, AssociationOptions, to_foreign_key}; @@ -33,9 +34,6 @@ pub fn expand_belongs_to( if let Some(item) = belongs_to_impl(&builder) { push(Annotatable::Item(item)); } - for item in selectable_column_hack(&builder) { - push(Annotatable::Item(item)); - } } } @@ -49,8 +47,7 @@ struct BelongsToAssociationBuilder<'a, 'b: 'a> { impl<'a, 'b> BelongsToAssociationBuilder<'a, 'b> { fn parent_struct_name(&self) -> ast::Ident { let association_name = self.options.name.name.as_str(); - let struct_name = capitalize_from_association_name(association_name.to_string()); - str_to_ident(&struct_name) + str_to_ident(&association_name.to_class_case()) } fn child_struct_name(&self) -> ast::Ident { @@ -66,7 +63,7 @@ impl<'a, 'b> BelongsToAssociationBuilder<'a, 'b> { } fn parent_table_name(&self) -> ast::Ident { - let pluralized = format!("{}s", &self.options.name.name.as_str()); + let pluralized = self.options.name.name.as_str().to_plural(); str_to_ident(&pluralized) } @@ -75,7 +72,7 @@ impl<'a, 'b> BelongsToAssociationBuilder<'a, 'b> { } fn foreign_key_name(&self) -> ast::Ident { - to_foreign_key(&self.options.name.name.as_str()) + self.options.fk.unwrap_or(to_foreign_key(&self.options.name.name.as_str())) } fn foreign_key(&self) -> ast::Path { @@ -98,22 +95,6 @@ impl<'a, 'b> BelongsToAssociationBuilder<'a, 'b> { ty_param_of_option(&ty).map(|t| t.clone()) .unwrap_or(ty) } - - fn column_path(&self, column_name: ast::Ident) -> ast::Path { - self.cx.path(self.span, vec![self.child_table_name(), column_name]) - } -} - -fn capitalize_from_association_name(name: String) -> String { - let mut result = String::with_capacity(name.len()); - let words = name.split("_"); - - for word in words { - result.push_str(&word[..1].to_uppercase()); - result.push_str(&word[1..]); - } - - result } fn belonging_to_dsl_impl( @@ -128,7 +109,7 @@ fn belonging_to_dsl_impl( let primary_key_name = builder.primary_key_name(); let item = quote_item!(builder.cx, - impl ::diesel::BelongingToDsl<$parent_struct_name> for $child_struct_name { + impl ::diesel::BelongingToDsl<$parent_struct_name, $foreign_key> for $child_struct_name { type Output = ::diesel::helper_types::FindBy< $child_table, $foreign_key, @@ -143,7 +124,7 @@ fn belonging_to_dsl_impl( push(Annotatable::Item(item)); let item = quote_item!(builder.cx, - impl ::diesel::BelongingToDsl> for $child_struct_name { + impl ::diesel::BelongingToDsl, $foreign_key> for $child_struct_name { type Output = ::diesel::helper_types::Filter< $child_table, ::diesel::expression::helper_types::EqAny< @@ -161,7 +142,7 @@ fn belonging_to_dsl_impl( push(Annotatable::Item(item)); let item = quote_item!(builder.cx, - impl ::diesel::BelongingToDsl<[$parent_struct_name]> for $child_struct_name { + impl ::diesel::BelongingToDsl<[$parent_struct_name], $foreign_key> for $child_struct_name { type Output = ::diesel::helper_types::Filter< $child_table, ::diesel::expression::helper_types::EqAny< @@ -184,12 +165,13 @@ fn belongs_to_impl(builder: &BelongsToAssociationBuilder) -> Option let child_struct_name = builder.child_struct_name(); let primary_key_type = builder.primary_key_type(); let foreign_key_name = builder.foreign_key_name(); + let foreign_key = builder.foreign_key(); if is_option_ty(&builder.foreign_key_type()) { None } else { Some(quote_item!(builder.cx, - impl ::diesel::associations::BelongsTo<$parent_struct_name> for $child_struct_name { + impl ::diesel::associations::BelongsTo<$parent_struct_name, $foreign_key> for $child_struct_name { fn foreign_key(&self) -> $primary_key_type { self.$foreign_key_name } @@ -204,43 +186,7 @@ fn join_to_impl(builder: &BelongsToAssociationBuilder) -> P { let foreign_key = builder.foreign_key(); quote_item!(builder.cx, - joinable_inner!($child_table => $parent_table : ($foreign_key = $parent_table)); + joinable_inner!($child_table => $parent_table : ($foreign_key = $parent_table = $child_table)); ).unwrap() } -fn selectable_column_hack(builder: &BelongsToAssociationBuilder) -> Vec> { - let mut result = builder.model.attrs.iter().flat_map(|attr| { - selectable_column_impl(builder, attr.column_name) - }).collect::>(); - result.append(&mut selectable_column_impl(builder, str_to_ident("star"))); - result -} - -fn selectable_column_impl( - builder: &BelongsToAssociationBuilder, - column_name: ast::Ident, -) -> Vec> { - let parent_table = builder.parent_table(); - let child_table = builder.child_table(); - let column = builder.column_path(column_name); - - [quote_item!(builder.cx, - impl ::diesel::expression::SelectableExpression< - ::diesel::query_source::InnerJoinSource<$parent_table, $child_table> - > for $column {} - ).unwrap(), quote_item!(builder.cx, - impl ::diesel::expression::SelectableExpression< - ::diesel::query_source::InnerJoinSource<$child_table, $parent_table> - > for $column {} - ).unwrap(), quote_item!(builder.cx, - impl ::diesel::expression::SelectableExpression< - ::diesel::query_source::LeftOuterJoinSource<$child_table, $parent_table>, - > for $column {} - ).unwrap(), quote_item!(builder.cx, - impl ::diesel::expression::SelectableExpression< - ::diesel::query_source::LeftOuterJoinSource<$parent_table, $child_table>, - <<$column as ::diesel::Expression>::SqlType - as ::diesel::types::IntoNullable>::Nullable, - > for $column {} - ).unwrap()].to_vec() -} diff --git a/diesel_codegen/src/associations/has_many.rs b/diesel_codegen/src/associations/has_many.rs index 3a0e5778e0fe..07dbed2aefab 100644 --- a/diesel_codegen/src/associations/has_many.rs +++ b/diesel_codegen/src/associations/has_many.rs @@ -27,9 +27,6 @@ pub fn expand_has_many( span: span, }; push(Annotatable::Item(join_to_impl(&builder))); - for item in selectable_column_hack(&builder).into_iter() { - push(Annotatable::Item(item)); - } } } @@ -58,16 +55,12 @@ impl<'a, 'b> HasManyAssociationBuilder<'a, 'b> { } fn foreign_key_name(&self) -> ast::Ident { - to_foreign_key(&self.model.name.name.as_str()) + self.options.fk.unwrap_or(to_foreign_key(&self.model.name.name.as_str())) } fn foreign_key(&self) -> ast::Path { self.cx.path(self.span, vec![self.association_name(), self.foreign_key_name()]) } - - fn column_path(&self, column_name: ast::Ident) -> ast::Path { - self.cx.path(self.span, vec![self.table_name(), column_name]) - } } fn join_to_impl(builder: &HasManyAssociationBuilder) -> P { @@ -76,43 +69,6 @@ fn join_to_impl(builder: &HasManyAssociationBuilder) -> P { let foreign_key = builder.foreign_key(); quote_item!(builder.cx, - joinable_inner!($table => $foreign_table : ($foreign_key = $table)); + joinable_inner!($table => $foreign_table : ($foreign_key = $table = $foreign_table)); ).unwrap() } - -fn selectable_column_hack(builder: &HasManyAssociationBuilder) -> Vec> { - let mut result = builder.model.attrs.iter().flat_map(|attr| { - selectable_column_impl(builder, attr.column_name) - }).collect::>(); - result.append(&mut selectable_column_impl(builder, str_to_ident("star"))); - result -} - -fn selectable_column_impl( - builder: &HasManyAssociationBuilder, - column_name: ast::Ident, -) -> Vec> { - let table = builder.table(); - let foreign_table = builder.foreign_table(); - let column = builder.column_path(column_name); - - [quote_item!(builder.cx, - impl ::diesel::expression::SelectableExpression< - ::diesel::query_source::InnerJoinSource<$table, $foreign_table> - > for $column {} - ).unwrap(), quote_item!(builder.cx, - impl ::diesel::expression::SelectableExpression< - ::diesel::query_source::InnerJoinSource<$foreign_table, $table> - > for $column {} - ).unwrap(), quote_item!(builder.cx, - impl ::diesel::expression::SelectableExpression< - ::diesel::query_source::LeftOuterJoinSource<$table, $foreign_table>, - > for $column {} - ).unwrap(), quote_item!(builder.cx, - impl ::diesel::expression::SelectableExpression< - ::diesel::query_source::LeftOuterJoinSource<$foreign_table, $table>, - <<$column as ::diesel::Expression>::SqlType - as ::diesel::types::IntoNullable>::Nullable, - > for $column {} - ).unwrap()].to_vec() -} diff --git a/diesel_codegen/src/associations/mod.rs b/diesel_codegen/src/associations/mod.rs index bf4fbb79a102..1f64f993943b 100644 --- a/diesel_codegen/src/associations/mod.rs +++ b/diesel_codegen/src/associations/mod.rs @@ -2,6 +2,7 @@ use syntax::ast::{self, MetaItem, MetaItemKind}; use syntax::codemap::Span; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::parse::token::str_to_ident; +use inflector::Inflector; use model::{infer_association_name, Model}; @@ -33,8 +34,10 @@ fn parse_association_options( }) } +#[derive(Debug)] struct AssociationOptions { name: ast::Ident, + fk: Option, } fn build_association_options( @@ -55,9 +58,19 @@ fn build_association_options( MetaItemKind::Word(ref name) => str_to_ident(&name), _ => return usage_err(), }; + let fk = if options.len() >1 { + match options[1].node { + MetaItemKind::Word(ref name) => Some(str_to_ident(&name)), + _ => None, + } + } else{ + None + }; + Some(AssociationOptions { name: association_name, + fk: fk, }) } _ => usage_err(), @@ -66,7 +79,7 @@ fn build_association_options( fn to_foreign_key(model_name: &str) -> ast::Ident { let lower_cased = infer_association_name(model_name); - str_to_ident(&format!("{}_id", &lower_cased)) + str_to_ident(&lower_cased.to_foreign_key()) } #[test] diff --git a/diesel_codegen/src/lib.rs b/diesel_codegen/src/lib.rs index b05c3f79d912..5b9b60fdbe65 100644 --- a/diesel_codegen/src/lib.rs +++ b/diesel_codegen/src/lib.rs @@ -2,6 +2,7 @@ #![deny(warnings)] #[macro_use] extern crate diesel; +extern crate inflector; #[cfg(feature = "with-syntex")] extern crate syntex; diff --git a/diesel_codegen/src/model.rs b/diesel_codegen/src/model.rs index d81c66919a2b..64de02dcb97c 100644 --- a/diesel_codegen/src/model.rs +++ b/diesel_codegen/src/model.rs @@ -3,10 +3,12 @@ use syntax::codemap::Span; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ptr::P; use syntax::parse::token::str_to_ident; +use inflector::Inflector; use attr::Attr; use util::{str_value_of_attr_with_name, struct_ty}; +#[derive(Debug)] pub struct Model { pub ty: P, pub attrs: Vec, @@ -61,25 +63,11 @@ impl Model { } pub fn infer_association_name(name: &str) -> String { - let mut result = String::with_capacity(name.len()); - result.push_str(&name[..1].to_lowercase()); - for character in name[1..].chars() { - if character.is_uppercase() { - result.push('_'); - for lowercase in character.to_lowercase() { - result.push(lowercase); - } - } else { - result.push(character); - } - } - result + name.to_snake_case() } fn infer_table_name(name: &str) -> String { - let mut result = infer_association_name(name); - result.push('s'); - result + infer_association_name(name).to_plural() } #[test] @@ -93,3 +81,8 @@ fn infer_table_name_properly_handles_underscores() { assert_eq!("foo_bars", &infer_table_name("FooBar")); assert_eq!("foo_bar_bazs", &infer_table_name("FooBarBaz")); } + +#[test] +fn infer_table_name_handle_crude() { + assert_eq!("geometries", &infer_table_name("Geometry")); +} diff --git a/diesel_codegen/src/schema_inference/pg.rs b/diesel_codegen/src/schema_inference/pg.rs index 878c6d3a95b4..13d465d9bcfe 100644 --- a/diesel_codegen/src/schema_inference/pg.rs +++ b/diesel_codegen/src/schema_inference/pg.rs @@ -26,8 +26,6 @@ table! { } joinable!(pg_attribute -> pg_type (atttypid)); -select_column_workaround!(pg_attribute -> pg_type (attrelid, attname, atttypid, attnotnull, attnum, attisdropped)); -select_column_workaround!(pg_type -> pg_attribute (oid, typname)); table! { pg_index (indrelid) { @@ -81,7 +79,7 @@ pub fn get_table_data(conn: &PgConnection, table_name: &str) -> QueryResult)>(&conn).unwrap() @@ -103,7 +103,7 @@ macro_rules! bench_medium_complex_query { b.iter(|| { use schema::users::dsl::*; - let target = users.left_outer_join(posts::table) + let target = users.left_outer_join(posts::table, posts::user_id) .filter(hair_color.eq("black")) .order(name.desc()) .into_boxed(); diff --git a/diesel_tests/tests/filter.rs b/diesel_tests/tests/filter.rs index 312a47897d94..d1aff04fb69e 100644 --- a/diesel_tests/tests/filter.rs +++ b/diesel_tests/tests/filter.rs @@ -100,7 +100,7 @@ fn filter_after_joining() { let tess = User::new(2, "Tess"); let seans_post = Post::new(1, 1, "Hello", None); let tess_post = Post::new(2, 2, "World", None); - let source = users::table.inner_join(posts::table); + let source = users::table.inner_join(posts::table, posts::user_id); assert_eq!(Ok((sean, seans_post)), source.filter(name.eq("Sean")).first(&connection)); assert_eq!(Ok((tess, tess_post)), diff --git a/diesel_tests/tests/joins.rs b/diesel_tests/tests/joins.rs index c3f5521599de..ca524f37de59 100644 --- a/diesel_tests/tests/joins.rs +++ b/diesel_tests/tests/joins.rs @@ -15,8 +15,8 @@ fn belongs_to() { let seans_post = Post::new(1, 1, "Hello", Some("Content")); let tess_post = Post::new(2, 2, "World", None); - let expected_data = vec![(seans_post, sean), (tess_post, tess)]; - let source = posts::table.inner_join(users::table); + let expected_data = vec![(sean, seans_post), (tess, tess_post)]; + let source = posts::table.inner_join(users::table, posts::user_id); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -31,7 +31,7 @@ fn select_single_from_join() { (2, 2, 'World') ").unwrap(); - let source = posts::table.inner_join(users::table); + let source = posts::table.inner_join(users::table, posts::user_id); let select_name = source.select(users::name); let select_title = source.select(posts::title); @@ -55,7 +55,7 @@ fn select_multiple_from_join() { (2, 2, 'World') ").unwrap(); - let source = posts::table.inner_join(users::table) + let source = posts::table.inner_join(users::table, posts::user_id) .select((users::name, posts::title)); let expected_data = vec![ @@ -74,7 +74,7 @@ fn select_only_one_side_of_join() { connection.execute("INSERT INTO posts (user_id, title) VALUES (2, 'Hello')") .unwrap(); - let source = users::table.inner_join(posts::table).select(users::all_columns); + let source = users::table.inner_join(posts::table, posts::user_id).select(users::all_columns); let expected_data = vec![User::new(2, "Tess")]; let actual_data: Vec<_> = source.load(&connection).unwrap(); @@ -101,7 +101,7 @@ fn left_outer_joins() { (sean, Some(seans_second_post)), (tess, None) ]; - let source = users::table.left_outer_join(posts::table); + let source = users::table.left_outer_join(posts::table, posts::user_id); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -121,7 +121,7 @@ fn columns_on_right_side_of_left_outer_joins_are_nullable() { ("Sean".to_string(), Some("World".to_string())), ("Tess".to_string(), None), ]; - let source = users::table.left_outer_join(posts::table).select((users::name, posts::title)); + let source = users::table.left_outer_join(posts::table, posts::user_id).select((users::name, posts::title)); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -142,7 +142,7 @@ fn select_multiple_from_right_side_returns_optional_tuple() { None, ]; - let source = users::table.left_outer_join(posts::table).select((posts::title, posts::body)); + let source = users::table.left_outer_join(posts::table, posts::user_id).select((posts::title, posts::body)); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -150,6 +150,9 @@ fn select_multiple_from_right_side_returns_optional_tuple() { #[test] fn select_complex_from_left_join() { + use diesel::query_builder::SelectStatement; + use diesel::types::*; + let connection = connection_with_sean_and_tess_in_users_table(); connection.execute("INSERT INTO posts (user_id, title, body) VALUES @@ -160,12 +163,14 @@ fn select_complex_from_left_join() { let sean = User::new(1, "Sean"); let tess = User::new(2, "Tess"); let expected_data = vec![ - (sean.clone(), Some(("Hello".to_string(), Some("Content".to_string())))), - (sean, Some(("World".to_string(), None))), - (tess, None), + (sean.clone(), Some(("Hello".to_string(), Some("Content".to_string())))), + (sean, Some(("World".to_string(), None))), + (tess, None), ]; - let source = users::table.left_outer_join(posts::table).select((users::all_columns, (posts::title, posts::body))); + let source: SelectStatement<((Integer, Text, Nullable),_), _,_> + = users::table.left_outer_join(posts::table, posts::user_id) + .select((users::all_columns, (posts::title, posts::body))); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -173,6 +178,9 @@ fn select_complex_from_left_join() { #[test] fn select_right_side_with_nullable_column_first() { + use diesel::query_builder::SelectStatement; + use diesel::types::*; + let connection = connection_with_sean_and_tess_in_users_table(); connection.execute("INSERT INTO posts (user_id, title, body) VALUES @@ -188,10 +196,12 @@ fn select_right_side_with_nullable_column_first() { (tess, None), ]; - let source = users::table.left_outer_join(posts::table).select((users::all_columns, (posts::body, posts::title))); - let actual_data: Vec<_> = source.load(&connection).unwrap(); + let source: SelectStatement<((Integer, Text, Nullable),_), _,_> + = users::table.left_outer_join(posts::table, posts::user_id) + .select((users::all_columns, (posts::body, posts::title))); + let actual_data: Vec<_> = source.load(&connection).unwrap(); - assert_eq!(expected_data, actual_data); + assert_eq!(expected_data, actual_data); } #[test] @@ -202,13 +212,13 @@ fn select_then_join() { connection.execute("INSERT INTO posts (user_id, title) VALUES (1, 'Hello')") .unwrap(); let expected_data = vec![1]; - let data: Vec<_> = users.select(id).inner_join(posts::table) + let data: Vec<_> = users.select(id).inner_join(posts::table, posts::user_id) .load(&connection).unwrap(); assert_eq!(expected_data, data); let expected_data = vec![1, 2]; - let data: Vec<_> = users.select(id).left_outer_join(posts::table) + let data: Vec<_> = users.select(id).left_outer_join(posts::table, posts::user_id) .load(&connection).unwrap(); assert_eq!(expected_data, data); @@ -229,17 +239,18 @@ fn join_through_other() { NewComment(posts[0].id, "OMG"), NewComment(posts[1].id, "WTF"), NewComment(posts[2].id, "Best post ever!!!") ], comments::table, &connection); + let comments = comments::table.load::(&connection).unwrap(); - let data = users.inner_join(comments::table).load(&connection) + let data = users.inner_join(posts::table, posts::user_id).inner_join(comments::table, comments::post_id).select((users::all_columns(), comments::table::all_columns())).load(&connection) .unwrap(); let sean = User::new(1, "Sean"); let tess = User::new(2, "Tess"); let expected_data = vec![ - (sean.clone(), comments[0].clone()), - (tess, comments[1].clone()), - (sean, comments[2].clone()), + (sean.clone(), comments[0].clone()), + (tess, comments[1].clone()), + (sean, comments[2].clone()) ]; assert_eq!(expected_data, data); } diff --git a/diesel_tests/tests/perf_details.rs b/diesel_tests/tests/perf_details.rs index 4e228aefbfc2..68200db12bf2 100644 --- a/diesel_tests/tests/perf_details.rs +++ b/diesel_tests/tests/perf_details.rs @@ -1,5 +1,5 @@ use schema::users::dsl::*; -use schema::posts::dsl::{posts, title}; +use schema::posts::dsl::{posts, title, user_id}; use std::cmp::max; use std::mem; use diesel::*; @@ -9,25 +9,25 @@ use diesel::query_builder::AsQuery; fn complex_queries_with_no_data_have_no_size() { assert_eq!(0, mem::size_of_val(&users.as_query())); assert_eq!(0, mem::size_of_val(&users.select(id).as_query())); - assert_eq!(0, mem::size_of_val(&users.inner_join(posts).filter(name.eq(title)))); + assert_eq!(0, mem::size_of_val(&users.inner_join(posts, user_id).filter(name.eq(title)))); } #[test] fn queries_with_data_are_no_bigger_than_their_variable_data() { assert_eq!( mem::size_of_val(&"Sean"), - mem::size_of_val(&users.inner_join(posts).filter(name.eq("Sean"))) + mem::size_of_val(&users.inner_join(posts, user_id).filter(name.eq("Sean"))) ); assert_eq!( mem::size_of::(), - mem::size_of_val(&users.inner_join(posts).filter(id.eq(1))) + mem::size_of_val(&users.inner_join(posts, user_id).filter(id.eq(1))) ); - let source = users.inner_join(posts).filter(name.eq("Sean")).filter(id.eq(1)); + let source = users.inner_join(posts, user_id).filter(name.eq("Sean")).filter(id.eq(1)); assert_eq!( mem::size_of_val(&"Sean") + max(mem::align_of_val(&source), mem::size_of::()), mem::size_of_val(&source) ); - let source = users.inner_join(posts).filter(name.eq("Sean").and(id.eq(1))); + let source = users.inner_join(posts, user_id).filter(name.eq("Sean").and(id.eq(1))); assert_eq!( mem::size_of_val(&"Sean") + max(mem::align_of_val(&source), mem::size_of::()), mem::size_of_val(&source) diff --git a/diesel_tests/tests/schema.rs b/diesel_tests/tests/schema.rs index e62c9b20c68f..4788ab2d0699 100644 --- a/diesel_tests/tests/schema.rs +++ b/diesel_tests/tests/schema.rs @@ -49,10 +49,6 @@ pub use self::backend_specifics::*; numeric_expr!(users::id); -select_column_workaround!(users -> comments (id, name, hair_color)); -select_column_workaround!(comments -> users (id, post_id, text)); - -join_through!(users -> posts -> comments); #[derive(Debug, PartialEq, Eq, Queryable, Clone)] #[insertable_into(users)]