Skip to content

Commit

Permalink
Hypothetical code if RFC 1935 were implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
sgrif committed Mar 3, 2017
1 parent fda765c commit 51ca264
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 94 deletions.
20 changes: 12 additions & 8 deletions diesel/src/expression/expression_methods/eq_all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,28 @@ pub trait EqAll<Rhs> {
fn eq_all(self, rhs: Rhs) -> Self::Output;
}

impl<L1, L2, LTail, R1, R2, RTail> EqAll<Cons<R1, Cons<R2, RTail>>>
for Cons<L1, Cons<L2, LTail>> where
impl<L1, L2, LTail, R1, R2, RTail> EqAll<(R1, R2, ...RTail)>
for (L1, L2, ...LTail) where
L1: EqAll<R1>,
Cons<L2, LTail>: EqAll<Cons<R2, RTail>>,
(L2, ...LTail): EqAll<(R2, ...RTail)>,

This comment has been minimized.

Copy link
@eddyb

eddyb Mar 3, 2017

Why isn't this LTail: EqAll<RTail>, i.e. why is there more than one head being eaten in this impl?

This comment has been minimized.

Copy link
@sgrif

sgrif Mar 3, 2017

Author Member

This is a case where we cannot implement it correctly for 0 elements, so instead of implementing on (Head, ...Tail) and (), we implement on (Head, Head2, ...Tail) and (Head,). Usually when we do this it's because we need to give special handling to the first and/or last elements (e.g. inserting commas), but in this case it's because the output is something like And<Eq<Left1, Right2>, Eq<Left2, Right2>> and I didn't want to have to do some nonsense to make And<Eq<Left, Right>, True> generate equivalent SQL to Eq<Left, Right>

This comment has been minimized.

Copy link
@eddyb

eddyb Mar 3, 2017

You can keep the (Head,) impl though, because you have a bound that is not implemented by ().

EDIT: I mean that the two impls won't overlap and this should be true with HList today too.

This comment has been minimized.

Copy link
@sgrif

sgrif Mar 3, 2017

Author Member

Oh you're right, thanks!

