diff --git a/diesel/src/expression/expression_methods/eq_all.rs b/diesel/src/expression/expression_methods/eq_all.rs index cfaab8342fcc..98c3cb7e5a68 100644 --- a/diesel/src/expression/expression_methods/eq_all.rs +++ b/diesel/src/expression/expression_methods/eq_all.rs @@ -14,24 +14,28 @@ pub trait EqAll { fn eq_all(self, rhs: Rhs) -> Self::Output; } -impl EqAll>> - for Cons> where +impl EqAll<(R1, R2, ...RTail)> + for (L1, L2, ...LTail) where L1: EqAll, - Cons: EqAll>, + (L2, ...LTail): EqAll<(R2, ...RTail)>, + LTail: Tuple, + RTail: Tuple, { - type Output = And<>::Output, as EqAll>>::Output>; + type Output = And<>::Output, <(L1, ...LTail) as EqAll<(R1, ...RTail)>>::Output>; - fn eq_all(self, rhs: Cons>) -> Self::Output { - self.0.eq_all(rhs.0).and(self.1.eq_all(rhs.1)) + fn eq_all(self, rhs: (R1, R2, ...RTail)) -> Self::Output { + let (lhead, ...ltail) = self; + let (rhead, ...rtail) = rhs; + lhead.eq_all(rhead).and(ltail.eq_all(rtail)) } } -impl EqAll> for Cons where +impl EqAll<(Right,)> for Cons<(Left,)> where Left: EqAll, { type Output = >::Output; - fn eq_all(self, rhs: Cons) -> Self::Output { + fn eq_all(self, rhs: (Right,)) -> Self::Output { self.0.eq_all(rhs.0) } } diff --git a/diesel/src/expression/mod.rs b/diesel/src/expression/mod.rs index b21c9b80f82f..bec9e52fcac3 100644 --- a/diesel/src/expression/mod.rs +++ b/diesel/src/expression/mod.rs @@ -82,15 +82,17 @@ impl<'a, T: Expression + ?Sized> Expression for &'a T { type SqlType = T::SqlType; } -impl Expression for Cons where +impl Expression for (Head, ...Tail) where Head: Expression + NonAggregate, Tail: Expression + NonAggregate, + Tail: Tuple, + Tail::SqlType: Tuple, { - type SqlType = Cons; + type SqlType = (Head::SqlType, ...Tail::SqlType); } -impl Expression for Nil { - type SqlType = Nil; +impl Expression for () { + type SqlType = (); } /// Describes how a type can be represented as an expression for a given type. @@ -142,16 +144,18 @@ impl<'a, T: ?Sized, QS> SelectableExpression for &'a T where type SqlTypeForSelect = T::SqlTypeForSelect; } -impl SelectableExpression for Cons where +impl SelectableExpression for (Head, ...Tail) where Head: SelectableExpression, Tail: SelectableExpression, - Cons: Expression, + (Head, ...Tail): Expression, + Tail: Tuple, + Tail::SqlTypeForSelect: Tuple, { - type SqlTypeForSelect = Cons; + type SqlTypeForSelect = (Head::SqlTypeForSelect, ...Tail::SqlTypeForSelect); } -impl SelectableExpression for Nil { - type SqlTypeForSelect = Nil; +impl SelectableExpression for () { + type SqlTypeForSelect = (); } /// Marker trait to indicate that an expression does not include any aggregate @@ -167,13 +171,13 @@ impl NonAggregate for Box { impl<'a, T: NonAggregate + ?Sized> NonAggregate for &'a T { } -impl NonAggregate for Cons where +impl NonAggregate for (Head, ...Tail) where Head: NonAggregate, - Tail: NonAggregate, + Tail: NonAggregate + Tuple, { } -impl NonAggregate for Nil { +impl NonAggregate for () { } use query_builder::{QueryFragment, QueryId}; diff --git a/diesel/src/insertable/hlist_impls.rs b/diesel/src/insertable/hlist_impls.rs index ecd418af52fc..8768f61f3954 100644 --- a/diesel/src/insertable/hlist_impls.rs +++ b/diesel/src/insertable/hlist_impls.rs @@ -2,9 +2,10 @@ use hlist::*; use super::*; use query_builder::QueryFragment; -impl InsertValues for Cons where +impl InsertValues for (Head, ...Tail) where DB: Backend, - Cons: InsertValuesRecursive, + (Head, ...Tail): InsertValuesRecursive, + Tail: Tuple, { fn column_names(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { InsertValuesRecursive::::column_names(self, false, out) @@ -23,14 +24,14 @@ impl InsertValues for Cons where } #[doc(hidden)] -pub trait InsertValuesRecursive { +pub trait InsertValuesRecursive: Tuple { fn column_names(&self, comma_needed: bool, out: &mut DB::QueryBuilder) -> BuildQueryResult; fn values_clause(&self, comma_needed: bool, out: &mut DB::QueryBuilder) -> BuildQueryResult; fn values_bind_params(&self, out: &mut DB::BindCollector) -> QueryResult<()>; } impl InsertValuesRecursive - for Cons, Tail> where + for (ColumnInsertValue, ...Tail) where DB: Backend + SupportsDefaultKeyword, Col: Column, Col::SqlType: IntoNullable, @@ -71,7 +72,7 @@ use sqlite::Sqlite; #[cfg(feature="sqlite")] impl InsertValuesRecursive - for Cons, Tail> where + for (ColumnInsertValue, ...Tail) where Col: Column, Col::SqlType: IntoNullable, Expr: Expression::Nullable> + QueryFragment, @@ -118,7 +119,7 @@ impl InsertValuesRecursive } } -impl InsertValuesRecursive for Nil { +impl InsertValuesRecursive for () { fn column_names(&self, _: bool, _: &mut DB::QueryBuilder) -> BuildQueryResult { Ok(()) } diff --git a/diesel/src/query_builder/mod.rs b/diesel/src/query_builder/mod.rs index 73f1fb728ecc..80f12e283e0e 100644 --- a/diesel/src/query_builder/mod.rs +++ b/diesel/src/query_builder/mod.rs @@ -132,30 +132,40 @@ impl QueryFragment for () { } } -impl QueryFragment for Cons> where +trait QueryFragmentTuple: Tuple { + fn to_sql(self, out: &mut DB::QueryBuilder) -> BuildQueryResult; + fn collect_binds(self, out: &mut DB::BindCollector) -> QueryResult<()>; + fn is_safe_to_cache_prepared(self) -> bool; +} + +impl QueryFragmentTuple for (Head, Head2, ...Tail) where DB: Backend, Head: QueryFragment, - Cons: QueryFragment, + Tail: Tuple, + (Head2, ...Tail): QueryFragmentTuple, { - fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { - try!(self.0.to_sql(out)); + fn to_sql(self, out: &mut DB::QueryBuilder) -> BuildQueryResult { + let (head, ...tail) = self; + try!(head.to_sql(out)); out.push_sql(", "); - try!(self.1.to_sql(out)); + try!(tail.to_sql(out)); Ok(()) } - fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> { - try!(self.0.collect_binds(out)); - try!(self.1.collect_binds(out)); + fn collect_binds(self, out: &mut DB::BindCollector) -> QueryResult<()> { + let (head, ...tail) = self; + try!(head.collect_binds(out)); + try!(tail.collect_binds(out)); Ok(()) } - fn is_safe_to_cache_prepared(&self) -> bool { - self.0.is_safe_to_cache_prepared() && self.1.is_safe_to_cache_prepared() + fn is_safe_to_cache_prepared(self) -> bool { + let (head, ...tail) = self; + head.is_safe_to_cache_prepared() && tail.is_safe_to_cache_prepared() } } -impl QueryFragment for Cons where +impl QueryFragmentTuple for (Head,) where DB: Backend, Head: QueryFragment, { @@ -172,6 +182,24 @@ impl QueryFragment for Cons where } } +impl QueryFragment for (...T) where + DB: Backend, + T: Tuple, + for<'a> T::AsRefs<'a>: QueryFragmentTuple, +{ + fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { + self.elements_as_ref().to_sql(out) + } + + fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> { + self.elements_as_ref().collect_binds(out) + } + + fn is_safe_to_cache_prepared(&self) -> bool { + self.elements_as_ref().is_safe_to_cache_prepared(out) + } +} + /// Types that can be converted into a complete, typed SQL query. This is used /// internally to automatically add the right select clause when none is /// specified, or to automatically add `RETURNING *` in certain contexts diff --git a/diesel/src/query_builder/query_id.rs b/diesel/src/query_builder/query_id.rs index 69e859e35a0e..437ffb8b0aa5 100644 --- a/diesel/src/query_builder/query_id.rs +++ b/diesel/src/query_builder/query_id.rs @@ -48,8 +48,25 @@ impl QueryId for QueryFragment { } } -impl_query_id!(Cons); -impl_query_id!(Nil); +impl QueryId for (Head, ...Tail) where + Head: QueryId, + Tail: Tuple + QueryId, + Tail::QueryId: Tuple, +{ + type QueryId = (Head::QueryId, ...Tail::QueryId); + + fn has_static_query_id() -> bool { + Head::has_static_query_id() && Tail::has_static_query_id() + } +} + +impl QueryId for () { + type QueryId = (); + + fn has_static_query_id() -> bool { + true + } +} #[cfg(test)] mod tests { diff --git a/diesel/src/query_builder/update_statement/changeset.rs b/diesel/src/query_builder/update_statement/changeset.rs index 98c501ea9038..9c935bcd477e 100644 --- a/diesel/src/query_builder/update_statement/changeset.rs +++ b/diesel/src/query_builder/update_statement/changeset.rs @@ -62,75 +62,99 @@ impl, DB: Backend> Changeset for Option { } } -impl AsChangeset for Cons> where +impl AsChangeset for (Head, Head2, ...Tail) where Head: AsChangeset, - Cons: AsChangeset, + Tail: Tuple, + (Head2, ...Tail): AsChangeset, + <(Head2, ...Tail) as AsChangeset>::Changeset: Tuple, { type Target = Head::Target; - type Changeset = Cons< + type Changeset = ( Head::Changeset, - as AsChangeset>::Changeset, - >; + ...<(Head2, ...Tail) as AsChangeset>::Changeset, + ); fn as_changeset(self) -> Self::Changeset { - Cons( - self.0.as_changeset(), - self.1.as_changeset(), - ) + let (head, ...tail) = self; + (head.as_changeset(), ...tail.as_changeset()) } } -impl AsChangeset for Cons where +impl AsChangeset for (Head,) where Head: AsChangeset, { type Target = Head::Target; - type Changeset = Cons; + type Changeset = (Head::Changeset,); fn as_changeset(self) -> Self::Changeset { - Cons( - self.0.as_changeset(), - Nil, - ) + (self.0.as_changeset(),) } } -impl Changeset for Cons where +trait ChangesetTuple: Tuple { + fn is_noop(self) -> bool; + fn to_sql(self, out: &mut DB::QueryBuilder) -> BuildQueryResult; + fn collect_binds(self, out: &mut DB::BindCollector) -> QueryResult<()>; +} + +impl ChangesetTuple for (Head, ...Tail) where DB: Backend, Head: Changeset, - Tail: Changeset, + Tail: ChangesetTuple, { - fn is_noop(&self) -> bool { - self.0.is_noop() && self.1.is_noop() + fn is_noop(self) -> bool { + let (head, ...tail) = self; + head.is_noop() && tail.is_noop() } - fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { + fn to_sql(self, out: &mut DB::QueryBuilder) -> BuildQueryResult { use query_builder::QueryBuilder; - if !self.0.is_noop() { - try!(self.0.to_sql(out)); - if !self.1.is_noop() { + let (head, ...tail) = self; + if !head.is_noop() { + try!(head.to_sql(out)); + if !tail.is_noop() { out.push_sql(", "); } } - self.1.to_sql(out) + tail.to_sql(out) } - fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> { - try!(self.0.collect_binds(out)); - self.1.collect_binds(out) + fn collect_binds(self, out: &mut DB::BindCollector) -> QueryResult<()> { + let (head, ...tail) = self; + try!(head.collect_binds(out)); + tail.collect_binds(out) } } -impl Changeset for Nil { - fn is_noop(&self) -> bool { +impl ChangesetTuple for () { + fn is_noop(self) -> bool { true } - fn to_sql(&self, _: &mut DB::QueryBuilder) -> BuildQueryResult { + fn to_sql(self, _: &mut DB::QueryBuilder) -> BuildQueryResult { Ok(()) } - fn collect_binds(&self, _: &mut DB::BindCollector) -> QueryResult<()> { + fn collect_binds(self, _: &mut DB::BindCollector) -> QueryResult<()> { Ok(()) } } + +impl Changeset for (...T) where + DB: Backend, + T: Tuple, + for<'a> T::AsRefs<'a>: ChangesetTuple, +{ + fn is_noop(&self) -> bool { + self.elements_as_ref().is_noop() + } + + fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { + self.elements_as_ref().to_sql(out) + } + + fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> { + self.elements_as_ref().collect_binds(out) + } +} diff --git a/diesel/src/types/impls/hlist.rs b/diesel/src/types/impls/hlist.rs index c5ade544bedd..dd696216fa94 100644 --- a/diesel/src/types/impls/hlist.rs +++ b/diesel/src/types/impls/hlist.rs @@ -5,8 +5,9 @@ use query_source::Queryable; use row::Row; use types::{HasSqlType, FromSqlRow, Nullable, NotNull}; -impl HasSqlType> for DB where +impl HasSqlType<(Head, ...Tail)> for DB where DB: Backend + HasSqlType + HasSqlType, + Tail: Tuple, { fn metadata() -> DB::TypeMetadata { unreachable!("hlists don't implement `ToSql` directly"); @@ -18,7 +19,7 @@ impl HasSqlType> for DB where } } -impl HasSqlType for DB { +impl HasSqlType<()> for DB { fn metadata() -> DB::TypeMetadata { unreachable!("hlists don't implement `ToSql` directly"); } @@ -28,64 +29,68 @@ impl HasSqlType for DB { } } -impl NotNull for Cons { +impl NotNull for (...T) { } -impl FromSqlRow, DB> - for Cons where +impl FromSqlRow<(HeadST, ...TailST), DB> + for (Head, ...Tail) where Head: FromSqlRow, - Tail: FromSqlRow, + Tail: FromSqlRow + Tuple, + TailST: Tuple, DB: Backend + HasSqlType + HasSqlType, { fn build_from_row>(row: &mut R) -> BuildQueryResult { - Ok(Cons( + Ok(( try!(Head::build_from_row(row)), - try!(Tail::build_from_row(row)), + ...try!(Tail::build_from_row(row)), )) } } -impl FromSqlRow for Nil { +impl FromSqlRow<(), DB> for () { fn build_from_row>(_: &mut R) -> BuildQueryResult { - Ok(Nil) + Ok(()) } } impl FromSqlRow, DB> - for Option> where + for Option<(Head, ...Tail)> where DB: Backend + HasSqlType, - Cons: FromSqlRow + Hlist, + (Head, ...Tail): FromSqlRow, + Tail: Tuple, ST: NotNull, { fn build_from_row>(row: &mut R) -> BuildQueryResult { - if row.next_is_null(Cons::::len()) { + if row.next_is_null((Head, ...Tail)::len()) { Ok(None) } else { - Cons::::build_from_row(row).map(Some) + (Head, ...Tail)::build_from_row(row).map(Some) } } } -impl Queryable, DB> - for Cons where +impl Queryable<(HeadST, ...TailST), DB> + for (Head, ...Tail) where DB: Backend + HasSqlType + HasSqlType, Head: Queryable, - Tail: Queryable, + Tail: Queryable + Tuple, + >::Row: Tuple, + TailST: Tuple, { - type Row = Cons; + type Row = (Head::Row, ...Tail::Row); - fn build(Cons(head, tail): Self::Row) -> Self { - Cons( + fn build((head, ...tail): Self::Row) -> Self { + ( Head::build(head), - Tail::build(tail), + ...Tail::build(tail), ) } } -impl Queryable for Nil { - type Row = Nil; +impl Queryable<(), DB> for () { + type Row = (); fn build(_: Self::Row) -> Self { - Nil + () } }