diff --git a/diesel/Cargo.toml b/diesel/Cargo.toml index 571f023bbeb7..01e43cbc5249 100644 --- a/diesel/Cargo.toml +++ b/diesel/Cargo.toml @@ -26,6 +26,7 @@ url = { version = "1.4.0", optional = true } uuid = { version = ">=0.2.0, <0.5.0", optional = true, features = ["use_std"] } [dev-dependencies] +assert_matches = "1.0.1" cfg-if = "0.1.0" diesel_codegen = "0.11.0" dotenv = "0.8.0" @@ -36,14 +37,13 @@ tempdir = "^0.3.4" default = ["with-deprecated"] extras = ["chrono", "serde_json", "uuid", "deprecated-time"] unstable = [] +conservative-macro-names = [] +deprecated-time = ["time"] lint = ["clippy"] -large-tables = [] -huge-tables = ["large-tables"] +mysql = ["mysqlclient-sys", "url"] postgres = ["pq-sys"] sqlite = ["libsqlite3-sys", "libc"] -mysql = ["mysqlclient-sys", "url"] with-deprecated = [] -deprecated-time = ["time"] [badges] travis-ci = { repository = "diesel-rs/diesel" } diff --git a/diesel/src/expression/expression_methods/eq_all.rs b/diesel/src/expression/expression_methods/eq_all.rs index 31f980c17545..ce9a3edd6705 100644 --- a/diesel/src/expression/expression_methods/eq_all.rs +++ b/diesel/src/expression/expression_methods/eq_all.rs @@ -1,9 +1,10 @@ use expression::Expression; -use expression::predicates::And; use expression::expression_methods::*; +use expression::predicates::And; +use hlist::*; use types::Bool; -/// This method is used by `FindDsl` to work with tuples. Because we cannot +/// This method is used by `FindDsl` to work with hlists. Because we cannot /// express this without specialization or overlapping impls, it is brute force /// implemented on columns in the `column!` macro. #[doc(hidden)] @@ -13,45 +14,24 @@ pub trait EqAll { fn eq_all(self, rhs: Rhs) -> Self::Output; } -// FIXME: This is much easier to represent with a macro once macro types are stable -// which appears to be slated for 1.13 -impl EqAll<(R1, R2)> for (L1, L2) where - L1: EqAll, - L2: EqAll, +impl EqAll> + for Cons where + LHead: EqAll, + LTail: EqAll, { - type Output = And<>::Output, >::Output>; + type Output = And<>::Output, >::Output>; - fn eq_all(self, rhs: (R1, R2)) -> Self::Output { + fn eq_all(self, rhs: Cons) -> Self::Output { self.0.eq_all(rhs.0).and(self.1.eq_all(rhs.1)) } } -impl EqAll<(R1, R2, R3)> for (L1, L2, L3) where - L1: EqAll, - L2: EqAll, - L3: EqAll, -{ - type Output = And<>::Output, And<>::Output, >::Output>>; - - fn eq_all(self, rhs: (R1, R2, R3)) -> Self::Output { - self.0.eq_all(rhs.0).and( - self.1.eq_all(rhs.1).and(self.2.eq_all(rhs.2))) - } -} - -impl EqAll<(R1, R2, R3, R4)> for (L1, L2, L3, L4) where - L1: EqAll, - L2: EqAll, - L3: EqAll, - L4: EqAll, +impl EqAll> for Cons where + Left: EqAll, { - type Output = And<>::Output, And<>::Output, And<>::Output, >::Output>>>; + type Output = >::Output; - fn eq_all(self, rhs: (R1, R2, R3, R4)) -> Self::Output { - self.0.eq_all(rhs.0).and( - self.1.eq_all(rhs.1).and( - self.2.eq_all(rhs.2).and( - self.3.eq_all(rhs.3) - ))) + fn eq_all(self, rhs: Cons) -> Self::Output { + self.0.eq_all(rhs.0) } } diff --git a/diesel/src/expression/functions/mod.rs b/diesel/src/expression/functions/mod.rs index 0d49dbf6f180..6abe5ab0b2ba 100644 --- a/diesel/src/expression/functions/mod.rs +++ b/diesel/src/expression/functions/mod.rs @@ -28,7 +28,7 @@ macro_rules! sql_function_body { #[allow(non_camel_case_types)] impl<$($arg_name),*> $crate::expression::Expression for $struct_name<$($arg_name),*> where - for <'a> ($(&'a $arg_name),*): $crate::expression::Expression, + for <'a> DieselHlist!($(&'a $arg_name),*): $crate::expression::Expression, { type SqlType = $return_type; } @@ -36,25 +36,25 @@ macro_rules! sql_function_body { #[allow(non_camel_case_types)] impl<$($arg_name),*, DB> $crate::query_builder::QueryFragment for $struct_name<$($arg_name),*> where DB: $crate::backend::Backend, - for <'a> ($(&'a $arg_name),*): $crate::query_builder::QueryFragment, + for <'a> DieselHlist!($(&'a $arg_name),*): $crate::query_builder::QueryFragment, { fn to_sql(&self, out: &mut DB::QueryBuilder) -> $crate::query_builder::BuildQueryResult { use $crate::query_builder::QueryBuilder; out.push_sql(concat!(stringify!($fn_name), "(")); try!($crate::query_builder::QueryFragment::to_sql( - &($(&self.$arg_name),*), out)); + &diesel_hlist!($(&self.$arg_name),*), out)); out.push_sql(")"); Ok(()) } fn collect_binds(&self, out: &mut DB::BindCollector) -> $crate::result::QueryResult<()> { try!($crate::query_builder::QueryFragment::collect_binds( - &($(&self.$arg_name),*), out)); + &diesel_hlist!($(&self.$arg_name),*), out)); Ok(()) } fn is_safe_to_cache_prepared(&self) -> bool { - ($(&self.$arg_name),*).is_safe_to_cache_prepared() + diesel_hlist!($(&self.$arg_name),*).is_safe_to_cache_prepared() } } diff --git a/diesel/src/expression/mod.rs b/diesel/src/expression/mod.rs index 7b6857d759df..b21c9b80f82f 100644 --- a/diesel/src/expression/mod.rs +++ b/diesel/src/expression/mod.rs @@ -62,6 +62,7 @@ pub use self::dsl::*; pub use self::sql_literal::SqlLiteral; use backend::Backend; +use hlist::*; /// Represents a typed fragment of SQL. Apps should not need to implement this /// type directly, but it may be common to use this as type boundaries. @@ -81,6 +82,17 @@ impl<'a, T: Expression + ?Sized> Expression for &'a T { type SqlType = T::SqlType; } +impl Expression for Cons where + Head: Expression + NonAggregate, + Tail: Expression + NonAggregate, +{ + type SqlType = Cons; +} + +impl Expression for Nil { + type SqlType = Nil; +} + /// Describes how a type can be represented as an expression for a given type. /// These types couldn't just implement [`Expression`](trait.Expression.html) /// directly, as many things can be used as an expression of multiple types. @@ -130,6 +142,18 @@ impl<'a, T: ?Sized, QS> SelectableExpression for &'a T where type SqlTypeForSelect = T::SqlTypeForSelect; } +impl SelectableExpression for Cons where + Head: SelectableExpression, + Tail: SelectableExpression, + Cons: Expression, +{ + type SqlTypeForSelect = Cons; +} + +impl SelectableExpression for Nil { + type SqlTypeForSelect = Nil; +} + /// Marker trait to indicate that an expression does not include any aggregate /// functions. Used to ensure that aggregate expressions aren't mixed with /// non-aggregate expressions in a select clause, and that they're never @@ -143,6 +167,15 @@ impl NonAggregate for Box { impl<'a, T: NonAggregate + ?Sized> NonAggregate for &'a T { } +impl NonAggregate for Cons where + Head: NonAggregate, + Tail: NonAggregate, +{ +} + +impl NonAggregate for Nil { +} + use query_builder::{QueryFragment, QueryId}; /// Helper trait used when boxing expressions. This exists to work around the diff --git a/diesel/src/hlist.rs b/diesel/src/hlist.rs new file mode 100644 index 000000000000..5a1a916e4483 --- /dev/null +++ b/diesel/src/hlist.rs @@ -0,0 +1,135 @@ +//! A heterogeneous list used by Diesel to emulate variadic functions. +//! +//! Most applications will not need to concern themselves with this module. See +//! the [`hlist!` macro](../macro.hlist.html) for common usage. +//! +//! A heterogeneous list, or hlist for short, is a singly linked list where the +//! type of each element may differ. This is represented statically in the type. +//! +//! Hlists are very similar to tuples in that they are finite, and heterogenous. +//! In general, you can think of `diesel_hlist!(1, "foo", MyStruct)` as +//! analogous to `(1, ("foo", (MyStruct, ())))`. +//! +//! However, they are significantly easier to work with in a generic context +//! than tuples. To implement a trait for all hlists, you need to implement that +//! trait for `Cons` and `Nil`. To implement a trait for all tuples, +//! you will need one impl per tuple size that you wish to support. The tradeoff +//! for this is that they are somewhat more difficult to work with in a +//! non-generic context. They cannot be indexed, they must be accessed via +//! pattern matching. +//! +//! Diesel exposes three macros to work with hlists. Hlists can be constructed +//! using [`diesel_hlist!`](../macro.diesel_hlist.html). The type of an hlist +//! can be referenced using [`DieselHlist!`](../macro.DieselHlist.html). +//! Patterns which match hlists can be constructed using +//! [`diesel_hlist_pat!`](../macro.diesel_hlist_pat.html). The type of +//! `diesel_hlist!(1, "foo", MyStruct)` is `DieselHlist!(i32, &str, MyStruct)` +//! and matches the pattern `diesel_hlist_pat!(x, y, z)`. + +use std::fmt::{Debug, Formatter, Error as FmtError}; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +/// An hlist which contains one or more items. `Tail` will always either be +/// `Cons` or `Nil`. +pub struct Cons(pub Head, pub Tail); +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +/// An empty hlist. +pub struct Nil; + +/// Utility trait for working with hlists +pub trait Hlist { + /// The total length of a list. + /// + /// Since this is represented in the type, this function does not take + /// `&self`. + /// + /// # Example + /// + /// ```rust + /// # #[macro_use] extern crate diesel; + /// # use diesel::hlist::*; + /// # fn main() { + /// assert_eq!(0, ::len()); + /// assert_eq!(1, ::len()); + /// assert_eq!(2, ::len()); + /// # } + fn len() -> usize; +} + +impl Hlist for Cons where + U: Hlist, +{ + fn len() -> usize { + U::len() + 1 + } +} + +impl Hlist for Nil { + fn len() -> usize { + 0 + } +} + +impl Debug for Nil { + fn fmt(&self, formatter: &mut Formatter) -> Result<(), FmtError> { + formatter.write_str("hlist()") + } +} + +impl Debug for Cons where + Cons: DebugItems, +{ + fn fmt(&self, formatter: &mut Formatter) -> Result<(), FmtError> { + let mut items: &DebugItems = &*self; + let mut formatter = formatter.debug_tuple("hlist"); + while let Some((head, tail)) = items.pop() { + items = tail; + formatter.field(head); + } + formatter.finish() + } +} + +#[doc(hidden)] +pub trait DebugItems { + fn pop(&self) -> Option<(&Debug, &DebugItems)>; +} + +impl DebugItems for Cons where + T: Debug, + U: DebugItems, +{ + fn pop(&self) -> Option<(&Debug, &DebugItems)> { + Some((&self.0, &self.1)) + } +} + +impl DebugItems for Nil { + fn pop(&self) -> Option<(&Debug, &DebugItems)> { + None + } +} + +#[test] +fn debug_empty_hlist() { + assert_eq!("hlist()", format!("{:?}", Nil)); + assert_eq!("hlist()", format!("{:#?}", Nil)); +} + +#[test] +fn debug_single_item_hlist() { + assert_eq!("hlist(1)", format!("{:?}", Cons(1, Nil))); + assert_eq!("hlist(2)", format!("{:?}", Cons(2, Nil))); + assert_eq!(r#"hlist("hello")"#, format!("{:?}", Cons("hello", Nil))); + assert_eq!(r#"hlist("world")"#, format!("{:?}", Cons("world", Nil))); +} + +#[test] +fn debug_multi_item_hlist() { + assert_eq!("hlist(1, 2)", format!("{:?}", Cons(1, Cons(2, Nil)))); + assert_eq!("hlist(2, 3)", format!("{:?}", Cons(2, Cons(3, Nil)))); + let str_hlist = Cons("hello", Cons("world", Nil)); + assert_eq!(r#"hlist("hello", "world")"#, format!("{:?}", str_hlist)); + let mixed_hlist = Cons("hello", Cons(1, Cons("world", Cons(2, Nil)))); + assert_eq!(r#"hlist("hello", 1, "world", 2)"#, format!("{:?}", mixed_hlist)); +} diff --git a/diesel/src/insertable/hlist_impls.rs b/diesel/src/insertable/hlist_impls.rs new file mode 100644 index 000000000000..ecd418af52fc --- /dev/null +++ b/diesel/src/insertable/hlist_impls.rs @@ -0,0 +1,133 @@ +use hlist::*; +use super::*; +use query_builder::QueryFragment; + +impl InsertValues for Cons where + DB: Backend, + Cons: InsertValuesRecursive, +{ + fn column_names(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { + InsertValuesRecursive::::column_names(self, false, out) + } + + fn values_clause(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { + out.push_sql("("); + try!(InsertValuesRecursive::::values_clause(self, false, out)); + out.push_sql(")"); + Ok(()) + } + + fn values_bind_params(&self, out: &mut DB::BindCollector) -> QueryResult<()> { + InsertValuesRecursive::::values_bind_params(self, out) + } +} + +#[doc(hidden)] +pub trait InsertValuesRecursive { + 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 + DB: Backend + SupportsDefaultKeyword, + Col: Column, + Col::SqlType: IntoNullable, + Expr: Expression::Nullable> + QueryFragment, + Tail: InsertValuesRecursive, +{ + fn column_names(&self, comma_needed: bool, out: &mut DB::QueryBuilder) -> BuildQueryResult { + if comma_needed { + out.push_sql(", "); + } + try!(out.push_identifier(Col::name())); + self.1.column_names(true, out) + } + + fn values_clause(&self, comma_needed: bool, out: &mut DB::QueryBuilder) -> BuildQueryResult { + if comma_needed { + out.push_sql(", "); + } + match self.0 { + ColumnInsertValue::Expression(_, ref value) => { + try!(value.to_sql(out)); + } + _ => out.push_sql("DEFAULT"), + } + self.1.values_clause(true, out) + } + + fn values_bind_params(&self, out: &mut DB::BindCollector) -> QueryResult<()> { + if let ColumnInsertValue::Expression(_, ref value) = self.0 { + try!(value.collect_binds(out)); + } + self.1.values_bind_params(out) + } +} + +#[cfg(feature="sqlite")] +use sqlite::Sqlite; + +#[cfg(feature="sqlite")] +impl InsertValuesRecursive + for Cons, Tail> where + Col: Column, + Col::SqlType: IntoNullable, + Expr: Expression::Nullable> + QueryFragment, + Tail: InsertValuesRecursive, +{ + fn column_names( + &self, + mut comma_needed: bool, + out: &mut ::QueryBuilder, + ) -> BuildQueryResult { + if let ColumnInsertValue::Expression(..) = self.0 { + if comma_needed { + out.push_sql(", "); + } + try!(out.push_identifier(Col::name())); + comma_needed = true; + } + self.1.column_names(comma_needed, out) + } + + fn values_clause( + &self, + mut comma_needed: bool, + out: &mut ::QueryBuilder, + ) -> BuildQueryResult { + if let ColumnInsertValue::Expression(_, ref value) = self.0 { + if comma_needed { + out.push_sql(", "); + } + try!(value.to_sql(out)); + comma_needed = true; + } + self.1.values_clause(comma_needed, out) + } + + fn values_bind_params( + &self, + out: &mut ::BindCollector, + ) -> QueryResult<()> { + if let ColumnInsertValue::Expression(_, ref value) = self.0 { + try!(value.collect_binds(out)); + } + self.1.values_bind_params(out) + } +} + +impl InsertValuesRecursive for Nil { + fn column_names(&self, _: bool, _: &mut DB::QueryBuilder) -> BuildQueryResult { + Ok(()) + } + + fn values_clause(&self, _: bool, _: &mut DB::QueryBuilder) -> BuildQueryResult { + Ok(()) + } + + fn values_bind_params(&self, _: &mut DB::BindCollector) -> QueryResult<()> { + Ok(()) + } +} diff --git a/diesel/src/insertable.rs b/diesel/src/insertable/mod.rs similarity index 99% rename from diesel/src/insertable.rs rename to diesel/src/insertable/mod.rs index 4c192901fe33..7a09cefc7a03 100644 --- a/diesel/src/insertable.rs +++ b/diesel/src/insertable/mod.rs @@ -7,6 +7,8 @@ use query_builder::{QueryBuilder, BuildQueryResult}; use query_source::{Table, Column}; use types::IntoNullable; +mod hlist_impls; + /// Represents that a structure can be used to to insert a new row into the /// database. This is automatically implemented for `&[T]` and `&Vec` for /// inserting more than one record. diff --git a/diesel/src/lib.rs b/diesel/src/lib.rs index 0b0745a1beb7..46e4bd6445c1 100644 --- a/diesel/src/lib.rs +++ b/diesel/src/lib.rs @@ -23,12 +23,11 @@ extern crate byteorder; -#[macro_use] -mod macros; +#[cfg(test)] #[macro_use] extern crate assert_matches; +#[cfg(test)] #[macro_use] extern crate cfg_if; -#[cfg(test)] #[macro_use] -extern crate cfg_if; +mod macros; #[cfg(test)] pub mod test_helpers; @@ -38,6 +37,7 @@ pub mod backend; pub mod connection; #[macro_use] pub mod expression; +pub mod hlist; #[doc(hidden)] pub mod insertable; pub mod query_builder; diff --git a/diesel/src/macros/as_changeset.rs b/diesel/src/macros/as_changeset.rs index d54d625f9f2a..bfb8286db016 100644 --- a/diesel/src/macros/as_changeset.rs +++ b/diesel/src/macros/as_changeset.rs @@ -146,7 +146,7 @@ macro_rules! impl_AsChangeset { treat_none_as_null = $treat_none_as_null, $($headers)* ), - changeset_ty = ($( + changeset_ty = DieselHlist!($( AsChangeset_changeset_ty! { table_name = $table_name, treat_none_as_null = $treat_none_as_null, @@ -225,7 +225,7 @@ macro_rules! impl_AsChangeset { fn as_changeset(self) -> Self::Changeset { use $crate::prelude::ExpressionMethods; let $self_to_columns = *self; - ($( + diesel_hlist!($( AsChangeset_column_expr!( $table_name::$column_name, $column_name, diff --git a/diesel/src/macros/hlist.rs b/diesel/src/macros/hlist.rs new file mode 100644 index 000000000000..f29950546041 --- /dev/null +++ b/diesel/src/macros/hlist.rs @@ -0,0 +1,177 @@ +#[macro_export] +/// Alias for `hlist!`. +/// +/// Since `hlist!` is quite a generic name, we also export it with a more +/// specific name that is unlikely to collide with other crates. The generic +/// names can be disabled by enabling the `conservative-macro-names` feature. +/// +/// If you are writing a crate which adds features to diesel, not an +/// application, you should always use this macro instead of `hlist!` in order +/// to be compatible with applications which disable the more generic names. +macro_rules! diesel_hlist { + // Empty list + () => { $crate::hlist::Nil }; + + // List without trailing commas + ($($item:expr),+) => { diesel_hlist!($($item,)+) }; + + // List with at least one item + ($first:expr, $($rest:expr,)*) => { + $crate::hlist::Cons($first, diesel_hlist!($($rest,)*)) + }; +} + +#[macro_export] +/// Alias for `Hlist!`. +/// +/// Since `Hlist!` is quite a generic name, we also export it with a more +/// specific name that is unlikely to collide with other crates. The generic +/// names can be disabled by enabling the `conservative-macro-names` feature. +/// +/// If you are writing a crate which adds features to diesel, not an +/// application, you should always use this macro instead of `Hlist!` in order +/// to be compatible with applications which disable the more generic names. +macro_rules! DieselHlist { + // Empty list + () => { $crate::hlist::Nil }; + + // List without trailing commas + ($($item:ty),+) => { DieselHlist!($($item,)+) }; + + // List with at least one item + ($first:ty, $($rest:ty,)*) => { + $crate::hlist::Cons<$first, DieselHlist!($($rest,)*)> + }; +} + +#[macro_export] +/// Alias for `hlist_pat!`. +/// +/// Since `hlist_pat!` is quite a generic name, we also export it with a more +/// specific name that is unlikely to collide with other crates. The generic +/// names can be disabled by enabling the `conservative-macro-names` feature. +/// +/// If you are writing a crate which adds features to diesel, not an +/// application, you should always use this macro instead of `hlist_pat!` in order +/// to be compatible with applications which disable the more generic names. +macro_rules! diesel_hlist_pat { + // Empty list + () => { $crate::hlist::Nil }; + + // List without trailing commas + ($($item:pat),+) => { diesel_hlist_pat!($($item,)+) }; + + // List with at least one item + ($first:pat, $($rest:pat,)*) => { + $crate::hlist::Cons($first, diesel_hlist_pat!($($rest,)*)) + }; +} + +#[cfg(not(feature="conservative-macro-names"))] +#[macro_use] +mod hlist_rename { + #[macro_export] + /// Constructs a variable length argument list. + /// + /// A list of arguments to be passed to a function which takes a variable + /// number of arguments, such as + /// [`select`](prelude/trait.SelectDsl.html#tymethod.select) and + /// [`order`](prelude/trait.OrderDsl.html#tymethod.order). + /// + /// Note: This macro has a generic name which may conflict with other + /// crates. You can disable the generic names by enabling the + /// `conservative-macro-names` feature. This macro is also exported as + /// `diesel_hlist!`. + /// + /// # Example + /// + /// ```ignore + /// users.select(hlist!(id, name)) + /// ``` + macro_rules! hlist { + ($($args:tt)*) => { diesel_hlist!($($args)*) } + } + + #[macro_export] + /// The type of a variable length argument list. + /// + /// Similar to `hlist!`, but used in type positions. The type of `hlist!(1, + /// "foo", MyStruct)` will be `Hlist!(i32, &str, MyStruct)`. + /// + /// Note: This macro has a generic name which may conflict with other + /// crates. You can disable the generic names by enabling the + /// `conservative-macro-names` feature. This macro is also exported as + /// `DieselHlist!`. + macro_rules! Hlist { + ($($args:tt)*) => { DieselHlist!($($args)*) } + } + + #[macro_export] + /// A pattern which matches variable length argument lists. + /// + /// Similar to `hlist!`, but used in pattern positions. A value of + /// `hlist!(1, "foo", MyStruct)` will match `hlist_pat!(x, y, z)`. + /// + /// Note: This macro has a generic name which may conflict with other + /// crates. You can disable the generic names by enabling the + /// `conservative-macro-names` feature. This macro is also exported as + /// `diesel_hlist_pat!`. + macro_rules! hlist_pat { + ($($args:tt)*) => { diesel_hlist_pat!($($args)*) } + } +} + +#[cfg(test)] +mod tests { + use hlist::*; + + #[test] + fn empty_hlist() { + let hlist: DieselHlist!() = diesel_hlist!(); + assert_eq!(Nil, hlist); + + // We can't use assert_matches! here because there's only one pattern + // that compiles + match hlist { + diesel_hlist!() => {} // this would fail to compile if the pattern were wrong + } + } + + #[test] + fn one_item_hlist() { + let hlist: DieselHlist!(i32) = diesel_hlist!(1); + assert_eq!(Cons(1, Nil), hlist); + assert_matches!(hlist, diesel_hlist!(1)); + + let hlist: DieselHlist!(i32,) = diesel_hlist!(2,); + assert_eq!(Cons(2, Nil), hlist); + assert_matches!(hlist, diesel_hlist!(2,)); + + let hlist: DieselHlist!(&str) = diesel_hlist!("hello"); + assert_eq!(Cons("hello", Nil), hlist); + assert_matches!(hlist, diesel_hlist!("hello")); + + let hlist: DieselHlist!(&str,) = diesel_hlist!("world",); + assert_eq!(Cons("world", Nil), hlist); + assert_matches!(hlist, diesel_hlist!("world",)); + } + + #[test] + fn multi_item_hlist() { + let hlist: DieselHlist!(i32, i32) = diesel_hlist!(1, 2); + assert_eq!(Cons(1, Cons(2, Nil)), hlist); + assert_matches!(hlist, diesel_hlist!(1, 2)); + + let hlist: DieselHlist!(i32, i32,) = diesel_hlist!(2, 3,); + assert_eq!(Cons(2, Cons(3, Nil)), hlist); + assert_matches!(hlist, diesel_hlist!(2, 3,)); + + let str_hlist: DieselHlist!(&str, &str) = diesel_hlist!("hello", "world"); + assert_eq!(Cons("hello", Cons("world", Nil)), str_hlist); + assert_matches!(str_hlist, diesel_hlist!("hello", "world")); + + let mixed_hlist: DieselHlist!(&str, i32, &str,) = diesel_hlist!("hello", 1, "world",); + assert_eq!(Cons("hello", Cons(1, Cons("world", Nil))), mixed_hlist); + assert_matches!(mixed_hlist, diesel_hlist!("hello", 1, "world",)); + } +} diff --git a/diesel/src/macros/insertable.rs b/diesel/src/macros/insertable.rs index ca2d761d0edc..369b17ba501a 100644 --- a/diesel/src/macros/insertable.rs +++ b/diesel/src/macros/insertable.rs @@ -160,7 +160,7 @@ macro_rules! impl_Insertable { impl<$($lifetime,)* 'insert, DB> $crate::insertable::Insertable<$table_name::table, DB> for &'insert $struct_ty where DB: $crate::backend::Backend, - ($( + DieselHlist!($( $crate::insertable::ColumnInsertValue< $table_name::$column_name, $crate::expression::helper_types::AsNullableExpr< @@ -170,7 +170,7 @@ macro_rules! impl_Insertable { > ,)+): $crate::insertable::InsertValues, { - type Values = ($( + type Values = DieselHlist!($( $crate::insertable::ColumnInsertValue< $table_name::$column_name, $crate::expression::helper_types::AsNullableExpr< @@ -186,7 +186,7 @@ macro_rules! impl_Insertable { use $crate::insertable::ColumnInsertValue; use $crate::types::IntoNullable; let $self_to_columns = *self; - ($( + diesel_hlist!($( Insertable_column_expr!($table_name::$column_name, $column_name, $field_kind) ,)+) } @@ -265,7 +265,7 @@ mod tests { let new_user = NewUser { name: "Sean".into(), hair_color: "Black".into() }; ::insert(&new_user).into(users::table).execute(&conn).unwrap(); - let saved = users::table.select((users::name, users::hair_color)) + let saved = users::table.select(diesel_hlist!(users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; assert_eq!(Ok(expected), saved); @@ -293,7 +293,7 @@ mod tests { let new_user = NewUser { name: "Sean".into(), hair_color: None }; ::insert(&new_user).into(users::table).execute(&conn).unwrap(); - let saved = users::table.select((users::name, users::hair_color)) + let saved = users::table.select(diesel_hlist!(users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); @@ -370,7 +370,7 @@ mod tests { let new_user = NewUser { my_name: "Sean".into(), hair_color: "Black".into() }; ::insert(&new_user).into(users::table).execute(&conn).unwrap(); - let saved = users::table.select((users::name, users::hair_color)) + let saved = users::table.select(diesel_hlist!(users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; assert_eq!(Ok(expected), saved); @@ -397,7 +397,7 @@ mod tests { let new_user = NewUser { my_name: "Sean".into(), my_hair_color: None }; ::insert(&new_user).into(users::table).execute(&conn).unwrap(); - let saved = users::table.select((users::name, users::hair_color)) + let saved = users::table.select(diesel_hlist!(users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); @@ -424,7 +424,7 @@ mod tests { let new_user = NewUser("Sean", None); ::insert(&new_user).into(users::table).execute(&conn).unwrap(); - let saved = users::table.select((users::name, users::hair_color)) + let saved = users::table.select(diesel_hlist!(users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); @@ -451,7 +451,7 @@ mod tests { let new_user = NewUser("Sean", None); ::insert(&new_user).into(users::table).execute(&conn).unwrap(); - let saved = users::table.select((users::name, users::hair_color)) + let saved = users::table.select(diesel_hlist!(users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); diff --git a/diesel/src/macros/mod.rs b/diesel/src/macros/mod.rs index 4dbb0d991cad..fa89a09e8201 100644 --- a/diesel/src/macros/mod.rs +++ b/diesel/src/macros/mod.rs @@ -134,7 +134,7 @@ macro_rules! __diesel_column { /// # use self::followings::dsl::*; /// # // Poor man's assert_eq! -- since this is type level this would fail /// # // to compile if the wrong primary key were generated -/// # let (user_id {}, post_id {}) = followings.primary_key(); +/// # let diesel_hlist_pat!(user_id {}, post_id {}) = followings.primary_key(); /// # } /// ``` /// @@ -253,8 +253,8 @@ macro_rules! table_body { table_body! { schema_name = $schema_name, table_name = $name, - primary_key_ty = ($(columns::$pk,)+), - primary_key_expr = ($(columns::$pk,)+), + primary_key_ty = DieselHlist!($(columns::$pk,)+), + primary_key_expr = diesel_hlist!($(columns::$pk,)+), columns = [$($column_name -> $Type,)+], } }; @@ -285,7 +285,7 @@ macro_rules! table_body { } #[allow(non_upper_case_globals, dead_code)] - pub const all_columns: ($($column_name,)+) = ($($column_name,)+); + pub const all_columns: DieselHlist!($($column_name,)+) = diesel_hlist!($($column_name,)+); #[allow(non_camel_case_types, missing_debug_implementations)] #[derive(Debug, Clone, Copy)] @@ -298,7 +298,7 @@ macro_rules! table_body { } } - pub type SqlType = ($($column_ty,)+); + pub type SqlType = DieselHlist!($($column_ty,)+); pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, table, DB>; @@ -306,7 +306,7 @@ macro_rules! table_body { impl AsQuery for table { type SqlType = SqlType; - type Query = SelectStatement<($($column_name,)+), Self>; + type Query = SelectStatement; fn as_query(self) -> Self::Query { SelectStatement::simple(all_columns, self) @@ -315,14 +315,14 @@ macro_rules! table_body { impl Table for table { type PrimaryKey = $primary_key_ty; - type AllColumns = ($($column_name,)+); + type AllColumns = DieselHlist!($($column_name,)+); fn primary_key(&self) -> Self::PrimaryKey { $primary_key_expr } fn all_columns() -> Self::AllColumns { - ($($column_name,)+) + diesel_hlist!($($column_name,)+) } } @@ -609,6 +609,7 @@ macro_rules! print_sql { // The order of these modules is important (at least for those which have tests). // Utililty macros which don't call any others need to come first. +#[macro_use] mod hlist; #[macro_use] mod parse; #[macro_use] mod query_id; #[macro_use] mod static_cond; diff --git a/diesel/src/query_builder/mod.rs b/diesel/src/query_builder/mod.rs index 5fbf95e590d2..73f1fb728ecc 100644 --- a/diesel/src/query_builder/mod.rs +++ b/diesel/src/query_builder/mod.rs @@ -42,11 +42,12 @@ pub use self::insert_statement::IncompleteInsertStatement; use std::error::Error; use backend::Backend; +use hlist::*; use result::QueryResult; #[doc(hidden)] pub type Binds = Vec>>; -pub type BuildQueryResult = Result<(), Box>; +pub type BuildQueryResult = Result>; /// Apps should not need to concern themselves with this trait. /// @@ -131,6 +132,46 @@ impl QueryFragment for () { } } +impl QueryFragment for Cons> where + DB: Backend, + Head: QueryFragment, + Cons: QueryFragment, +{ + fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { + try!(self.0.to_sql(out)); + out.push_sql(", "); + try!(self.1.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)); + Ok(()) + } + + fn is_safe_to_cache_prepared(&self) -> bool { + self.0.is_safe_to_cache_prepared() && self.1.is_safe_to_cache_prepared() + } +} + +impl QueryFragment for Cons where + DB: Backend, + Head: QueryFragment, +{ + fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { + self.0.to_sql(out) + } + + fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> { + self.0.collect_binds(out) + } + + fn is_safe_to_cache_prepared(&self) -> bool { + self.0.is_safe_to_cache_prepared() + } +} + /// 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 03041bcd052c..69e859e35a0e 100644 --- a/diesel/src/query_builder/query_id.rs +++ b/diesel/src/query_builder/query_id.rs @@ -1,3 +1,4 @@ +use hlist::*; use std::any::{Any, TypeId}; use super::QueryFragment; @@ -47,6 +48,9 @@ impl QueryId for QueryFragment { } } +impl_query_id!(Cons); +impl_query_id!(Nil); + #[cfg(test)] mod tests { use std::any::TypeId; diff --git a/diesel/src/query_builder/update_statement/changeset.rs b/diesel/src/query_builder/update_statement/changeset.rs index 5f5911b5a9b8..98c501ea9038 100644 --- a/diesel/src/query_builder/update_statement/changeset.rs +++ b/diesel/src/query_builder/update_statement/changeset.rs @@ -1,4 +1,5 @@ use backend::Backend; +use hlist::*; use query_builder::BuildQueryResult; use query_source::QuerySource; use result::QueryResult; @@ -60,3 +61,76 @@ impl, DB: Backend> Changeset for Option { } } } + +impl AsChangeset for Cons> where + Head: AsChangeset, + Cons: AsChangeset, +{ + type Target = Head::Target; + type Changeset = Cons< + Head::Changeset, + as AsChangeset>::Changeset, + >; + + fn as_changeset(self) -> Self::Changeset { + Cons( + self.0.as_changeset(), + self.1.as_changeset(), + ) + } +} + +impl AsChangeset for Cons where + Head: AsChangeset, +{ + type Target = Head::Target; + type Changeset = Cons; + + fn as_changeset(self) -> Self::Changeset { + Cons( + self.0.as_changeset(), + Nil, + ) + } +} + +impl Changeset for Cons where + DB: Backend, + Head: Changeset, + Tail: Changeset, +{ + fn is_noop(&self) -> bool { + self.0.is_noop() && self.1.is_noop() + } + + 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() { + out.push_sql(", "); + } + } + self.1.to_sql(out) + } + + fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> { + try!(self.0.collect_binds(out)); + self.1.collect_binds(out) + } +} + +impl Changeset for Nil { + fn is_noop(&self) -> bool { + true + } + + fn to_sql(&self, _: &mut DB::QueryBuilder) -> BuildQueryResult { + Ok(()) + } + + fn collect_binds(&self, _: &mut DB::BindCollector) -> QueryResult<()> { + Ok(()) + } +} diff --git a/diesel/src/query_dsl/with_dsl.rs b/diesel/src/query_dsl/with_dsl.rs index 7d090b2b2188..299520ac1caa 100644 --- a/diesel/src/query_dsl/with_dsl.rs +++ b/diesel/src/query_dsl/with_dsl.rs @@ -47,10 +47,10 @@ impl<'a, Left, Right> QuerySource for WithQuerySource<'a, Left, Right> where Left: QuerySource, Aliased<'a, Right>: QuerySource + Expression, { - type FromClause = PgOnly<(Left::FromClause, as QuerySource>::FromClause)>; + type FromClause = PgOnly as QuerySource>::FromClause)>; fn from_clause(&self) -> Self::FromClause { - PgOnly((self.left.from_clause(), self.right.from_clause())) + PgOnly(diesel_hlist!(self.left.from_clause(), self.right.from_clause())) } } diff --git a/diesel/src/query_source/joins.rs b/diesel/src/query_source/joins.rs index fccf96af779f..d501498c13b7 100644 --- a/diesel/src/query_source/joins.rs +++ b/diesel/src/query_source/joins.rs @@ -36,19 +36,19 @@ impl QuerySource for InnerJoinSource where impl AsQuery for InnerJoinSource where Left: Table + JoinTo, Right: Table, - (Left::AllColumns, Right::AllColumns): SelectableExpression< + DieselHlist!(Left::AllColumns, Right::AllColumns): SelectableExpression< InnerJoinSource, - SqlTypeForSelect=(Left::SqlType, Right::SqlType), + SqlTypeForSelect=DieselHlist!(Left::SqlType, Right::SqlType), >, { - type SqlType = (Left::SqlType, Right::SqlType); + type SqlType = DieselHlist!(Left::SqlType, Right::SqlType); type Query = SelectStatement< - (Left::AllColumns, Right::AllColumns), + DieselHlist!(Left::AllColumns, Right::AllColumns), Self, >; fn as_query(self) -> Self::Query { - SelectStatement::simple((Left::all_columns(), Right::all_columns()), self) + SelectStatement::simple(diesel_hlist!(Left::all_columns(), Right::all_columns()), self) } } @@ -85,20 +85,20 @@ impl AsQuery for LeftOuterJoinSource where Left: Table + JoinTo, Right: Table, Right::SqlType: IntoNullable, - (Left::AllColumns, Nullable): SelectableExpression< + DieselHlist!(Left::AllColumns, Nullable): SelectableExpression< LeftOuterJoinSource, - SqlTypeForSelect=(Left::SqlType, ::Nullable) + SqlTypeForSelect=DieselHlist!(Left::SqlType, ::Nullable) >, { - type SqlType = (Left::SqlType, ::Nullable); + type SqlType = DieselHlist!(Left::SqlType, ::Nullable); type Query = SelectStatement< - (Left::AllColumns, Nullable), + DieselHlist!(Left::AllColumns, Nullable), Self, >; fn as_query(self) -> Self::Query { SelectStatement::simple( - (Left::all_columns(), Right::all_columns().nullable()), + diesel_hlist!(Left::all_columns(), Right::all_columns().nullable()), self, ) } diff --git a/diesel/src/types/impls/hlist.rs b/diesel/src/types/impls/hlist.rs new file mode 100644 index 000000000000..c5ade544bedd --- /dev/null +++ b/diesel/src/types/impls/hlist.rs @@ -0,0 +1,91 @@ +use backend::Backend; +use hlist::*; +use query_builder::*; +use query_source::Queryable; +use row::Row; +use types::{HasSqlType, FromSqlRow, Nullable, NotNull}; + +impl HasSqlType> for DB where + DB: Backend + HasSqlType + HasSqlType, +{ + fn metadata() -> DB::TypeMetadata { + unreachable!("hlists don't implement `ToSql` directly"); + } + + fn row_metadata(out: &mut Vec) { + >::row_metadata(out); + >::row_metadata(out); + } +} + +impl HasSqlType for DB { + fn metadata() -> DB::TypeMetadata { + unreachable!("hlists don't implement `ToSql` directly"); + } + + fn row_metadata(_: &mut Vec) { + // noop + } +} + +impl NotNull for Cons { +} + +impl FromSqlRow, DB> + for Cons where + Head: FromSqlRow, + Tail: FromSqlRow, + DB: Backend + HasSqlType + HasSqlType, +{ + fn build_from_row>(row: &mut R) -> BuildQueryResult { + Ok(Cons( + try!(Head::build_from_row(row)), + try!(Tail::build_from_row(row)), + )) + } +} + +impl FromSqlRow for Nil { + fn build_from_row>(_: &mut R) -> BuildQueryResult { + Ok(Nil) + } +} + +impl FromSqlRow, DB> + for Option> where + DB: Backend + HasSqlType, + Cons: FromSqlRow + Hlist, + ST: NotNull, +{ + fn build_from_row>(row: &mut R) -> BuildQueryResult { + if row.next_is_null(Cons::::len()) { + Ok(None) + } else { + Cons::::build_from_row(row).map(Some) + } + } +} + +impl Queryable, DB> + for Cons where + DB: Backend + HasSqlType + HasSqlType, + Head: Queryable, + Tail: Queryable, +{ + type Row = Cons; + + fn build(Cons(head, tail): Self::Row) -> Self { + Cons( + Head::build(head), + Tail::build(tail), + ) + } +} + +impl Queryable for Nil { + type Row = Nil; + + fn build(_: Self::Row) -> Self { + Nil + } +} diff --git a/diesel/src/types/impls/mod.rs b/diesel/src/types/impls/mod.rs index 19d1d28517db..303e9b10e155 100644 --- a/diesel/src/types/impls/mod.rs +++ b/diesel/src/types/impls/mod.rs @@ -182,6 +182,7 @@ macro_rules! debug_to_sql { mod date_and_time; pub mod floats; +mod hlist; mod integers; pub mod option; mod primitives; diff --git a/diesel/src/types/impls/tuples.rs b/diesel/src/types/impls/tuples.rs index c40b7327668b..da369bd57c24 100644 --- a/diesel/src/types/impls/tuples.rs +++ b/diesel/src/types/impls/tuples.rs @@ -1,1773 +1,49 @@ use associations::BelongsTo; -use backend::{Backend, SupportsDefaultKeyword}; -use expression::{Expression, SelectableExpression, NonAggregate}; -use insertable::{ColumnInsertValue, InsertValues}; -use query_builder::*; -use query_source::{QuerySource, Queryable, Table, Column}; -use result::QueryResult; -use row::Row; -use std::error::Error; -use types::{HasSqlType, FromSqlRow, Nullable, IntoNullable, NotNull}; +use backend::Backend; +use query_source::Queryable; +use types::HasSqlType; macro_rules! tuple_impls { - ($( - $Tuple:tt { - $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+ - } - )+) => { - $( - impl<$($T),+, DB> HasSqlType<($($T,)+)> for DB where - $(DB: HasSqlType<$T>),+, - DB: Backend, - { - fn metadata() -> DB::TypeMetadata { - unreachable!("Tuples should never implement `ToSql` directly"); - } - - fn row_metadata(out: &mut Vec) { - $(>::row_metadata(out);)+ - } - } - - impl<$($T),+> NotNull for ($($T,)+) { - } - - impl<$($T),+, $($ST),+, DB> FromSqlRow<($($ST,)+), DB> for ($($T,)+) where - DB: Backend, - $($T: FromSqlRow<$ST, DB>),+, - $(DB: HasSqlType<$ST>),+, - DB: HasSqlType<($($ST,)+)>, - { - fn build_from_row>(row: &mut RowT) -> Result> { - Ok(($(try!($T::build_from_row(row)),)+)) - } - } - - impl<$($T),+, $($ST),+, DB> FromSqlRow, DB> for Option<($($T,)+)> where - DB: Backend, - $($T: FromSqlRow<$ST, DB>),+, - $(DB: HasSqlType<$ST>),+, - DB: HasSqlType<($($ST,)+)>, - { - fn build_from_row>(row: &mut RowT) -> Result> { - if row.next_is_null($Tuple) { - Ok(None) - } else { - Ok(Some(($(try!($T::build_from_row(row)),)+))) - } - } - } - - impl<$($T),+, $($ST),+, DB> Queryable<($($ST,)+), DB> for ($($T,)+) where - DB: Backend, - $($T: Queryable<$ST, DB>),+, - $(DB: HasSqlType<$ST>),+, - DB: HasSqlType<($($ST,)+)>, - { - type Row = ($($T::Row,)+); - - fn build(row: Self::Row) -> Self { - ($($T::build(row.$idx),)+) - } - } + ($($T:ident,)+) => { + impl<$($T,)+ ST, DB> Queryable for ($($T,)+) where + DB: Backend + HasSqlType, + DieselHlist!($($T,)+): Queryable, + { + type Row = >::Row; - impl<$($T: Expression + NonAggregate),+> Expression for ($($T,)+) { - type SqlType = ($(<$T as Expression>::SqlType,)+); + #[allow(non_snake_case)] + fn build(row: Self::Row) -> Self { + let diesel_hlist_pat!($($T,)+) = Queryable::build(row); + ($($T,)+) } + } - #[cfg_attr(feature = "clippy", allow(eq_op))] // Clippy doesn't like the trivial case for 1-tuples - impl<$($T: QueryFragment),+, DB: Backend> QueryFragment for ($($T,)+) { - fn to_sql(&self, out: &mut DB::QueryBuilder) - -> BuildQueryResult { - $( - if $idx != 0 { - out.push_sql(", "); - } - try!(self.$idx.to_sql(out)); - )+ - Ok(()) - } + impl<$($T,)+ Parent> BelongsTo for ($($T,)+) where + A: BelongsTo, + { + type ForeignKey = A::ForeignKey; + type ForeignKeyColumn = A::ForeignKeyColumn; - fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> { - $( - try!(self.$idx.collect_binds(out)); - )+ - Ok(()) - } - - fn is_safe_to_cache_prepared(&self) -> bool { - $(self.$idx.is_safe_to_cache_prepared() &&)+ true - } - } - - impl<$($T: QueryId),+> QueryId for ($($T,)+) { - type QueryId = ($($T::QueryId,)+); - - fn has_static_query_id() -> bool { - $($T::has_static_query_id() &&)+ true - } - } - - impl<$($T: Expression + NonAggregate),+> NonAggregate for ($($T,)+) { + fn foreign_key(&self) -> Option<&Self::ForeignKey> { + self.0.foreign_key() } - #[cfg_attr(feature = "clippy", allow(eq_op))] // Clippy doesn't like the trivial case for 1-tuples - impl<$($T,)+ $($ST,)+ Tab, DB> InsertValues - for ($(ColumnInsertValue<$T, $ST>,)+) where - DB: Backend + SupportsDefaultKeyword, - Tab: Table, - $($T: Column,)+ - $($T::SqlType: IntoNullable,)+ - $($ST: Expression::Nullable> + QueryFragment,)+ - { - fn column_names(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { - $( - if $idx != 0 { - out.push_sql(", "); - } - try!(out.push_identifier($T::name())); - )+ - Ok(()) - } - - fn values_clause(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { - out.push_sql("("); - $( - if $idx != 0 { - out.push_sql(", "); - } - match self.$idx { - ColumnInsertValue::Expression(_, ref value) => { - try!(value.to_sql(out)); - } - _ => out.push_sql("DEFAULT"), - } - )+ - out.push_sql(")"); - Ok(()) - } - - fn values_bind_params(&self, out: &mut DB::BindCollector) -> QueryResult<()> { - $( - if let ColumnInsertValue::Expression(_, ref value) = self.$idx { - try!(value.collect_binds(out)); - } - )+ - Ok(()) - } - } - - #[cfg(feature = "sqlite")] - impl<$($T,)+ $($ST,)+ Tab> InsertValues<::sqlite::Sqlite> - for ($(ColumnInsertValue<$T, $ST>,)+) where - Tab: Table, - $($T: Column,)+ - $($T::SqlType: IntoNullable,)+ - $($ST: Expression::Nullable> + QueryFragment<::sqlite::Sqlite>,)+ - { - #[allow(unused_assignments)] - fn column_names(&self, out: &mut ::sqlite::SqliteQueryBuilder) -> BuildQueryResult { - let mut columns_present = false; - $( - if let ColumnInsertValue::Expression(..) = self.$idx { - if columns_present { - out.push_sql(", "); - } - try!(out.push_identifier($T::name())); - columns_present = true; - } - )+ - Ok(()) - } - - #[allow(unused_assignments)] - fn values_clause(&self, out: &mut ::sqlite::SqliteQueryBuilder) -> BuildQueryResult { - out.push_sql("("); - let mut columns_present = false; - $( - if let ColumnInsertValue::Expression(_, ref value) = self.$idx { - if columns_present { - out.push_sql(", "); - } - try!(value.to_sql(out)); - columns_present = true; - } - )+ - out.push_sql(")"); - Ok(()) - } - - fn values_bind_params( - &self, - out: &mut <::sqlite::Sqlite as Backend>::BindCollector - ) -> QueryResult<()> { - $( - if let ColumnInsertValue::Expression(_, ref value) = self.$idx { - try!(value.collect_binds(out)); - } - )+ - Ok(()) - } + fn foreign_key_column() -> Self::ForeignKeyColumn { + A::foreign_key_column() } - - impl<$($T,)+ QS> SelectableExpression for ($($T,)+) where - $($T: SelectableExpression,)+ - ($($T,)+): Expression, - { - type SqlTypeForSelect = ($($T::SqlTypeForSelect,)+); - } - - impl AsChangeset for ($($T,)+) where - $($T: AsChangeset,)+ - Target: QuerySource, - { - type Target = Target; - type Changeset = ($($T::Changeset,)+); - - fn as_changeset(self) -> Self::Changeset { - ($(self.$idx.as_changeset(),)+) - } - } - - impl Changeset for ($($T,)+) where - DB: Backend, - $($T: Changeset,)+ - { - fn is_noop(&self) -> bool { - $(self.$idx.is_noop() &&)+ true - } - - #[allow(unused_assignments)] - fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult { - let mut needs_comma = false; - $( - let noop_element = self.$idx.is_noop(); - if !noop_element { - if needs_comma { - out.push_sql(", "); - } - try!(self.$idx.to_sql(out)); - needs_comma = true; - } - )+ - Ok(()) - } - - fn collect_binds(&self, out: &mut DB::BindCollector) -> QueryResult<()> { - $( - try!(self.$idx.collect_binds(out)); - )+ - Ok(()) - } - } - - impl<$($T,)+ Parent> BelongsTo for ($($T,)+) where - A: BelongsTo, - { - type ForeignKey = A::ForeignKey; - type ForeignKeyColumn = A::ForeignKeyColumn; - - fn foreign_key(&self) -> Option<&Self::ForeignKey> { - self.0.foreign_key() - } - - fn foreign_key_column() -> Self::ForeignKeyColumn { - A::foreign_key_column() - } - } - )+ - } -} - -tuple_impls! { - 1 { - (0) -> A, SA, TA, - } - 2 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - } - 3 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - } - 4 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - } - 5 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - } - 6 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - } - 7 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - } - 8 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - } - 9 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - } - 10 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - } - 11 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - } - 12 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - } - 13 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - } - 14 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - } - 15 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - } - 16 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - } -} - -#[cfg(feature = "large-tables")] -tuple_impls! { - 17 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - } - 18 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - } - 19 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - } - 20 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - } - 21 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - } - 22 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - } - 23 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - } - 24 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - } - 25 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - } - 26 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, + } } } -#[cfg(feature = "huge-tables")] -tuple_impls! { - 27 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - } - 28 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - } - 29 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - } - 30 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - } - 31 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - } - 32 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - } - 33 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - } - 34 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - } - 35 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - } - 36 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - } - 37 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - } - 38 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - } - 39 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - } - 40 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - } - 41 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - } - 42 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - } - 43 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - } - 44 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - (43) -> AR, SAR, TAR, - } - 45 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - (43) -> AR, SAR, TAR, - (44) -> AS, SAS, TAS, - } - 46 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - (43) -> AR, SAR, TAR, - (44) -> AS, SAS, TAS, - (45) -> AT, SAT, TAT, - } - 47 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - (43) -> AR, SAR, TAR, - (44) -> AS, SAS, TAS, - (45) -> AT, SAT, TAT, - (46) -> AU, SAU, TAU, - } - 48 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - (43) -> AR, SAR, TAR, - (44) -> AS, SAS, TAS, - (45) -> AT, SAT, TAT, - (46) -> AU, SAU, TAU, - (47) -> AV, SAV, TAV, - } - 49 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - (43) -> AR, SAR, TAR, - (44) -> AS, SAS, TAS, - (45) -> AT, SAT, TAT, - (46) -> AU, SAU, TAU, - (47) -> AV, SAV, TAV, - (48) -> AW, SAW, TAW, - } - 50 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - (43) -> AR, SAR, TAR, - (44) -> AS, SAS, TAS, - (45) -> AT, SAT, TAT, - (46) -> AU, SAU, TAU, - (47) -> AV, SAV, TAV, - (48) -> AW, SAW, TAW, - (49) -> AX, SAX, TAX, - } - 51 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - (43) -> AR, SAR, TAR, - (44) -> AS, SAS, TAS, - (45) -> AT, SAT, TAT, - (46) -> AU, SAU, TAU, - (47) -> AV, SAV, TAV, - (48) -> AW, SAW, TAW, - (49) -> AX, SAX, TAX, - (50) -> AY, SAY, TAY, - } - 52 { - (0) -> A, SA, TA, - (1) -> B, SB, TB, - (2) -> C, SC, TC, - (3) -> D, SD, TD, - (4) -> E, SE, TE, - (5) -> F, SF, TF, - (6) -> G, SG, TG, - (7) -> H, SH, TH, - (8) -> I, SI, TI, - (9) -> J, SJ, TJ, - (10) -> K, SK, TK, - (11) -> L, SL, TL, - (12) -> M, SM, TM, - (13) -> N, SN, TN, - (14) -> O, SO, TO, - (15) -> P, SP, TP, - (16) -> Q, SQ, TQ, - (17) -> R, SR, TR, - (18) -> S, SS, TS, - (19) -> T, ST, TT, - (20) -> U, SU, TU, - (21) -> V, SV, TV, - (22) -> W, SW, TW, - (23) -> X, SX, TX, - (24) -> Y, SY, TY, - (25) -> Z, SZ, TZ, - (26) -> AA, SAA, TAA, - (27) -> AB, SAB, TAB, - (28) -> AC, SAC, TAC, - (29) -> AD, SAD, TAD, - (30) -> AE, SAE, TAE, - (31) -> AF, SAF, TAF, - (32) -> AG, SAG, TAG, - (33) -> AH, SAH, TAH, - (34) -> AI, SAI, TAI, - (35) -> AJ, SAJ, TAJ, - (36) -> AK, SAK, TAK, - (37) -> AL, SAL, TAL, - (38) -> AM, SAM, TAM, - (39) -> AN, SAN, TAN, - (40) -> AO, SAO, TAO, - (41) -> AP, SAP, TAP, - (42) -> AQ, SAQ, TAQ, - (43) -> AR, SAR, TAR, - (44) -> AS, SAS, TAS, - (45) -> AT, SAT, TAT, - (46) -> AU, SAU, TAU, - (47) -> AV, SAV, TAV, - (48) -> AW, SAW, TAW, - (49) -> AX, SAX, TAX, - (50) -> AY, SAY, TAY, - (51) -> AZ, SAZ, TAZ, - } -} +tuple_impls!(A,); +tuple_impls!(A, B,); +tuple_impls!(A, B, C,); +tuple_impls!(A, B, C, D,); +tuple_impls!(A, B, C, D, E,); +tuple_impls!(A, B, C, D, E, F,); +tuple_impls!(A, B, C, D, E, F, G,); +tuple_impls!(A, B, C, D, E, F, G, H,); +tuple_impls!(A, B, C, D, E, F, G, H, I,); +tuple_impls!(A, B, C, D, E, F, G, H, I, J,); +tuple_impls!(A, B, C, D, E, F, G, H, I, J, K,); +tuple_impls!(A, B, C, D, E, F, G, H, I, J, K, L,); diff --git a/diesel_codegen/src/queryable.rs b/diesel_codegen/src/queryable.rs index b05816e06f8d..8682c25f97cf 100644 --- a/diesel_codegen/src/queryable.rs +++ b/diesel_codegen/src/queryable.rs @@ -15,11 +15,11 @@ pub fn derive_queryable(item: syn::MacroInput) -> Tokens { let struct_ty = &model.ty; let row_ty = model.attrs.as_slice().iter().map(|a| &a.ty); - let row_ty = quote!((#(#row_ty,)*)); + let row_ty = quote!(DieselHlist!(#(#row_ty,)*)); let build_expr = build_expr_for_model(&model); let field_names = model.attrs.as_slice().iter().map(Attr::name_for_pattern); - let row_pat = quote!((#(#field_names,)*)); + let row_pat = quote!(diesel_hlist_pat!(#(#field_names,)*)); let model_name_uppercase = model.name.as_ref().to_uppercase(); let dummy_const = format!("_IMPL_QUERYABLE_FOR_{}", model_name_uppercase).into(); diff --git a/diesel_codegen/tests/queryable.rs b/diesel_codegen/tests/queryable.rs index 4882ca7a4b80..69c2028966e9 100644 --- a/diesel_codegen/tests/queryable.rs +++ b/diesel_codegen/tests/queryable.rs @@ -13,7 +13,7 @@ fn named_struct_definition() { } let conn = connection(); - let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); + let data = select(sql::("1, 2")).get_result(&conn); assert_eq!(Ok(MyStruct { foo: 1, bar: 2 }), data); } @@ -23,7 +23,7 @@ fn tuple_struct() { struct MyStruct(#[column_name(foo)] i32, #[column_name(bar)] i32); let conn = connection(); - let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); + let data = select(sql::("1, 2")).get_result(&conn); assert_eq!(Ok(MyStruct(1, 2)), data); } @@ -33,6 +33,6 @@ fn tuple_struct_without_column_name_annotations() { struct MyStruct(i32, i32); let conn = connection(); - let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); + let data = select(sql::("1, 2")).get_result(&conn); assert_eq!(Ok(MyStruct(1, 2)), data); } diff --git a/diesel_compile_tests/Cargo.toml b/diesel_compile_tests/Cargo.toml index aede95040cbc..c2ce00f26258 100644 --- a/diesel_compile_tests/Cargo.toml +++ b/diesel_compile_tests/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Sean Griffin "] [workspace] [dependencies] -diesel = { version = "0.11.0", features = ["extras", "sqlite", "postgres", "mysql"] } +diesel = { version = "0.11.0", features = ["extras", "sqlite", "postgres", "mysql", "conservative-macro-names"] } diesel_codegen = { version = "0.11.0" } compiletest_rs = "0.2.3" diff --git a/diesel_compile_tests/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs b/diesel_compile_tests/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs index 6db1281cce4e..ef734141ecec 100644 --- a/diesel_compile_tests/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs +++ b/diesel_compile_tests/tests/compile-fail/cannot_mix_aggregate_and_non_aggregate_selects.rs @@ -13,6 +13,6 @@ table! { fn main() { use self::users::dsl::*; - let source = users.select((id, count(users.star()))); + let source = users.select(diesel_hlist!(id, count(users.star()))); //~^ ERROR E0277 } diff --git a/diesel_compile_tests/tests/compile-fail/custom_returning_requires_nonaggregate.rs b/diesel_compile_tests/tests/compile-fail/custom_returning_requires_nonaggregate.rs index 5446d57302eb..db5f3603b3e1 100644 --- a/diesel_compile_tests/tests/compile-fail/custom_returning_requires_nonaggregate.rs +++ b/diesel_compile_tests/tests/compile-fail/custom_returning_requires_nonaggregate.rs @@ -28,6 +28,6 @@ fn main() { let new_user = NewUser { name: "Foobar".to_string(), }; - let stmt = insert(&new_user).into(users).returning((name, count(name))); + let stmt = insert(&new_user).into(users).returning(diesel_hlist!(name, count(name))); //~^ ERROR NonAggregate } diff --git a/diesel_compile_tests/tests/compile-fail/custom_returning_requires_selectable_expression.rs b/diesel_compile_tests/tests/compile-fail/custom_returning_requires_selectable_expression.rs index 2ea7fdc81536..ce39253d1a0b 100644 --- a/diesel_compile_tests/tests/compile-fail/custom_returning_requires_selectable_expression.rs +++ b/diesel_compile_tests/tests/compile-fail/custom_returning_requires_selectable_expression.rs @@ -34,6 +34,6 @@ fn main() { let new_user = NewUser { name: "Foobar".to_string(), }; - let stmt = insert(&new_user).into(users).returning((name, bad::age)); + let stmt = insert(&new_user).into(users).returning(diesel_hlist!(name, bad::age)); //~^ ERROR SelectableExpression } diff --git a/diesel_compile_tests/tests/compile-fail/insert_statement_does_not_support_returning_methods_on_sqlite.rs b/diesel_compile_tests/tests/compile-fail/insert_statement_does_not_support_returning_methods_on_sqlite.rs index 3bbe7202e28e..2e33501e6b42 100644 --- a/diesel_compile_tests/tests/compile-fail/insert_statement_does_not_support_returning_methods_on_sqlite.rs +++ b/diesel_compile_tests/tests/compile-fail/insert_statement_does_not_support_returning_methods_on_sqlite.rs @@ -1,5 +1,5 @@ -#[macro_use] -extern crate diesel; +#[macro_use] extern crate diesel; +#[macro_use] extern crate diesel_codegen; use diesel::*; use diesel::sqlite::{Sqlite, SqliteQueryBuilder, SqliteConnection}; @@ -13,32 +13,15 @@ table! { } } +#[derive(Queryable)] pub struct User { id: i32, name: String, } -use diesel::types::FromSqlRow; - -impl Queryable<(Integer, VarChar), DB> for User where - (i32, String): FromSqlRow<(Integer, VarChar), DB>, -{ - type Row = (i32, String); - - fn build(row: Self::Row) -> Self { - User { - id: row.0, - name: row.1, - } - } -} - -pub struct NewUser(String); - -impl_Insertable! { - (users) - pub struct NewUser(#[column_name(name)] String,); -} +#[derive(Insertable)] +#[table_name="users"] +pub struct NewUser(#[column_name(name)] String); fn main() { let connection = SqliteConnection::establish(":memory:").unwrap(); diff --git a/diesel_compile_tests/tests/compile-fail/selecting_multiple_columns_requires_all_must_be_from_selectable_table.rs b/diesel_compile_tests/tests/compile-fail/selecting_multiple_columns_requires_all_must_be_from_selectable_table.rs index faa919cf3446..7df809a5358b 100644 --- a/diesel_compile_tests/tests/compile-fail/selecting_multiple_columns_requires_all_must_be_from_selectable_table.rs +++ b/diesel_compile_tests/tests/compile-fail/selecting_multiple_columns_requires_all_must_be_from_selectable_table.rs @@ -24,4 +24,5 @@ fn main() { //~| ERROR E0277 let stuff = users::table.select((posts::id, users::name)); //~^ ERROR Selectable + //~| ERROR E0277 } diff --git a/diesel_infer_schema/src/data_structures.rs b/diesel_infer_schema/src/data_structures.rs index b82ba099678c..e8bc38537535 100644 --- a/diesel_infer_schema/src/data_structures.rs +++ b/diesel_infer_schema/src/data_structures.rs @@ -37,23 +37,23 @@ impl ColumnInformation { #[cfg(feature="uses_information_schema")] impl Queryable for ColumnInformation where DB: Backend + UsesInformationSchema + HasSqlType, - (String, String, String): FromSqlRow, + DieselHlist!(String, String, String): FromSqlRow, { - type Row = (String, String, String); + type Row = DieselHlist!(String, String, String); - fn build(row: Self::Row) -> Self { - ColumnInformation::new(row.0, row.1, row.2 == "YES") + fn build(diesel_hlist_pat!(col, ty, is_nullable): Self::Row) -> Self { + ColumnInformation::new(col, ty, is_nullable == "YES") } } #[cfg(feature = "sqlite")] impl Queryable for ColumnInformation where Sqlite: HasSqlType, - (i32, String, String, bool, Option, bool): FromSqlRow, + DieselHlist!(i32, String, String, bool, Option, bool): FromSqlRow, { - type Row = (i32, String, String, bool, Option, bool); + type Row = DieselHlist!(i32, String, String, bool, Option, bool); - fn build(row: Self::Row) -> Self { - ColumnInformation::new(row.1, row.2, !row.3) + fn build(diesel_hlist_pat!(_, col, ty, not_null, _, _): Self::Row) -> Self { + ColumnInformation::new(col, ty, !not_null) } } diff --git a/diesel_infer_schema/src/information_schema.rs b/diesel_infer_schema/src/information_schema.rs index d9b6f29ba5b1..e3cf02f23209 100644 --- a/diesel_infer_schema/src/information_schema.rs +++ b/diesel_infer_schema/src/information_schema.rs @@ -111,7 +111,7 @@ pub fn get_table_data(conn: &Conn, table: &TableData) }; let type_column = Conn::Backend::type_column(); - columns.select((column_name, type_column, is_nullable)) + columns.select(hlist!(column_name, type_column, is_nullable)) .filter(table_name.eq(&table.name)) .filter(table_schema.eq(schema_name)) .order(ordinal_position) @@ -156,7 +156,7 @@ pub fn load_table_names(connection: &Conn, schema_name: Option<&str>) None => Conn::Backend::default_schema(connection)?, }; - tables.select((table_name, table_schema)) + tables.select(hlist!(table_name, table_schema)) .filter(table_schema.eq(schema_name)) .filter(table_name.not_like("\\_\\_%")) .filter(table_type.like("BASE TABLE")) diff --git a/diesel_infer_schema/src/sqlite.rs b/diesel_infer_schema/src/sqlite.rs index 8a89c6ee93a8..f22236d3635b 100644 --- a/diesel_infer_schema/src/sqlite.rs +++ b/diesel_infer_schema/src/sqlite.rs @@ -53,25 +53,17 @@ pub fn get_table_data(conn: &SqliteConnection, table: &TableData) } struct FullTableInfo { - _cid: i32, name: String, - _type_name: String, - _not_null: bool, - _dflt_value: Option, primary_key: bool, } impl Queryable for FullTableInfo { - type Row = (i32, String, String, bool, Option, bool); - - fn build(row: Self::Row) -> Self { - FullTableInfo{ - _cid: row.0, - name: row.1, - _type_name: row.2, - _not_null: row.3, - _dflt_value: row.4, - primary_key: row.5, + type Row = DieselHlist!(i32, String, String, bool, Option, bool); + + fn build(diesel_hlist_pat!(_, name, _, _, _, primary_key): Self::Row) -> Self { + FullTableInfo { + name: name, + primary_key: primary_key, } } } diff --git a/diesel_infer_schema/src/table_data.rs b/diesel_infer_schema/src/table_data.rs index 9603dd4e43db..9e66e12d3146 100644 --- a/diesel_infer_schema/src/table_data.rs +++ b/diesel_infer_schema/src/table_data.rs @@ -31,11 +31,11 @@ impl TableData { impl Queryable for TableData where DB: Backend + HasSqlType, - (String, String): FromSqlRow, + DieselHlist!(String, String): FromSqlRow, { - type Row = (String, String); + type Row = DieselHlist!(String, String); - fn build((name, schema): Self::Row) -> Self { + fn build(diesel_hlist_pat!(name, schema): Self::Row) -> Self { TableData::new(name, schema) } } diff --git a/diesel_tests/tests/deserialization.rs b/diesel_tests/tests/deserialization.rs index 8b2c488480ba..955e2aa8a43b 100644 --- a/diesel_tests/tests/deserialization.rs +++ b/diesel_tests/tests/deserialization.rs @@ -17,5 +17,5 @@ fn generated_queryable_allows_lifetimes() { id: 1, name: Cow::Owned("Sean".to_string()), }; - assert_eq!(Ok(expected_user), users.select((id, name)).first(&connection)); + assert_eq!(Ok(expected_user), users.select(hlist!(id, name)).first(&connection)); } diff --git a/diesel_tests/tests/expressions/date_and_time.rs b/diesel_tests/tests/expressions/date_and_time.rs index 0ebe44163128..90b268e2b2b0 100644 --- a/diesel_tests/tests/expressions/date_and_time.rs +++ b/diesel_tests/tests/expressions/date_and_time.rs @@ -127,7 +127,7 @@ fn interval_is_deserialized_properly() { let connection = connection(); let data = select(sql:: - <(types::Interval, types::Interval, types::Interval, types::Interval)>( + ( "'1 minute'::interval, '1 day'::interval, '1 month'::interval, '4 years 3 days 2 hours 1 minute'::interval")) .first(&connection); @@ -162,13 +162,13 @@ fn adding_interval_to_timestamp() { fn setup_test_table(conn: &TestConnection) { use schema_dsl::*; - create_table("has_timestamps", ( + create_table("has_timestamps", hlist!( integer("id").primary_key().auto_increment(), timestamp("created_at").not_null(), timestamp("updated_at").not_null().default("CURRENT_TIMESTAMP"), )).execute(conn).unwrap(); - create_table("has_time", ( + create_table("has_time", hlist!( integer("id").primary_key().auto_increment(), time("time").not_null(), )).execute(conn).unwrap(); diff --git a/diesel_tests/tests/find.rs b/diesel_tests/tests/find.rs index 057ac44e3a48..0a2bfb7faac8 100644 --- a/diesel_tests/tests/find.rs +++ b/diesel_tests/tests/find.rs @@ -48,8 +48,8 @@ fn find_with_composite_pk() { .execute(&connection) .unwrap(); - assert_eq!(Ok(first_following), followings.find((1, 1)).first(&connection)); - assert_eq!(Ok(second_following), followings.find((1, 2)).first(&connection)); - assert_eq!(Ok(third_following), followings.find((2, 1)).first(&connection)); - assert_eq!(Ok(None::), followings.find((2, 2)).first(&connection).optional()); + assert_eq!(Ok(first_following), followings.find(hlist!(1, 1)).first(&connection)); + assert_eq!(Ok(second_following), followings.find(hlist!(1, 2)).first(&connection)); + assert_eq!(Ok(third_following), followings.find(hlist!(2, 1)).first(&connection)); + assert_eq!(Ok(None::), followings.find(hlist!(2, 2)).first(&connection).optional()); } diff --git a/diesel_tests/tests/insert.rs b/diesel_tests/tests/insert.rs index c511c848dc2c..9dca0fb488fa 100644 --- a/diesel_tests/tests/insert.rs +++ b/diesel_tests/tests/insert.rs @@ -52,7 +52,7 @@ fn insert_records_with_custom_returning_clause() { let inserted_users = insert(new_users) .into(users) - .returning((name, hair_color)) + .returning(hlist!(name, hair_color)) .get_results::<(String, Option)>(&connection) .unwrap(); let expected_users = vec![ @@ -71,7 +71,7 @@ fn batch_insert_with_defaults() { let connection = connection(); connection.execute("DROP TABLE users").unwrap(); - create_table("users", ( + create_table("users", hlist!( integer("id").primary_key().auto_increment(), string("name").not_null(), string("hair_color").not_null().default("'Green'"), @@ -100,7 +100,7 @@ fn insert_with_defaults() { let connection = connection(); connection.execute("DROP TABLE users").unwrap(); - create_table("users", ( + create_table("users", hlist!( integer("id").primary_key().auto_increment(), string("name").not_null(), string("hair_color").not_null().default("'Green'"), diff --git a/diesel_tests/tests/joins.rs b/diesel_tests/tests/joins.rs index 308c916369fb..a715b5ae84a4 100644 --- a/diesel_tests/tests/joins.rs +++ b/diesel_tests/tests/joins.rs @@ -56,7 +56,7 @@ fn select_multiple_from_join() { ").unwrap(); let source = posts::table.inner_join(users::table) - .select((users::name, posts::title)); + .select(hlist!(users::name, posts::title)); let expected_data = vec![ ("Sean".to_string(), "Hello".to_string()), @@ -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).select(hlist!(users::name, posts::title)); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -140,7 +140,7 @@ fn columns_on_right_side_of_left_outer_joins_can_be_used_in_filter() { ("Sean".to_string(), Some("Hello".to_string())), ]; let source = users::table.left_outer_join(posts::table) - .select((users::name, posts::title)) + .select(hlist!(users::name, posts::title)) .filter(posts::title.eq("Hello")); let actual_data: Vec<_> = source.load(&connection).unwrap(); @@ -162,7 +162,7 @@ fn select_multiple_from_right_side_returns_optional_tuple_when_nullable_is_calle None, ]; - let source = users::table.left_outer_join(posts::table).select((posts::title, posts::body).nullable()); + let source = users::table.left_outer_join(posts::table).select(hlist!(posts::title, posts::body).nullable()); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -185,7 +185,7 @@ fn select_complex_from_left_join() { (tess, None), ]; - let source = users::table.left_outer_join(posts::table).select((users::all_columns, (posts::title, posts::body).nullable())); + let source = users::table.left_outer_join(posts::table).select(hlist!(users::all_columns, hlist!(posts::title, posts::body).nullable())); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); @@ -208,7 +208,7 @@ 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).nullable())); + let source = users::table.left_outer_join(posts::table).select(hlist!(users::all_columns, hlist!(posts::body, posts::title).nullable())); let actual_data: Vec<_> = source.load(&connection).unwrap(); assert_eq!(expected_data, actual_data); diff --git a/diesel_tests/tests/select.rs b/diesel_tests/tests/select.rs index ca4b4aac2312..80719d6259dd 100644 --- a/diesel_tests/tests/select.rs +++ b/diesel_tests/tests/select.rs @@ -15,7 +15,7 @@ fn selecting_basic_data() { ("Tess".to_string(), None::), ]; let actual_data: Vec<_> = users - .select((name, hair_color)) + .select(hlist!(name, hair_color)) .load(&connection) .unwrap(); assert_eq!(expected_data, actual_data); @@ -33,7 +33,7 @@ fn selecting_a_struct() { NewUser::new("Tess", None), ]; let actual_users: Vec<_> = users - .select((name, hair_color)) + .select(hlist!(name, hair_color)) .load(&connection).unwrap(); assert_eq!(expected_users, actual_users); } @@ -77,7 +77,7 @@ fn selecting_nullable_followed_by_non_null() { connection.execute("INSERT INTO users (name) VALUES ('Sean')") .unwrap(); - let source = users.select((hair_color, name)); + let source = users.select(hlist!(hair_color, name)); let expected_data = vec![(None::, "Sean".to_string())]; let data: Vec<_> = source.load(&connection).unwrap(); @@ -112,7 +112,7 @@ fn selecting_columns_and_tables_with_reserved_names() { use self::select::dsl::*; let connection = connection(); - create_table("select", ( + create_table("select", hlist!( integer("id").primary_key().auto_increment(), integer("join").not_null(), )).execute(&connection).unwrap(); @@ -133,7 +133,7 @@ fn selecting_columns_and_tables_with_reserved_names() { fn selecting_columns_with_different_definition_order() { let connection = connection(); connection.execute("DROP TABLE users").unwrap(); - create_table("users", ( + create_table("users", hlist!( integer("id").primary_key().auto_increment(), string("hair_color"), string("name").not_null(), diff --git a/diesel_tests/tests/transactions.rs b/diesel_tests/tests/transactions.rs index ea177f4e1216..572dff4a7ac1 100644 --- a/diesel_tests/tests/transactions.rs +++ b/diesel_tests/tests/transactions.rs @@ -106,7 +106,7 @@ fn test_transaction_panics_on_error() { fn setup_test_table(connection: &TestConnection, table_name: &str) { use schema_dsl::*; - create_table(table_name, ( + create_table(table_name, hlist!( integer("id").primary_key().auto_increment(), )).execute(connection).unwrap(); } diff --git a/diesel_tests/tests/update.rs b/diesel_tests/tests/update.rs index b5d771c51b5d..d72aa6f5e75a 100644 --- a/diesel_tests/tests/update.rs +++ b/diesel_tests/tests/update.rs @@ -60,7 +60,7 @@ fn test_updating_multiple_columns() { let connection = connection_with_sean_and_tess_in_users_table(); let sean = find_user_by_name("Sean", &connection); - update(users.filter(id.eq(sean.id))).set(( + update(users.filter(id.eq(sean.id))).set(hlist!( name.eq("Jim"), hair_color.eq(Some("black")), )).execute(&connection).unwrap(); @@ -93,7 +93,7 @@ fn update_with_custom_returning_clause() { let sean = find_user_by_name("Sean", &connection); let user = update(users.filter(id.eq(sean.id))) .set(hair_color.eq("black")) - .returning((name, hair_color)) + .returning(hlist!(name, hair_color)) .get_result::<(String, Option)>(&connection); let expected_result = ("Sean".to_string(), Some("black".to_string()));