LTail: Tuple,
RTail: Tuple,
{
type Output = And<<L1 as EqAll<R1>>::Output, <Cons<L2, LTail> as EqAll<Cons<R2, RTail>>>::Output>;
type Output = And<<L1 as EqAll<R1>>::Output, <(L1, ...LTail) as EqAll<(R1, ...RTail)>>::Output>;

fn eq_all(self, rhs: Cons<R1, Cons<R2, RTail>>) -> 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<Left, Right> EqAll<Cons<Right, Nil>> for Cons<Left, Nil> where
impl<Left, Right> EqAll<(Right,)> for Cons<(Left,)> where
Left: EqAll<Right>,
{
type Output = <Left as EqAll<Right>>::Output;

fn eq_all(self, rhs: Cons<Right, Nil>) -> Self::Output {
fn eq_all(self, rhs: (Right,)) -> Self::Output {
self.0.eq_all(rhs.0)
}
}
28 changes: 16 additions & 12 deletions diesel/src/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,17 @@ impl<'a, T: Expression + ?Sized> Expression for &'a T {
type SqlType = T::SqlType;
}

impl<Head, Tail> Expression for Cons<Head, Tail> where
impl<Head, Tail> Expression for (Head, ...Tail) where
Head: Expression + NonAggregate,
Tail: Expression + NonAggregate,
Tail: Tuple,
Tail::SqlType: Tuple,
{
type SqlType = Cons<Head::SqlType, Tail::SqlType>;
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.
Expand Down Expand Up @@ -142,16 +144,18 @@ impl<'a, T: ?Sized, QS> SelectableExpression<QS> for &'a T where
type SqlTypeForSelect = T::SqlTypeForSelect;
}

impl<Head, Tail, QS> SelectableExpression<QS> for Cons<Head, Tail> where
impl<Head, Tail, QS> SelectableExpression<QS> for (Head, ...Tail) where
Head: SelectableExpression<QS>,
Tail: SelectableExpression<QS>,
Cons<Head, Tail>: Expression,
(Head, ...Tail): Expression,
Tail: Tuple,
Tail::SqlTypeForSelect: Tuple,
{
type SqlTypeForSelect = Cons<Head::SqlTypeForSelect, Tail::SqlTypeForSelect>;
type SqlTypeForSelect = (Head::SqlTypeForSelect, ...Tail::SqlTypeForSelect);
}

impl<QS> SelectableExpression<QS> for Nil {
type SqlTypeForSelect = Nil;
impl<QS> SelectableExpression<QS> for () {
type SqlTypeForSelect = ();
}

/// Marker trait to indicate that an expression does not include any aggregate
Expand All @@ -167,13 +171,13 @@ impl<T: NonAggregate + ?Sized> NonAggregate for Box<T> {
impl<'a, T: NonAggregate + ?Sized> NonAggregate for &'a T {
}

impl<Head, Tail> NonAggregate for Cons<Head, Tail> where
impl<Head, Tail> NonAggregate for (Head, ...Tail) where
Head: NonAggregate,
Tail: NonAggregate,
Tail: NonAggregate + Tuple,
{
}

impl NonAggregate for Nil {
impl NonAggregate for () {
}

use query_builder::{QueryFragment, QueryId};
Expand Down
13 changes: 7 additions & 6 deletions diesel/src/insertable/hlist_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ use hlist::*;
use super::*;
use query_builder::QueryFragment;

impl<Head, Tail, DB> InsertValues<DB> for Cons<Head, Tail> where
impl<Head, Tail, DB> InsertValues<DB> for (Head, ...Tail) where
DB: Backend,
Cons<Head, Tail>: InsertValuesRecursive<DB>,
(Head, ...Tail): InsertValuesRecursive<DB>,
Tail: Tuple,
{
fn column_names(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
InsertValuesRecursive::<DB>::column_names(self, false, out)
Expand All @@ -23,14 +24,14 @@ impl<Head, Tail, DB> InsertValues<DB> for Cons<Head, Tail> where
}

#[doc(hidden)]
pub trait InsertValuesRecursive<DB: Backend> {
pub trait InsertValuesRecursive<DB: Backend>: 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<Col, Expr, Tail, DB> InsertValuesRecursive<DB>
for Cons<ColumnInsertValue<Col, Expr>, Tail> where
for (ColumnInsertValue<Col, Expr>, ...Tail) where
DB: Backend + SupportsDefaultKeyword,
Col: Column,
Col::SqlType: IntoNullable,
Expand Down Expand Up @@ -71,7 +72,7 @@ use sqlite::Sqlite;

#[cfg(feature="sqlite")]
impl<Col, Expr, Tail> InsertValuesRecursive<Sqlite>
for Cons<ColumnInsertValue<Col, Expr>, Tail> where
for (ColumnInsertValue<Col, Expr>, ...Tail) where
Col: Column,
Col::SqlType: IntoNullable,
Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable> + QueryFragment<Sqlite>,
Expand Down Expand Up @@ -118,7 +119,7 @@ impl<Col, Expr, Tail> InsertValuesRecursive<Sqlite>
}
}

impl<DB: Backend> InsertValuesRecursive<DB> for Nil {
impl<DB: Backend> InsertValuesRecursive<DB> for () {
fn column_names(&self, _: bool, _: &mut DB::QueryBuilder) -> BuildQueryResult {
Ok(())
}
Expand Down
50 changes: 39 additions & 11 deletions diesel/src/query_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,30 +132,40 @@ impl<DB: Backend> QueryFragment<DB> for () {
}
}

impl<Head, Head2, Tail, DB> QueryFragment<DB> for Cons<Head, Cons<Head2, Tail>> where
trait QueryFragmentTuple<DB>: 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<Head, Head2, Tail, DB> QueryFragmentTuple<DB> for (Head, Head2, ...Tail) where
DB: Backend,
Head: QueryFragment<DB>,
Cons<Head2, Tail>: QueryFragment<DB>,
Tail: Tuple,
(Head2, ...Tail): QueryFragmentTuple<DB>,
{
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<Head, DB> QueryFragment<DB> for Cons<Head, Nil> where
impl<Head, DB> QueryFragmentTuple<DB> for (Head,) where
DB: Backend,
Head: QueryFragment<DB>,
{
Expand All @@ -172,6 +182,24 @@ impl<Head, DB> QueryFragment<DB> for Cons<Head, Nil> where
}
}

impl<T, DB> QueryFragment<DB> for (...T) where
DB: Backend,
T: Tuple,
for<'a> T::AsRefs<'a>: QueryFragmentTuple<DB>,
{
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
Expand Down
21 changes: 19 additions & 2 deletions diesel/src/query_builder/query_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,25 @@ impl<DB> QueryId for QueryFragment<DB> {
}
}

impl_query_id!(Cons<T, U>);
impl_query_id!(Nil);
impl<Head, ...Tail> 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 {
Expand Down
86 changes: 55 additions & 31 deletions diesel/src/query_builder/update_statement/changeset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,75 +62,99 @@ impl<T: Changeset<DB>, DB: Backend> Changeset<DB> for Option<T> {
}
}

impl<Head, Head2, Tail> AsChangeset for Cons<Head, Cons<Head2, Tail>> where
impl<Head, Head2, Tail> AsChangeset for (Head, Head2, ...Tail) where
Head: AsChangeset,
Cons<Head2, Tail>: AsChangeset<Target=Head::Target>,
Tail: Tuple,
(Head2, ...Tail): AsChangeset<Target=Head::Target>,
<(Head2, ...Tail) as AsChangeset>::Changeset: Tuple,
{
type Target = Head::Target;
type Changeset = Cons<
type Changeset = (
Head::Changeset,
<Cons<Head2, Tail> 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<Head> AsChangeset for Cons<Head, Nil> where
impl<Head> AsChangeset for (Head,) where
Head: AsChangeset,
{
type Target = Head::Target;
type Changeset = Cons<Head::Changeset, Nil>;
type Changeset = (Head::Changeset,);

fn as_changeset(self) -> Self::Changeset {
Cons(
self.0.as_changeset(),
Nil,
)
(self.0.as_changeset(),)
}
}

impl<Head, Tail, DB> Changeset<DB> for Cons<Head, Tail> where
trait ChangesetTuple<DB: Backend>: 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<Head, Tail, DB> ChangesetTuple<DB> for (Head, ...Tail) where
DB: Backend,
Head: Changeset<DB>,
Tail: Changeset<DB>,
Tail: ChangesetTuple<DB>,
{
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<DB: Backend> Changeset<DB> for Nil {
fn is_noop(&self) -> bool {
impl<DB: Backend> ChangesetTuple<DB> 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<DB, T> Changeset<DB> for (...T) where
DB: Backend,
T: Tuple,
for<'a> T::AsRefs<'a>: ChangesetTuple<DB>,
{
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)
}
}
Loading

0 comments on commit 51ca264

Please sign in to comment.