Repository: diesel-rs/diesel (language: rust)

Issue:
add select_by
fixes #2037 

- [x] implements
- [x] documents
- [x] [name](https://github.com/diesel-rs/diesel/pull/2367#discussion_r410656773) of the trait `Selectable::Expression`
- [ ] [rethink](https://github.com/diesel-rs/diesel/pull/2367#discussion_r410752231) `GroupBy`
- [x] work with `Join`/`InternalJoinDsl`
- [x] do not implement [`AsQuery`](https://github.com/diesel-rs/diesel/pull/2367#discussion_r410826692)
- [ ] implements `DistinctOnDsl`, `ToInnerJoin`
Consider `#[derive(QueryAll)]`
Not sold on the name. We should come up with a better one.

A question that periodically comes up is "my `Queryable` struct uses all but one column from my table. How do I do this?"

In crates.io we have the following:

```rust
/// We literally never want to select `textsearchable_index_col`
/// so we provide this type and constant to pass to `.select`
type AllColumns = (
    crates::id,
    crates::name,
    crates::updated_at,
    crates::created_at,
    crates::downloads,
    crates::description,
    crates::homepage,
    crates::documentation,
    crates::readme,
    crates::readme_file,
    crates::license,
    crates::repository,
    crates::max_upload_size,
);

pub const ALL_COLUMNS: AllColumns = (
    crates::id,
    crates::name,
    crates::updated_at,
    crates::created_at,
    crates::downloads,
    crates::description,
    crates::homepage,
    crates::documentation,
    crates::readme,
    crates::readme_file,
    crates::license,
    crates::repository,
    crates::max_upload_size,
);

impl Crate {
    pub fn all() -> diesel::dsl::Select<crates::table, AllColumns> {
        crates::table.select(ALL_COLUMNS)
    }
}
```

This seems like a reasonable pattern to derive in Diesel. I'd want to pull this into a trait though so the type and constant can become an associated type and associated constant. I think I'd also want to call them `UsedColumns`.



Discussion / hints:

Fix patch:
diff --git a/diesel/src/connection/mod.rs b/diesel/src/connection/mod.rs
index 4d03dc6e66b0..9e15a1b60589 100644
--- a/diesel/src/connection/mod.rs
+++ b/diesel/src/connection/mod.rs
@@ -9,6 +9,7 @@ use crate::backend::Backend;
 use crate::deserialize::FromSqlRow;
 use crate::expression::QueryMetadata;
 use crate::query_builder::{AsQuery, QueryFragment, QueryId};
+use crate::query_dsl::load_dsl::CompatibleType;
 use crate::result::*;
 
 #[doc(hidden)]
@@ -180,12 +181,13 @@ pub trait Connection: SimpleConnection + Send {
     fn execute(&self, query: &str) -> QueryResult<usize>;
 
     #[doc(hidden)]
-    fn load<T, U>(&self, source: T) -> QueryResult<Vec<U>>
+    fn load<T, U, ST>(&self, source: T) -> QueryResult<Vec<U>>
     where
         Self: Sized,
         T: AsQuery,
         T::Query: QueryFragment<Self::Backend> + QueryId,
-        U: FromSqlRow<T::SqlType, Self::Backend>,
+        T::SqlType: CompatibleType<U, Self::Backend, SqlType = ST>,
+        U: FromSqlRow<ST, Self::Backend>,
         Self::Backend: QueryMetadata<T::SqlType>;
 
     #[doc(hidden)]
diff --git a/diesel/src/deserialize.rs b/diesel/src/deserialize.rs
index 9f700d574d4d..6f3f4965c797 100644
--- a/diesel/src/deserialize.rs
+++ b/diesel/src/deserialize.rs
@@ -4,8 +4,10 @@ use std::error::Error;
 use std::result;
 
 use crate::backend::{self, Backend};
+use crate::expression::select_by::SelectBy;
 use crate::row::{NamedRow, Row};
 use crate::sql_types::{SingleValue, SqlType, Untyped};
+use crate::Selectable;
 
 /// A specialized result type representing the result of deserializing
 /// a value from the database.
@@ -385,10 +387,21 @@ pub trait FromStaticSqlRow<ST, DB: Backend>: Sized {
     fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result<Self>;
 }
 
+#[doc(hidden)]
+pub trait SqlTypeOrSelectable {}
+
+impl<ST> SqlTypeOrSelectable for ST where ST: SqlType + SingleValue {}
+impl<U, DB> SqlTypeOrSelectable for SelectBy<U, DB>
+where
+    U: Selectable<DB>,
+    DB: Backend,
+{
+}
+
 impl<T, ST, DB> FromSqlRow<ST, DB> for T
 where
     T: Queryable<ST, DB>,
-    ST: SqlType,
+    ST: SqlTypeOrSelectable,
     DB: Backend,
     T::Row: FromStaticSqlRow<ST, DB>,
 {
@@ -434,7 +447,7 @@ where
 
 impl<T, ST, DB> StaticallySizedRow<ST, DB> for T
 where
-    ST: SqlType + crate::util::TupleSize,
+    ST: SqlTypeOrSelectable + crate::util::TupleSize,
     T: Queryable<ST, DB>,
     DB: Backend,
 {
diff --git a/diesel/src/expression/mod.rs b/diesel/src/expression/mod.rs
index 6a3afca94576..a10a737a2e5e 100644
--- a/diesel/src/expression/mod.rs
+++ b/diesel/src/expression/mod.rs
@@ -40,6 +40,8 @@ pub mod nullable;
 #[macro_use]
 pub mod operators;
 #[doc(hidden)]
+pub mod select_by;
+#[doc(hidden)]
 pub mod sql_literal;
 #[doc(hidden)]
 pub mod subselect;
@@ -318,6 +320,126 @@ where
 {
 }
 
+/// Trait indicating that a record can be selected and queried from the database.
+///
+/// Types which implement `Selectable` represent the select clause of a SQL query.
+/// Use [`SelectableHelper::as_select()`] to construct the select clause. Once you
+/// called `.select(YourType::as_select())` we enforce at the type system level that you
+/// use the same type to load the query result into.
+///
+/// The constructed select clause can contain arbitrary expressions coming from different
+/// tables. The corresponding [derive](derive@Selectable) provides a simple way to
+/// construct a select clause matching fields to the corresponding table columns.
+///
+/// # Examples
+///
+/// If you just want to construct a select clause using an existing struct, you can use
+/// `#[derive(Selectable)]`, See [`#[derive(Selectable)]`](derive@Selectable) for details.
+///
+///
+/// ```rust
+/// # include!("../doctest_setup.rs");
+/// #
+/// use schema::users;
+///
+/// #[derive(Queryable, PartialEq, Debug, Selectable)]
+/// struct User {
+///     id: i32,
+///     name: String,
+/// }
+///
+/// # fn main() {
+/// #     run_test();
+/// # }
+/// #
+/// # fn run_test() -> QueryResult<()> {
+/// #     use schema::users::dsl::*;
+/// #     let connection = establish_connection();
+/// let first_user = users.select(User::as_select()).first(&connection)?;
+/// let expected = User { id: 1, name: "Sean".into() };
+/// assert_eq!(expected, first_user);
+/// #     Ok(())
+/// # }
+/// ```
+///
+/// Alternatively, we can implement the trait for our struct manually.
+///
+/// ```rust
+/// # include!("../doctest_setup.rs");
+/// #
+/// use schema::users;
+/// use diesel::prelude::{Queryable, Selectable};
+/// use diesel::backend::Backend;
+///
+/// #[derive(Queryable, PartialEq, Debug)]
+/// struct User {
+///     id: i32,
+///     name: String,
+/// }
+///
+/// impl<DB> Selectable<DB> for User
+/// where
+///     DB: Backend
+/// {
+///     type SelectExpression = (users::id, users::name);
+///
+///     fn construct_selection() -> Self::SelectExpression {
+///         (users::id, users::name)
+///     }
+/// }
+///
+/// # fn main() {
+/// #     run_test();
+/// # }
+/// #
+/// # fn run_test() -> QueryResult<()> {
+/// #     use schema::users::dsl::*;
+/// #     let connection = establish_connection();
+/// let first_user = users.select(User::as_select()).first(&connection)?;
+/// let expected = User { id: 1, name: "Sean".into() };
+/// assert_eq!(expected, first_user);
+/// #     Ok(())
+/// # }
+/// ```
+pub trait Selectable<DB: Backend> {
+    /// The expression you'd like to select.
+    ///
+    /// This is typically a tuple of corresponding to the table columns of your struct's fields.
+    type SelectExpression: Expression;
+
+    /// Construct an instance of the expression
+    fn construct_selection() -> Self::SelectExpression;
+}
+
+#[doc(inline)]
+pub use diesel_derives::Selectable;
+
+/// This helper trait provides several methods for
+/// constructing a select or returning clause based on a
+/// [`Selectable`] implementation.
+pub trait SelectableHelper<DB: Backend>: Selectable<DB> + Sized {
+    /// Construct a select clause based on a [`Selectable`] implementation.
+    ///
+    /// The returned select clause enforces that you use the same type
+    /// for constructing the select clause and for loading the query result into.
+    fn as_select() -> select_by::SelectBy<Self, DB>;
+
+    /// An alias for `as_select` that can be used with returning clauses
+    fn as_returning() -> select_by::SelectBy<Self, DB> {
+        Self::as_select()
+    }
+}
+
+impl<T, DB> SelectableHelper<DB> for T
+where
+    T: Selectable<DB>,
+    DB: Backend,
+{
+    fn as_select() -> select_by::SelectBy<Self, DB> {
+        select_by::SelectBy::new()
+    }
+}
+
 /// Is this expression valid for a given group by clause?
 ///
 /// Implementations of this trait must ensure that aggregate expressions are
diff --git a/diesel/src/expression/nullable.rs b/diesel/src/expression/nullable.rs
index ee4b23481917..144ae3a11a84 100644
--- a/diesel/src/expression/nullable.rs
+++ b/diesel/src/expression/nullable.rs
@@ -49,6 +49,19 @@ impl<T: QueryId> QueryId for Nullable<T> {
     const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID;
 }
 
+impl<T, DB> Selectable<DB> for Option<T>
+where
+    DB: Backend,
+    T: Selectable<DB>,
+    Nullable<T::SelectExpression>: Expression,
+{
+    type SelectExpression = Nullable<T::SelectExpression>;
+
+    fn construct_selection() -> Self::SelectExpression {
+        Nullable::new(T::construct_selection())
+    }
+}
+
 impl<T, QS> SelectableExpression<QS> for Nullable<T>
 where
     Self: AppearsOnTable<QS>,
diff --git a/diesel/src/expression/select_by.rs b/diesel/src/expression/select_by.rs
new file mode 100644
index 000000000000..955bfb1e2ef1
--- /dev/null
+++ b/diesel/src/expression/select_by.rs
@@ -0,0 +1,103 @@
+use crate::backend::Backend;
+use crate::dsl::SqlTypeOf;
+use crate::expression::{
+    AppearsOnTable, Expression, QueryMetadata, Selectable, SelectableExpression,
+    TypedExpressionType, ValidGrouping,
+};
+use crate::query_builder::*;
+use crate::result::QueryResult;
+
+#[derive(Debug, Default)]
+pub struct SelectBy<T, DB>(std::marker::PhantomData<(T, DB)>);
+
+impl<T, DB> Clone for SelectBy<T, DB> {
+    fn clone(&self) -> Self {
+        Self(self.0)
+    }
+}
+
+impl<T, DB> Copy for SelectBy<T, DB> {}
+
+impl<T, E, DB> QueryId for SelectBy<T, DB>
+where
+    DB: Backend,
+    T: Selectable<DB, SelectExpression = E>,
+    E: QueryId + Expression,
+{
+    type QueryId = E::QueryId;
+
+    const HAS_STATIC_QUERY_ID: bool = E::HAS_STATIC_QUERY_ID;
+}
+
+impl<T, DB> SelectBy<T, DB> {
+    pub(crate) fn new() -> Self {
+        Self(Default::default())
+    }
+}
+
+impl<T, E, DB> Expression for SelectBy<T, DB>
+where
+    DB: Backend,
+    T: Selectable<DB, SelectExpression = E>,
+    E: QueryId + Expression,
+{
+    type SqlType = SelectBy<T, DB>;
+}
+
+impl<T, DB> TypedExpressionType for SelectBy<T, DB>
+where
+    T: Selectable<DB>,
+    DB: Backend,
+{
+}
+
+impl<T, GB, E, DB> ValidGrouping<GB> for SelectBy<T, DB>
+where
+    DB: Backend,
+    T: Selectable<DB, SelectExpression = E>,
+    E: Expression + ValidGrouping<GB>,
+{
+    type IsAggregate = E::IsAggregate;
+}
+
+impl<T, DB> QueryMetadata<SelectBy<T, DB>> for DB
+where
+    DB: Backend,
+    T: Selectable<DB>,
+    DB: QueryMetadata<SqlTypeOf<T::SelectExpression>>,
+{
+    fn row_metadata(lookup: &Self::MetadataLookup, out: &mut Vec<Option<Self::TypeMetadata>>) {
+        <DB as QueryMetadata<SqlTypeOf<<T as Selectable<DB>>::SelectExpression>>>::row_metadata(
+            lookup, out,
+        )
+    }
+}
+
+impl<T, DB> QueryFragment<DB> for SelectBy<T, DB>
+where
+    T: Selectable<DB>,
+    T::SelectExpression: QueryFragment<DB>,
+    DB: Backend,
+{
+    fn walk_ast(&self, out: AstPass<DB>) -> QueryResult<()> {
+        T::construct_selection().walk_ast(out)
+    }
+}
+
+impl<T, QS, DB> SelectableExpression<QS> for SelectBy<T, DB>
+where
+    DB: Backend,
+    T: Selectable<DB>,
+    T::SelectExpression: SelectableExpression<QS>,
+    Self: AppearsOnTable<QS>,
+{
+}
+
+impl<T, QS, DB> AppearsOnTable<QS> for SelectBy<T, DB>
+where
+    DB: Backend,
+    T: Selectable<DB>,
+    T::SelectExpression: AppearsOnTable<QS>,
+    Self: Expression,
+{
+}
diff --git a/diesel/src/lib.rs b/diesel/src/lib.rs
index 826bd2c8d7e5..8f28291d11f9 100644
--- a/diesel/src/lib.rs
+++ b/diesel/src/lib.rs
@@ -382,7 +382,7 @@ pub mod prelude {
     pub use crate::deserialize::{Queryable, QueryableByName};
     #[doc(inline)]
     pub use crate::expression::{
-        AppearsOnTable, BoxableExpression, Expression, IntoSql, SelectableExpression,
+        AppearsOnTable, BoxableExpression, Expression, IntoSql, Selectable, SelectableExpression,
     };
 
     #[doc(inline)]
@@ -407,6 +407,8 @@ pub mod prelude {
     #[doc(inline)]
     pub use crate::result::{ConnectionError, ConnectionResult, OptionalExtension, QueryResult};
 
+    pub use crate::expression::SelectableHelper;
+
     #[cfg(feature = "mysql")]
     #[doc(inline)]
     pub use crate::mysql::MysqlConnection;
diff --git a/diesel/src/mysql/connection/mod.rs b/diesel/src/mysql/connection/mod.rs
index a6e59416b3b6..5d2d8e095ab4 100644
--- a/diesel/src/mysql/connection/mod.rs
+++ b/diesel/src/mysql/connection/mod.rs
@@ -12,6 +12,7 @@ use crate::deserialize::FromSqlRow;
 use crate::expression::QueryMetadata;
 use crate::query_builder::bind_collector::RawBytesBindCollector;
 use crate::query_builder::*;
+use crate::query_dsl::load_dsl::CompatibleType;
 use crate::result::*;
 
 #[allow(missing_debug_implementations, missing_copy_implementations)]
@@ -59,11 +60,12 @@ impl Connection for MysqlConnection {
     }
 
     #[doc(hidden)]
-    fn load<T, U>(&self, source: T) -> QueryResult<Vec<U>>
+    fn load<T, U, ST>(&self, source: T) -> QueryResult<Vec<U>>
     where
         T: AsQuery,
         T::Query: QueryFragment<Self::Backend> + QueryId,
-        U: FromSqlRow<T::SqlType, Self::Backend>,
+        T::SqlType: CompatibleType<U, Self::Backend, SqlType = ST>,
+        U: FromSqlRow<ST, Self::Backend>,
         Self::Backend: QueryMetadata<T::SqlType>,
     {
         use crate::result::Error::DeserializationError;
diff --git a/diesel/src/pg/connection/mod.rs b/diesel/src/pg/connection/mod.rs
index 0fca09ce44c1..175abbf560aa 100644
--- a/diesel/src/pg/connection/mod.rs
+++ b/diesel/src/pg/connection/mod.rs
@@ -19,6 +19,7 @@ use crate::pg::metadata_lookup::{GetPgMetadataCache, PgMetadataCache};
 use crate::pg::{Pg, TransactionBuilder};
 use crate::query_builder::bind_collector::RawBytesBindCollector;
 use crate::query_builder::*;
+use crate::query_dsl::load_dsl::CompatibleType;
 use crate::result::ConnectionError::CouldntSetupConfiguration;
 use crate::result::Error::DeserializationError;
 use crate::result::*;
@@ -68,11 +69,12 @@ impl Connection for PgConnection {
     }
 
     #[doc(hidden)]
-    fn load<T, U>(&self, source: T) -> QueryResult<Vec<U>>
+    fn load<T, U, ST>(&self, source: T) -> QueryResult<Vec<U>>
     where
         T: AsQuery,
         T::Query: QueryFragment<Self::Backend> + QueryId,
-        U: FromSqlRow<T::SqlType, Self::Backend>,
+        T::SqlType: CompatibleType<U, Self::Backend, SqlType = ST>,
+        U: FromSqlRow<ST, Self::Backend>,
         Self::Backend: QueryMetadata<T::SqlType>,
     {
         let (query, params) = self.prepare_query(&source.as_query())?;
diff --git a/diesel/src/pg/metadata_lookup.rs b/diesel/src/pg/metadata_lookup.rs
index cb2a0393050c..af56123e40e2 100644
--- a/diesel/src/pg/metadata_lookup.rs
+++ b/diesel/src/pg/metadata_lookup.rs
@@ -72,7 +72,8 @@ fn lookup_type<T: Connection<Backend = Pg>>(
         }
         Some(schema) => vec![schema],
         None => {
-            search_path = crate::dsl::sql("SHOW search_path").get_result::<String>(conn)?;
+            search_path = crate::dsl::sql::<crate::sql_types::Text>("SHOW search_path")
+                .get_result::<String>(conn)?;
 
             search_path
                 .split(',')
diff --git a/diesel/src/query_dsl/load_dsl.rs b/diesel/src/query_dsl/load_dsl.rs
index 999bb0ff9633..03b95a20651d 100644
--- a/diesel/src/query_dsl/load_dsl.rs
+++ b/diesel/src/query_dsl/load_dsl.rs
@@ -2,7 +2,7 @@ use super::RunQueryDsl;
 use crate::backend::Backend;
 use crate::connection::Connection;
 use crate::deserialize::FromSqlRow;
-use crate::expression::QueryMetadata;
+use crate::expression::{select_by::SelectBy, Expression, QueryMetadata, Selectable};
 use crate::query_builder::{AsQuery, QueryFragment, QueryId};
 use crate::result::QueryResult;
 
@@ -18,13 +18,49 @@ pub trait LoadQuery<Conn, U>: RunQueryDsl<Conn> {
     fn internal_load(self, conn: &Conn) -> QueryResult<Vec<U>>;
 }
 
+use crate::expression::TypedExpressionType;
+use crate::sql_types::{SqlType, Untyped};
+
+pub trait CompatibleType<U, DB> {
+    type SqlType;
+}
+
+impl<ST, U, DB> CompatibleType<U, DB> for ST
+where
+    DB: Backend,
+    ST: SqlType + crate::sql_types::SingleValue,
+    U: FromSqlRow<ST, DB>,
+{
+    type SqlType = ST;
+}
+
+impl<U, DB> CompatibleType<U, DB> for Untyped
+where
+    U: FromSqlRow<Untyped, DB>,
+    DB: Backend,
+{
+    type SqlType = Untyped;
+}
+
+impl<U, DB, E, ST> CompatibleType<U, DB> for SelectBy<U, DB>
+where
+    DB: Backend,
+    ST: SqlType + TypedExpressionType,
+    U: Selectable<DB, SelectExpression = E>,
+    E: Expression<SqlType = ST>,
+    U: FromSqlRow<ST, DB>,
+{
+    type SqlType = ST;
+}
+
 impl<Conn, T, U> LoadQuery<Conn, U> for T
 where
     Conn: Connection,
     T: AsQuery + RunQueryDsl<Conn>,
     T::Query: QueryFragment<Conn::Backend> + QueryId,
-    U: FromSqlRow<T::SqlType, Conn::Backend>,
+    T::SqlType: CompatibleType<U, Conn::Backend>,
     Conn::Backend: QueryMetadata<T::SqlType>,
+    U: FromSqlRow<<T::SqlType as CompatibleType<U, Conn::Backend>>::SqlType, Conn::Backend>,
 {
     fn internal_load(self, conn: &Conn) -> QueryResult<Vec<U>> {
         conn.load(self)
diff --git a/diesel/src/query_dsl/mod.rs b/diesel/src/query_dsl/mod.rs
index 10aa3f8cfcb3..119d4997258d 100644
--- a/diesel/src/query_dsl/mod.rs
+++ b/diesel/src/query_dsl/mod.rs
@@ -48,6 +48,8 @@ pub use self::belonging_to_dsl::BelongingToDsl;
 pub use self::combine_dsl::CombineDsl;
 pub use self::join_dsl::{InternalJoinDsl, JoinOnDsl, JoinWithImplicitOnClause};
 #[doc(hidden)]
+pub use self::load_dsl::CompatibleType;
+#[doc(hidden)]
 pub use self::load_dsl::LoadQuery;
 pub use self::save_changes_dsl::{SaveChangesDsl, UpdateAndFetchResults};
 
diff --git a/diesel/src/r2d2.rs b/diesel/src/r2d2.rs
index 4e93e05e7f2b..8ab1cfdf90de 100644
--- a/diesel/src/r2d2.rs
+++ b/diesel/src/r2d2.rs
@@ -21,6 +21,7 @@ use crate::deserialize::FromSqlRow;
 use crate::expression::QueryMetadata;
 use crate::prelude::*;
 use crate::query_builder::{AsQuery, QueryFragment, QueryId};
+use crate::query_dsl::load_dsl::CompatibleType;
 
 /// An r2d2 connection manager for use with Diesel.
 ///
@@ -147,11 +148,12 @@ where
         (&**self).execute(query)
     }
 
-    fn load<T, U>(&self, source: T) -> QueryResult<Vec<U>>
+    fn load<T, U, ST>(&self, source: T) -> QueryResult<Vec<U>>
     where
         T: AsQuery,
         T::Query: QueryFragment<Self::Backend> + QueryId,
-        U: FromSqlRow<T::SqlType, Self::Backend>,
+        T::SqlType: CompatibleType<U, Self::Backend, SqlType = ST>,
+        U: FromSqlRow<ST, Self::Backend>,
         Self::Backend: QueryMetadata<T::SqlType>,
     {
         (&**self).load(source)
diff --git a/diesel/src/sqlite/connection/mod.rs b/diesel/src/sqlite/connection/mod.rs
index 5059c8840e26..12428912b08d 100644
--- a/diesel/src/sqlite/connection/mod.rs
+++ b/diesel/src/sqlite/connection/mod.rs
@@ -80,16 +80,17 @@ impl Connection for SqliteConnection {
     }
 
     #[doc(hidden)]
-    fn load<T, U>(&self, source: T) -> QueryResult<Vec<U>>
+    fn load<T, U, ST>(&self, source: T) -> QueryResult<Vec<U>>
     where
         T: AsQuery,
         T::Query: QueryFragment<Self::Backend> + QueryId,
-        U: FromSqlRow<T::SqlType, Self::Backend>,
+        T::SqlType: crate::query_dsl::load_dsl::CompatibleType<U, Sqlite, SqlType = ST>,
+        U: FromSqlRow<ST, Sqlite>,
         Self::Backend: QueryMetadata<T::SqlType>,
     {
         let mut statement = self.prepare_query(&source.as_query())?;
         let statement_use = StatementUse::new(&mut statement, true);
-        let iter = StatementIterator::new(statement_use);
+        let iter = StatementIterator::<_, U>::new(statement_use);
         iter.collect()
     }
 
diff --git a/diesel/src/type_impls/tuples.rs b/diesel/src/type_impls/tuples.rs
index 023fb7af63f6..3f0332237f2f 100644
--- a/diesel/src/type_impls/tuples.rs
+++ b/diesel/src/type_impls/tuples.rs
@@ -1,12 +1,16 @@
 use crate::associations::BelongsTo;
 use crate::backend::Backend;
-use crate::deserialize::{self, FromSqlRow, FromStaticSqlRow, Queryable, StaticallySizedRow};
+use crate::deserialize::{
+    self, FromSqlRow, FromStaticSqlRow, Queryable, SqlTypeOrSelectable, StaticallySizedRow,
+};
 use crate::expression::{
     is_contained_in_group_by, AppearsOnTable, AsExpression, AsExpressionList, Expression,
-    IsContainedInGroupBy, QueryMetadata, SelectableExpression, TypedExpressionType, ValidGrouping,
+    IsContainedInGroupBy, QueryMetadata, Selectable, SelectableExpression, TypedExpressionType,
+    ValidGrouping,
 };
 use crate::insertable::{CanInsertInSingleQuery, InsertValues, Insertable};
 use crate::query_builder::*;
+use crate::query_dsl::load_dsl::CompatibleType;
 use crate::query_source::*;
 use crate::result::QueryResult;
 use crate::row::*;
@@ -57,6 +61,18 @@ macro_rules! tuple_impls {
                 type Nullable = Nullable<($($T,)*)>;
             }
 
+            impl<$($T,)+ __DB> Selectable<__DB> for ($($T,)+)
+            where
+                __DB: Backend,
+                $($T: Selectable<__DB>),+,
+            {
+                type SelectExpression = ($($T::SelectExpression,)+);
+
+                fn construct_selection() -> Self::SelectExpression {
+                    ($($T::construct_selection(),)+)
+                }
+            }
+
             impl<$($T: QueryFragment<__DB>),+, __DB: Backend> QueryFragment<__DB> for ($($T,)+) {
                 #[allow(unused_assignments)]
                 fn walk_ast(&self, mut out: AstPass<__DB>) -> QueryResult<()> {
@@ -316,6 +332,30 @@ macro_rules! tuple_impls {
                 }
             }
 
+            impl<__T, $($ST,)* __DB> CompatibleType<__T, __DB> for ($($ST,)*)
+            where
+                __DB: Backend,
+                __T: FromSqlRow<($($ST,)*), __DB>,
+            {
+                type SqlType = Self;
+            }
+
+            impl<__T, $($ST,)* __DB> CompatibleType<Option<__T>, __DB> for Nullable<($($ST,)*)>
+            where
+                __DB: Backend,
+                ($($ST,)*): CompatibleType<__T, __DB>
+            {
+                type SqlType = Nullable<<($($ST,)*) as CompatibleType<__T, __DB>>::SqlType>;
+            }
+
+            impl<$($ST,)*> SqlTypeOrSelectable for ($($ST,)*)
+            where $($ST: SqlTypeOrSelectable,)*
+            {}
+
+            impl<$($ST,)*> SqlTypeOrSelectable for Nullable<($($ST,)*)>
+            where ($($ST,)*): SqlTypeOrSelectable
+            {}
+
             #[cfg(feature = "postgres")]
             impl<__D, $($T,)*> crate::query_dsl::order_dsl::ValidOrderingForDistinct<crate::pg::DistinctOnClause<__D>>
                 for crate::query_builder::order_clause::OrderClause<(__D, $($T,)*)> {}
@@ -328,7 +368,8 @@ macro_rules! impl_from_sql_row {
     (($T1: ident,), ($ST1: ident,)) => {
         impl<$T1, $ST1, __DB> crate::deserialize::FromStaticSqlRow<($ST1,), __DB> for ($T1,) where
             __DB: Backend,
-            $T1: FromSqlRow<$ST1, __DB>,
+            $ST1: CompatibleType<$T1, __DB>,
+            $T1: FromSqlRow<<$ST1 as CompatibleType<$T1, __DB>>::SqlType, __DB>,
         {
 
             #[allow(non_snake_case, unused_variables, unused_mut)]
@@ -368,9 +409,11 @@ macro_rules! impl_from_sql_row {
 
         impl<$T1, $ST1, $($T,)* $($ST,)* __DB> FromStaticSqlRow<($($ST,)* $ST1,), __DB> for ($($T,)* $T1,) where
             __DB: Backend,
-            $T1: FromSqlRow<$ST1, __DB>,
+            $ST1: CompatibleType<$T1, __DB>,
+            $T1: FromSqlRow<<$ST1 as CompatibleType<$T1, __DB>>::SqlType, __DB>,
             $(
-                $T: FromSqlRow<$ST, __DB> + StaticallySizedRow<$ST, __DB>,
+                $ST: CompatibleType<$T, __DB>,
+                $T: FromSqlRow<<$ST as CompatibleType<$T, __DB>>::SqlType, __DB> + StaticallySizedRow<<$ST as CompatibleType<$T, __DB>>::SqlType, __DB>,
             )*
 
         {
@@ -385,7 +428,7 @@ macro_rules! impl_from_sql_row {
                 $(
                     let row = full_row.partial_row(static_field_count..static_field_count + $T::FIELD_COUNT);
                     static_field_count += $T::FIELD_COUNT;
-                    let $T = $T::build_from_row(&row)?;
+                    let $T = <$T as FromSqlRow<<$ST as CompatibleType<$T, __DB>>::SqlType, __DB>>::build_from_row(&row)?;
                 )*
 
                 let row = full_row.partial_row(static_field_count..field_count);
diff --git a/diesel_derives/src/lib.rs b/diesel_derives/src/lib.rs
index 1821124a94cd..f5167c9a21e8 100644
--- a/diesel_derives/src/lib.rs
+++ b/diesel_derives/src/lib.rs
@@ -45,6 +45,7 @@ mod insertable;
 mod query_id;
 mod queryable;
 mod queryable_by_name;
+mod selectable;
 mod sql_function;
 mod sql_type;
 mod valid_grouping;
@@ -730,6 +731,48 @@ pub fn derive_queryable_by_name(input: TokenStream) -> TokenStream {
     expand_proc_macro(input, queryable_by_name::derive)
 }
 
+/// Implements `Selectable`
+///
+/// To implement `Selectable` this derive needs to know the corresponding table
+/// type. By default it uses the `snake_case` type name with an added `s`.
+/// It is possible to change this default by using `#[table_name = "something"]`.
+///
+/// If the name of a field on your struct is different than the column in your
+/// `table!` declaration, or if you are deriving this trait on a tuple struct,
+/// you can annotate the field with `#[column_name = "some_column"]`. For tuple
+/// structs, all fields must have this annotation.
+///
+/// If a field is another struct which implements `Selectable`,
+/// instead of a column, you can annotate that struct with `#[diesel(embed)]`.
+/// Then all fields contained by that inner struct are selected as separate tuple.
+/// Fields from a inner struct can come from a different table, as long as the
+/// select clause is valid in current query.
+///
+/// The derive enables using the `SelectableHelper::as_select` method to construct
+/// select clauses, in order to use LoadDsl, you might also check the
+/// `Queryable` trait and derive.
+///
+/// # Attributes
+///
+/// ## Type attributes
+///
+/// * `#[table_name = "path::to::table"]`, specifies a path to the table for which the
+/// current type is selectable. The path is relative to the current module.
+/// If this attribute is not used, the type name converted to
+/// `snake_case` with an added `s` is used as table name.
+///
+/// ## Field attributes
+/// * `#[column_name = "some_column"]`, overrides the column name for
+///    a given field. If not set, the name of the field is used as column
+///    name.
+/// * `#[diesel(embed)]`, specifies that the current field maps not only
+///    single database column, but is a type that implements
+///    `Selectable` on it's own
+#[proc_macro_derive(Selectable, attributes(table_name, column_name, sql_type, diesel))]
+pub fn derive_selectable(input: TokenStream) -> TokenStream {
+    expand_proc_macro(input, selectable::derive)
+}
+
 /// Implement necessary traits for adding a new sql type
 ///
 /// This trait implements all necessary traits to define a
diff --git a/diesel_derives/src/selectable.rs b/diesel_derives/src/selectable.rs
new file mode 100644
index 000000000000..7329b595e7ed
--- /dev/null
+++ b/diesel_derives/src/selectable.rs
@@ -0,0 +1,67 @@
+use proc_macro2;
+use syn;
+
+use field::*;
+use model::*;
+use util::*;
+
+pub fn derive(item: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Diagnostic> {
+    let model = Model::from_item(&item)?;
+
+    let (_, ty_generics, _) = item.generics.split_for_impl();
+    let mut generics = item.generics.clone();
+    generics
+        .params
+        .push(parse_quote!(__DB: diesel::backend::Backend));
+    let (impl_generics, _, where_clause) = generics.split_for_impl();
+
+    let struct_name = &item.ident;
+
+    let field_columns_ty = model
+        .fields()
+        .iter()
+        .map(|f| field_column_ty(f, &model))
+        .collect::<Result<Vec<_>, _>>()?;
+    let field_columns_inst = model
+        .fields()
+        .iter()
+        .map(|f| field_column_inst(f, &model))
+        .collect::<Result<Vec<_>, _>>()?;
+
+    Ok(wrap_in_dummy_mod(quote! {
+        use diesel::expression::Selectable;
+
+        impl #impl_generics Selectable<__DB>
+            for #struct_name #ty_generics
+        #where_clause
+        {
+            type SelectExpression = (#(#field_columns_ty,)*);
+
+            fn construct_selection() -> Self::SelectExpression {
+                (#(#field_columns_inst,)*)
+            }
+        }
+    }))
+}
+
+fn field_column_ty(field: &Field, model: &Model) -> Result<syn::Type, Diagnostic> {
+    if field.has_flag("embed") {
+        let embed_ty = &field.ty;
+        Ok(parse_quote!(<#embed_ty as Selectable<__DB>>::SelectExpression))
+    } else {
+        let table_name = model.table_name();
+        let column_name = field.column_name();
+        Ok(parse_quote!(#table_name::#column_name))
+    }
+}
+
+fn field_column_inst(field: &Field, model: &Model) -> Result<syn::Expr, Diagnostic> {
+    if field.has_flag("embed") {
+        let embed_ty = &field.ty;
+        Ok(parse_quote!(<#embed_ty as Selectable<__DB>>::construct_selection()))
+    } else {
+        let table_name = model.table_name();
+        let column_name = field.column_name();
+        Ok(parse_quote!(#table_name::#column_name))
+    }
+}


Test patch:
diff --git a/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr b/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr
index dc3bbce9e574..24ccea83840c 100644
--- a/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr
+++ b/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr
@@ -110,7 +110,7 @@ error[E0277]: the trait bound `{integer}: SelectableExpression<()>` is not satis
               <(A, B) as SelectableExpression<QS>>
               <(A, B, C) as SelectableExpression<QS>>
               <(A, B, C, D) as SelectableExpression<QS>>
-            and 127 others
+            and 128 others
     = note: required because of the requirements on the impl of `SelectableExpression<()>` for `({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>)`
     = note: 1 redundant requirements hidden
     = note: required because of the requirements on the impl of `SelectableExpression<()>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
@@ -132,7 +132,7 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied
               <(A, B) as ValidGrouping<__GroupByClause>>
               <(A, B, C) as ValidGrouping<__GroupByClause>>
               <(A, B, C, D) as ValidGrouping<__GroupByClause>>
-            and 122 others
+            and 123 others
     = note: required because of the requirements on the impl of `ValidGrouping<()>` for `({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>)`
     = note: 1 redundant requirements hidden
     = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
@@ -149,7 +149,7 @@ error[E0277]: the trait bound `{integer}: SelectableExpression<()>` is not satis
              <(A, B) as SelectableExpression<QS>>
              <(A, B, C) as SelectableExpression<QS>>
              <(A, B, C, D) as SelectableExpression<QS>>
-           and 127 others
+           and 128 others
    = note: required because of the requirements on the impl of `SelectableExpression<()>` for `({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>)`
    = note: 1 redundant requirements hidden
    = note: required because of the requirements on the impl of `SelectableExpression<()>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
@@ -168,7 +168,7 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied
              <(A, B) as ValidGrouping<__GroupByClause>>
              <(A, B, C) as ValidGrouping<__GroupByClause>>
              <(A, B, C, D) as ValidGrouping<__GroupByClause>>
-           and 122 others
+           and 123 others
    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>)`
    = note: 1 redundant requirements hidden
    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
@@ -186,7 +186,7 @@ error[E0277]: the trait bound `{integer}: QueryId` is not satisfied
              <() as QueryId>
              <(A, B) as QueryId>
              <(A, B, C) as QueryId>
-           and 188 others
+           and 189 others
    = note: required because of the requirements on the impl of `QueryId` for `({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>)`
    = note: 3 redundant requirements hidden
    = note: required because of the requirements on the impl of `QueryId` for `SelectStatement<(), diesel::query_builder::select_clause::SelectClause<diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>>>`
@@ -203,7 +203,7 @@ error[E0277]: the trait bound `{integer}: QueryFragment<_>` is not satisfied
              <() as QueryFragment<DB>>
              <(A, B) as QueryFragment<__DB>>
              <(A, B, C) as QueryFragment<__DB>>
-           and 218 others
+           and 219 others
    = note: required because of the requirements on the impl of `QueryFragment<_>` for `({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>)`
    = note: 2 redundant requirements hidden
    = note: required because of the requirements on the impl of `QueryFragment<_>` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::expression::bound::Bound<diesel::sql_types::Double, f64>), diesel::sql_types::Double>`
@@ -228,7 +228,7 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied
              <(A, B) as diesel::Expression>
              <(A, B, C) as diesel::Expression>
              <(A, B, C, D) as diesel::Expression>
-           and 103 others
+           and 104 others
    = note: required because of the requirements on the impl of `AsExpression<diesel::sql_types::Double>` for `{integer}`
    = note: required because of the requirements on the impl of `AsExpressionList<diesel::sql_types::Double>` for `({integer}, f64)`
 
diff --git a/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr b/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr
index afaf2a0835dd..e83dfcf408b1 100644
--- a/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr
+++ b/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr
@@ -31,7 +31,7 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied
              <(A, B) as diesel::Expression>
              <(A, B, C) as diesel::Expression>
              <(A, B, C, D) as diesel::Expression>
-           and 107 others
+           and 108 others
    = note: required because of the requirements on the impl of `diesel::Expression` for `diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>`
    = note: required because of the requirements on the impl of `FilterDsl<Grouped<diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>>>` for `SelectStatement<string_primary_key::table>`
 
@@ -46,6 +46,6 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied
              <(A, B) as ValidGrouping<__GroupByClause>>
              <(A, B, C) as ValidGrouping<__GroupByClause>>
              <(A, B, C, D) as ValidGrouping<__GroupByClause>>
-           and 128 others
+           and 129 others
    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>`
    = note: required because of the requirements on the impl of `FilterDsl<Grouped<diesel::expression::operators::Eq<string_primary_key::columns::id, {integer}>>>` for `SelectStatement<string_primary_key::table>`
diff --git a/diesel_compile_tests/tests/fail/insert_requires_value_of_same_type_as_column.stderr b/diesel_compile_tests/tests/fail/insert_requires_value_of_same_type_as_column.stderr
index 0e0e176063f0..275645dd2bc8 100644
--- a/diesel_compile_tests/tests/fail/insert_requires_value_of_same_type_as_column.stderr
+++ b/diesel_compile_tests/tests/fail/insert_requires_value_of_same_type_as_column.stderr
@@ -9,5 +9,5 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied
              <(A, B) as diesel::Expression>
              <(A, B, C) as diesel::Expression>
              <(A, B, C, D) as diesel::Expression>
-           and 107 others
+           and 108 others
    = note: required because of the requirements on the impl of `AsExpression<diesel::sql_types::Text>` for `{integer}`
diff --git a/diesel_compile_tests/tests/fail/queryable_type_missmatch.stderr b/diesel_compile_tests/tests/fail/queryable_type_missmatch.stderr
index dd5adeca847b..f2b70a862b87 100644
--- a/diesel_compile_tests/tests/fail/queryable_type_missmatch.stderr
+++ b/diesel_compile_tests/tests/fail/queryable_type_missmatch.stderr
@@ -1,45 +1,39 @@
-error[E0277]: the trait bound `UserWithToFewFields: diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` is not satisfied
+error[E0277]: the trait bound `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>): CompatibleType<UserWithToFewFields, _>` is not satisfied
   --> $DIR/queryable_type_missmatch.rs:59:26
    |
 59 |     let _ = users::table.load::<UserWithToFewFields>(conn);
-   |                          ^^^^ the trait `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` is not implemented for `UserWithToFewFields`
+   |                          ^^^^ the trait `CompatibleType<UserWithToFewFields, _>` is not implemented for `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>)`
    |
    = help: the following implementations were found:
-             <UserWithToFewFields as diesel::Queryable<(__ST0, __ST1), __DB>>
-   = note: required because of the requirements on the impl of `FromSqlRow<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` for `UserWithToFewFields`
+             <(SA, SB, SC) as CompatibleType<__T, __DB>>
    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithToFewFields>` for `users::table`
 
-error[E0277]: the trait bound `UserWithToManyFields: diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` is not satisfied
+error[E0277]: the trait bound `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>): CompatibleType<UserWithToManyFields, _>` is not satisfied
   --> $DIR/queryable_type_missmatch.rs:61:26
    |
 61 |     let _ = users::table.load::<UserWithToManyFields>(conn);
-   |                          ^^^^ the trait `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` is not implemented for `UserWithToManyFields`
+   |                          ^^^^ the trait `CompatibleType<UserWithToManyFields, _>` is not implemented for `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>)`
    |
    = help: the following implementations were found:
-             <UserWithToManyFields as diesel::Queryable<(__ST0, __ST1, __ST2, __ST3), __DB>>
-   = note: required because of the requirements on the impl of `FromSqlRow<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` for `UserWithToManyFields`
+             <(SA, SB, SC) as CompatibleType<__T, __DB>>
    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithToManyFields>` for `users::table`
 
-error[E0277]: the trait bound `(std::string::String, i32, Option<std::string::String>): FromStaticSqlRow<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` is not satisfied
+error[E0277]: the trait bound `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>): CompatibleType<UserWrongOrder, _>` is not satisfied
   --> $DIR/queryable_type_missmatch.rs:63:26
    |
 63 |     let _ = users::table.load::<UserWrongOrder>(conn);
-   |                          ^^^^ the trait `FromStaticSqlRow<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` is not implemented for `(std::string::String, i32, Option<std::string::String>)`
+   |                          ^^^^ the trait `CompatibleType<UserWrongOrder, _>` is not implemented for `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>)`
    |
    = help: the following implementations were found:
-             <(B, C, A) as FromStaticSqlRow<(SB, SC, SA), __DB>>
-   = note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` for `UserWrongOrder`
-   = note: required because of the requirements on the impl of `FromSqlRow<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` for `UserWrongOrder`
+             <(SA, SB, SC) as CompatibleType<__T, __DB>>
    = note: required because of the requirements on the impl of `LoadQuery<_, UserWrongOrder>` for `users::table`
 
-error[E0277]: the trait bound `(i32, i32, Option<std::string::String>): FromStaticSqlRow<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` is not satisfied
+error[E0277]: the trait bound `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>): CompatibleType<UserTypeMissmatch, _>` is not satisfied
   --> $DIR/queryable_type_missmatch.rs:65:26
    |
 65 |     let _ = users::table.load::<UserTypeMissmatch>(conn);
-   |                          ^^^^ the trait `FromStaticSqlRow<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` is not implemented for `(i32, i32, Option<std::string::String>)`
+   |                          ^^^^ the trait `CompatibleType<UserTypeMissmatch, _>` is not implemented for `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>)`
    |
    = help: the following implementations were found:
-             <(B, C, A) as FromStaticSqlRow<(SB, SC, SA), __DB>>
-   = note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` for `UserTypeMissmatch`
-   = note: required because of the requirements on the impl of `FromSqlRow<(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable<diesel::sql_types::Text>), _>` for `UserTypeMissmatch`
+             <(SA, SB, SC) as CompatibleType<__T, __DB>>
    = note: required because of the requirements on the impl of `LoadQuery<_, UserTypeMissmatch>` for `users::table`
diff --git a/diesel_compile_tests/tests/fail/select_carries_correct_result_type_info.stderr b/diesel_compile_tests/tests/fail/select_carries_correct_result_type_info.stderr
index e4a5420b7bdb..d32bc58540c7 100644
--- a/diesel_compile_tests/tests/fail/select_carries_correct_result_type_info.stderr
+++ b/diesel_compile_tests/tests/fail/select_carries_correct_result_type_info.stderr
@@ -10,6 +10,7 @@ error[E0277]: the trait bound `i32: FromSql<diesel::sql_types::Text, _>` is not
              <i32 as FromSql<diesel::sql_types::Integer, Sqlite>>
    = note: required because of the requirements on the impl of `Queryable<diesel::sql_types::Text, _>` for `i32`
    = note: required because of the requirements on the impl of `FromSqlRow<diesel::sql_types::Text, _>` for `i32`
+   = note: required because of the requirements on the impl of `CompatibleType<i32, _>` for `diesel::sql_types::Text`
    = note: required because of the requirements on the impl of `LoadQuery<_, i32>` for `SelectStatement<users::table, diesel::query_builder::select_clause::SelectClause<columns::name>>`
 
 error[E0277]: the trait bound `*const str: FromSql<diesel::sql_types::Integer, _>` is not satisfied
@@ -27,4 +28,5 @@ error[E0277]: the trait bound `*const str: FromSql<diesel::sql_types::Integer, _
    = note: required because of the requirements on the impl of `FromSql<diesel::sql_types::Integer, _>` for `std::string::String`
    = note: required because of the requirements on the impl of `Queryable<diesel::sql_types::Integer, _>` for `std::string::String`
    = note: required because of the requirements on the impl of `FromSqlRow<diesel::sql_types::Integer, _>` for `std::string::String`
+   = note: required because of the requirements on the impl of `CompatibleType<std::string::String, _>` for `diesel::sql_types::Integer`
    = note: required because of the requirements on the impl of `LoadQuery<_, std::string::String>` for `SelectStatement<users::table, diesel::query_builder::select_clause::SelectClause<columns::id>>`
diff --git a/diesel_compile_tests/tests/fail/select_sql_still_ensures_result_type.stderr b/diesel_compile_tests/tests/fail/select_sql_still_ensures_result_type.stderr
index 45e252829192..ab35b1a740f6 100644
--- a/diesel_compile_tests/tests/fail/select_sql_still_ensures_result_type.stderr
+++ b/diesel_compile_tests/tests/fail/select_sql_still_ensures_result_type.stderr
@@ -13,4 +13,5 @@ error[E0277]: the trait bound `*const str: FromSql<BigInt, _>` is not satisfied
    = note: required because of the requirements on the impl of `FromSql<BigInt, _>` for `std::string::String`
    = note: required because of the requirements on the impl of `Queryable<BigInt, _>` for `std::string::String`
    = note: required because of the requirements on the impl of `FromSqlRow<BigInt, _>` for `std::string::String`
+   = note: required because of the requirements on the impl of `CompatibleType<std::string::String, _>` for `BigInt`
    = note: required because of the requirements on the impl of `LoadQuery<_, std::string::String>` for `SelectStatement<users::table, diesel::query_builder::select_clause::SelectClause<SqlLiteral<BigInt>>>`
diff --git a/diesel_compile_tests/tests/fail/selectable.rs b/diesel_compile_tests/tests/fail/selectable.rs
new file mode 100644
index 000000000000..1d7d67f6cfed
--- /dev/null
+++ b/diesel_compile_tests/tests/fail/selectable.rs
@@ -0,0 +1,212 @@
+extern crate diesel;
+
+use diesel::prelude::*;
+
+table! {
+    users {
+        id -> Integer,
+        name -> Text,
+    }
+}
+
+table! {
+    posts {
+        id -> Integer,
+        title -> Text,
+        user_id -> Integer,
+    }
+}
+
+joinable!(posts -> users(user_id));
+allow_tables_to_appear_in_same_query!(users, posts);
+
+#[derive(Selectable, Queryable)]
+#[table_name = "users"]
+struct UserWithEmbeddedPost {
+    id: i32,
+    name: String,
+    #[diesel(embed)]
+    post: Post,
+}
+
+#[derive(Selectable, Queryable)]
+#[table_name = "users"]
+struct UserWithOptionalPost {
+    id: i32,
+    name: String,
+    #[diesel(embed)]
+    post: Option<Post>,
+}
+
+#[derive(Selectable, Queryable)]
+#[table_name = "posts"]
+struct Post {
+    id: i32,
+    title: String,
+}
+
+#[derive(Selectable)]
+#[table_name = "posts"]
+struct PostWithWrongField {
+    id: i32,
+    // There is a typo here:
+    titel: String
+}
+
+#[derive(Selectable)]
+// wrong table name here
+#[table_name = "post"]
+struct PostWithWrongTableName {
+    id: i32,
+    title: String,
+}
+
+#[derive(Queryable)]
+struct UserWithPostCount {
+    id: i32,
+    name: String,
+    post_count: i64,
+}
+
+impl Selectable<diesel::pg::Pg> for UserWithPostCount {
+    type SelectExpression = (users::id, users::name, diesel::dsl::count<posts::id>);
+
+    fn construct_selection() -> Self::SelectExpression {
+        (users::id, users::name, diesel::dsl::count(posts::id))
+    }
+}
+
+#[derive(Queryable)]
+struct UserWithoutSelectable {
+    id: i32,
+    name: String
+}
+
+
+fn main() {
+    let conn = PgConnection::establish("").unwrap();
+
+    // supported queries
+    //
+    // plain queries
+    let _  = posts::table.select(Post::as_select()).load(&conn).unwrap();
+
+    // boxed queries
+    let _  = posts::table.into_boxed().select(Post::as_select()).load(&conn).unwrap();
+    let _  = posts::table.select(Post::as_select()).into_boxed().load(&conn).unwrap();
+
+    // mixed clauses
+    let _ = posts::table.select((Post::as_select(), posts::title)).load::<(_, String)>(&conn).unwrap();
+
+    // This works for inner joins
+    let _ = users::table
+        .inner_join(posts::table)
+        .select(UserWithEmbeddedPost::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // also for left joins
+    let _ = users::table
+        .left_join(posts::table)
+        .select(UserWithOptionalPost::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // allow manual impls with complex expressions
+    // (and group by)
+    let _ = users::table
+        .inner_join(posts::table)
+        .group_by(users::id)
+        .select(UserWithPostCount::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // inserts
+    let _ = diesel::insert_into(posts::table)
+        .values(posts::title.eq(""))
+        .returning(Post::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // update
+    let _ = diesel::update(posts::table)
+        .set(posts::title.eq(""))
+        .returning(Post::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // delete
+    let _ = diesel::delete(posts::table)
+        .returning(Post::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // forbidden queries
+    //
+    // left joins force nullable
+    let _ = users::table
+        .left_join(posts::table)
+        .select(UserWithEmbeddedPost::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // group by clauses are considered
+    let _ = users::table
+        .inner_join(posts::table)
+        .group_by(posts::id)
+        .select(UserWithEmbeddedPost::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // missing group by clause
+    let _ = users::table
+        .inner_join(posts::table)
+        .select(UserWithPostCount::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // cannot load results from more than one table via
+    // returning clauses
+    let _ = diesel::insert_into(users::table)
+        .values(users::name.eq(""))
+        .returning(UserWithEmbeddedPost::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // cannot load results from more than one table via
+    // returning clauses
+    let _ = diesel::update(users::table)
+        .set(users::name.eq(""))
+        .returning(UserWithEmbeddedPost::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // cannot load results from more than one table via
+    // returning clauses
+    let _ = diesel::delete(users::table)
+        .returning(UserWithEmbeddedPost::as_select())
+        .load(&conn)
+        .unwrap();
+
+    // cannot use this method without deriving selectable
+    let _ = users::table.select(UserWithoutSelectable::as_select()).load(&conn).unwrap();
+
+    // type locking
+    let _ = posts::table.select(Post::as_select()).load::<(i32, String)>(&conn).unwrap();
+    let _ = posts::table.select(Post::as_select()).into_boxed().load::<(i32, String)>(&conn).unwrap();
+    let _ = posts::table.select((Post::as_select(), posts::title)).load::<((i32, String), String)>(&conn).unwrap();
+    let _ = diesel::insert_into(posts::table)
+        .values(posts::title.eq(""))
+        .returning(Post::as_select())
+        .load::<(i32, String, i32)>(&conn)
+        .unwrap();
+
+    // cannot use backend specific selectable with other backend
+    let conn = SqliteConnection::establish("").unwrap();
+    let _ = users::table
+        .inner_join(posts::table)
+        .group_by(users::id)
+        .select(UserWithPostCount::as_select())
+        .load(&conn)
+        .unwrap();
+}
diff --git a/diesel_compile_tests/tests/fail/selectable.stderr b/diesel_compile_tests/tests/fail/selectable.stderr
new file mode 100644
index 000000000000..0bc35444c8fb
--- /dev/null
+++ b/diesel_compile_tests/tests/fail/selectable.stderr
@@ -0,0 +1,629 @@
+error[E0433]: failed to resolve: use of undeclared crate or module `post`
+  --> $DIR/selectable.rs:58:16
+   |
+58 | #[table_name = "post"]
+   |                ^^^^^^ use of undeclared crate or module `post`
+
+error[E0412]: cannot find type `titel` in module `posts`
+  --> $DIR/selectable.rs:53:5
+   |
+53 |     titel: String
+   |     ^^^^^ not found in `posts`
+
+error[E0425]: cannot find value `titel` in module `posts`
+  --> $DIR/selectable.rs:53:5
+   |
+53 |     titel: String
+   |     ^^^^^ not found in `posts`
+
+error[E0271]: type mismatch resolving `<posts::table as AppearsInFromClause<posts::table>>::Count == diesel::query_source::Never`
+   --> $DIR/selectable.rs:149:10
+    |
+149 |         .select(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^ expected struct `diesel::query_source::Once`, found struct `diesel::query_source::Never`
+    |
+    = note: required because of the requirements on the impl of `SelectableExpression<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>>` for `posts::columns::id`
+    = note: 4 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `SelectDsl<SelectBy<UserWithEmbeddedPost, _>>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>`
+
+error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:149:10
+    |
+149 |         .select(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::id as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::id as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>>` for `posts::columns::id`
+    = note: 4 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `SelectDsl<SelectBy<UserWithEmbeddedPost, _>>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>`
+
+error[E0277]: the trait bound `posts::columns::title: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:149:10
+    |
+149 |         .select(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::title`
+    |
+    = help: the following implementations were found:
+              <posts::columns::title as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::title as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::title as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>>` for `posts::columns::title`
+    = note: 4 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `SelectDsl<SelectBy<UserWithEmbeddedPost, _>>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>`
+
+error[E0271]: type mismatch resolving `<posts::table as AppearsInFromClause<posts::table>>::Count == diesel::query_source::Never`
+   --> $DIR/selectable.rs:150:10
+    |
+150 |         .load(&conn)
+    |          ^^^^ expected struct `diesel::query_source::Once`, found struct `diesel::query_source::Never`
+    |
+    = note: required because of the requirements on the impl of `SelectableExpression<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>>` for `posts::columns::id`
+    = note: 4 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `SelectClauseExpression<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>` for `diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>`
+    = note: required because of the requirements on the impl of `Query` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:150:10
+    |
+150 |         .load(&conn)
+    |          ^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::id as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::id as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>>` for `posts::columns::id`
+    = note: 4 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `SelectClauseExpression<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>` for `diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>`
+    = note: required because of the requirements on the impl of `Query` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::title: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:150:10
+    |
+150 |         .load(&conn)
+    |          ^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::title`
+    |
+    = help: the following implementations were found:
+              <posts::columns::title as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::title as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::title as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>>` for `posts::columns::title`
+    = note: 4 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `SelectClauseExpression<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>` for `diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>`
+    = note: required because of the requirements on the impl of `Query` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, LeftOuter>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::id: IsContainedInGroupBy<users::columns::id>` is not satisfied
+   --> $DIR/selectable.rs:157:10
+    |
+157 |         .select(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^ the trait `IsContainedInGroupBy<users::columns::id>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::id>>
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::title>>
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::user_id>>
+    = note: required because of the requirements on the impl of `ValidGrouping<posts::columns::id>` for `users::columns::id`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `ValidGrouping<posts::columns::id>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `SelectDsl<SelectBy<UserWithEmbeddedPost, _>>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::DefaultSelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause<NoLimitClause, NoOffsetClause>, diesel::query_builder::group_by_clause::GroupByClause<posts::columns::id>>`
+
+error[E0277]: the trait bound `posts::columns::id: IsContainedInGroupBy<users::columns::name>` is not satisfied
+   --> $DIR/selectable.rs:157:10
+    |
+157 |         .select(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^ the trait `IsContainedInGroupBy<users::columns::name>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::id>>
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::title>>
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::user_id>>
+    = note: required because of the requirements on the impl of `ValidGrouping<posts::columns::id>` for `users::columns::name`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `ValidGrouping<posts::columns::id>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `SelectDsl<SelectBy<UserWithEmbeddedPost, _>>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::DefaultSelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause<NoLimitClause, NoOffsetClause>, diesel::query_builder::group_by_clause::GroupByClause<posts::columns::id>>`
+
+error[E0277]: the trait bound `posts::columns::id: IsContainedInGroupBy<users::columns::id>` is not satisfied
+   --> $DIR/selectable.rs:158:10
+    |
+158 |         .load(&conn)
+    |          ^^^^ the trait `IsContainedInGroupBy<users::columns::id>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::id>>
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::title>>
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::user_id>>
+    = note: required because of the requirements on the impl of `ValidGrouping<posts::columns::id>` for `users::columns::id`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `ValidGrouping<posts::columns::id>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause<NoLimitClause, NoOffsetClause>, diesel::query_builder::group_by_clause::GroupByClause<posts::columns::id>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause<NoLimitClause, NoOffsetClause>, diesel::query_builder::group_by_clause::GroupByClause<posts::columns::id>>`
+
+error[E0277]: the trait bound `posts::columns::id: IsContainedInGroupBy<users::columns::name>` is not satisfied
+   --> $DIR/selectable.rs:158:10
+    |
+158 |         .load(&conn)
+    |          ^^^^ the trait `IsContainedInGroupBy<users::columns::name>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::id>>
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::title>>
+              <posts::columns::id as IsContainedInGroupBy<posts::columns::user_id>>
+    = note: required because of the requirements on the impl of `ValidGrouping<posts::columns::id>` for `users::columns::name`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `ValidGrouping<posts::columns::id>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause<NoLimitClause, NoOffsetClause>, diesel::query_builder::group_by_clause::GroupByClause<posts::columns::id>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithEmbeddedPost, _>>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause<NoLimitClause, NoOffsetClause>, diesel::query_builder::group_by_clause::GroupByClause<posts::columns::id>>`
+
+error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggregates<diesel::expression::is_aggregate::Yes>` is not satisfied
+   --> $DIR/selectable.rs:164:10
+    |
+164 |         .select(UserWithPostCount::as_select())
+    |          ^^^^^^ the trait `MixedAggregates<diesel::expression::is_aggregate::Yes>` is not implemented for `diesel::expression::is_aggregate::No`
+    |
+    = help: the following implementations were found:
+              <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::Never>>
+              <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::No>>
+    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `(users::columns::id, users::columns::name, count::count::count<diesel::sql_types::Integer, posts::columns::id>)`
+    = note: 1 redundant requirements hidden
+    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `SelectBy<UserWithPostCount, Pg>`
+    = note: required because of the requirements on the impl of `SelectDsl<SelectBy<UserWithPostCount, Pg>>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>>`
+
+error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggregates<diesel::expression::is_aggregate::Yes>` is not satisfied
+   --> $DIR/selectable.rs:165:10
+    |
+165 |         .load(&conn)
+    |          ^^^^ the trait `MixedAggregates<diesel::expression::is_aggregate::Yes>` is not implemented for `diesel::expression::is_aggregate::No`
+    |
+    = help: the following implementations were found:
+              <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::Never>>
+              <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::No>>
+    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `(users::columns::id, users::columns::name, count::count::count<diesel::sql_types::Integer, posts::columns::id>)`
+    = note: 1 redundant requirements hidden
+    = note: required because of the requirements on the impl of `ValidGrouping<()>` for `SelectBy<UserWithPostCount, Pg>`
+    = note: required because of the requirements on the impl of `Query` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithPostCount, Pg>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithPostCount>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithPostCount, Pg>>>`
+
+error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:172:10
+    |
+172 |         .returning(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::id as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::id as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `InsertStatement<users::table, ValuesClause<ColumnInsertValue<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, users::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::title: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:172:10
+    |
+172 |         .returning(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::title`
+    |
+    = help: the following implementations were found:
+              <posts::columns::title as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::title as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::title as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `InsertStatement<users::table, ValuesClause<ColumnInsertValue<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, users::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0271]: type mismatch resolving `<users::table as AppearsInFromClause<posts::table>>::Count == diesel::query_source::Once`
+   --> $DIR/selectable.rs:172:10
+    |
+172 |         .returning(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^^^^ expected struct `diesel::query_source::Never`, found struct `diesel::query_source::Once`
+    |
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `posts::columns::id`
+    = note: 1 redundant requirements hidden
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `InsertStatement<users::table, ValuesClause<ColumnInsertValue<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, users::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:173:10
+    |
+173 |         .load(&conn)
+    |          ^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::id as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::id as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `InsertStatement<users::table, ValuesClause<ColumnInsertValue<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, users::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `InsertStatement<users::table, ValuesClause<ColumnInsertValue<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, users::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::title: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:173:10
+    |
+173 |         .load(&conn)
+    |          ^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::title`
+    |
+    = help: the following implementations were found:
+              <posts::columns::title as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::title as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::title as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `InsertStatement<users::table, ValuesClause<ColumnInsertValue<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, users::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `InsertStatement<users::table, ValuesClause<ColumnInsertValue<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, users::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0271]: type mismatch resolving `<users::table as AppearsInFromClause<posts::table>>::Count == diesel::query_source::Once`
+   --> $DIR/selectable.rs:173:10
+    |
+173 |         .load(&conn)
+    |          ^^^^ expected struct `diesel::query_source::Never`, found struct `diesel::query_source::Once`
+    |
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `posts::columns::id`
+    = note: 1 redundant requirements hidden
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `InsertStatement<users::table, ValuesClause<ColumnInsertValue<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, users::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `InsertStatement<users::table, ValuesClause<ColumnInsertValue<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, users::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:180:10
+    |
+180 |         .returning(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::id as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::id as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `UpdateStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::update_statement::changeset::Assign<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::title: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:180:10
+    |
+180 |         .returning(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::title`
+    |
+    = help: the following implementations were found:
+              <posts::columns::title as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::title as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::title as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `UpdateStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::update_statement::changeset::Assign<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0271]: type mismatch resolving `<users::table as AppearsInFromClause<posts::table>>::Count == diesel::query_source::Once`
+   --> $DIR/selectable.rs:180:10
+    |
+180 |         .returning(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^^^^ expected struct `diesel::query_source::Never`, found struct `diesel::query_source::Once`
+    |
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `posts::columns::id`
+    = note: 1 redundant requirements hidden
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `UpdateStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::update_statement::changeset::Assign<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:181:10
+    |
+181 |         .load(&conn)
+    |          ^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::id as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::id as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `UpdateStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::update_statement::changeset::Assign<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `UpdateStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::update_statement::changeset::Assign<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::title: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:181:10
+    |
+181 |         .load(&conn)
+    |          ^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::title`
+    |
+    = help: the following implementations were found:
+              <posts::columns::title as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::title as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::title as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `UpdateStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::update_statement::changeset::Assign<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `UpdateStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::update_statement::changeset::Assign<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0271]: type mismatch resolving `<users::table as AppearsInFromClause<posts::table>>::Count == diesel::query_source::Once`
+   --> $DIR/selectable.rs:181:10
+    |
+181 |         .load(&conn)
+    |          ^^^^ expected struct `diesel::query_source::Never`, found struct `diesel::query_source::Once`
+    |
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `posts::columns::id`
+    = note: 1 redundant requirements hidden
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `UpdateStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::update_statement::changeset::Assign<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `UpdateStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::update_statement::changeset::Assign<users::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:187:10
+    |
+187 |         .returning(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::id as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::id as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+
+error[E0277]: the trait bound `posts::columns::title: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:187:10
+    |
+187 |         .returning(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::title`
+    |
+    = help: the following implementations were found:
+              <posts::columns::title as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::title as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::title as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+
+error[E0271]: type mismatch resolving `<users::table as AppearsInFromClause<posts::table>>::Count == diesel::query_source::Once`
+   --> $DIR/selectable.rs:187:10
+    |
+187 |         .returning(UserWithEmbeddedPost::as_select())
+    |          ^^^^^^^^^ expected struct `diesel::query_source::Never`, found struct `diesel::query_source::Once`
+    |
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `posts::columns::id`
+    = note: 1 redundant requirements hidden
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+
+error[E0277]: the trait bound `posts::columns::id: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:188:10
+    |
+188 |         .load(&conn)
+    |          ^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::id`
+    |
+    = help: the following implementations were found:
+              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::id as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::id as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::id as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `DeleteStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `DeleteStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0277]: the trait bound `posts::columns::title: SelectableExpression<users::table>` is not satisfied
+   --> $DIR/selectable.rs:188:10
+    |
+188 |         .load(&conn)
+    |          ^^^^ the trait `SelectableExpression<users::table>` is not implemented for `posts::columns::title`
+    |
+    = help: the following implementations were found:
+              <posts::columns::title as SelectableExpression<JoinOn<Join, On>>>
+              <posts::columns::title as SelectableExpression<SelectStatement<From>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, Inner>>>
+              <posts::columns::title as SelectableExpression<diesel::query_source::joins::Join<Left, Right, LeftOuter>>>
+              <posts::columns::title as SelectableExpression<posts::table>>
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `DeleteStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `DeleteStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0271]: type mismatch resolving `<users::table as AppearsInFromClause<posts::table>>::Count == diesel::query_source::Once`
+   --> $DIR/selectable.rs:188:10
+    |
+188 |         .load(&conn)
+    |          ^^^^ expected struct `diesel::query_source::Never`, found struct `diesel::query_source::Once`
+    |
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `posts::columns::id`
+    = note: 1 redundant requirements hidden
+    = note: required because of the requirements on the impl of `AppearsOnTable<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `(posts::columns::id, posts::columns::title)`
+    = note: 2 redundant requirements hidden
+    = note: required because of the requirements on the impl of `SelectableExpression<users::table>` for `SelectBy<UserWithEmbeddedPost, _>`
+    = note: required because of the requirements on the impl of `Query` for `DeleteStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, UserWithEmbeddedPost>` for `DeleteStatement<users::table, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::returning_clause::ReturningClause<SelectBy<UserWithEmbeddedPost, _>>>`
+
+error[E0599]: the function or associated item `as_select` exists for struct `UserWithoutSelectable`, but its trait bounds were not satisfied
+   --> $DIR/selectable.rs:192:56
+    |
+80  | struct UserWithoutSelectable {
+    | ----------------------------
+    | |
+    | function or associated item `as_select` not found for this
+    | doesn't satisfy `UserWithoutSelectable: diesel::Selectable<_>`
+    | doesn't satisfy `UserWithoutSelectable: diesel::SelectableHelper<_>`
+...
+192 |     let _ = users::table.select(UserWithoutSelectable::as_select()).load(&conn).unwrap();
+    |                                                        ^^^^^^^^^ function or associated item cannot be called on `UserWithoutSelectable` due to unsatisfied trait bounds
+    |
+    = note: the following trait bounds were not satisfied:
+            `UserWithoutSelectable: diesel::Selectable<_>`
+            which is required by `UserWithoutSelectable: diesel::SelectableHelper<_>`
+            `&UserWithoutSelectable: diesel::Selectable<_>`
+            which is required by `&UserWithoutSelectable: diesel::SelectableHelper<_>`
+            `&mut UserWithoutSelectable: diesel::Selectable<_>`
+            which is required by `&mut UserWithoutSelectable: diesel::SelectableHelper<_>`
+
+error[E0277]: the trait bound `SelectBy<Post, _>: SingleValue` is not satisfied
+   --> $DIR/selectable.rs:195:52
+    |
+195 |     let _ = posts::table.select(Post::as_select()).load::<(i32, String)>(&conn).unwrap();
+    |                                                    ^^^^ the trait `SingleValue` is not implemented for `SelectBy<Post, _>`
+    |
+    = note: required because of the requirements on the impl of `CompatibleType<(i32, std::string::String), _>` for `SelectBy<Post, _>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, (i32, std::string::String)>` for `SelectStatement<posts::table, diesel::query_builder::select_clause::SelectClause<SelectBy<Post, _>>>`
+
+error[E0277]: the trait bound `(i32, std::string::String): diesel::Queryable<SelectBy<Post, _>, _>` is not satisfied
+   --> $DIR/selectable.rs:195:52
+    |
+195 |     let _ = posts::table.select(Post::as_select()).load::<(i32, String)>(&conn).unwrap();
+    |                                                    ^^^^ the trait `diesel::Queryable<SelectBy<Post, _>, _>` is not implemented for `(i32, std::string::String)`
+    |
+    = help: the following implementations were found:
+              <(A, B) as diesel::Queryable<(SA, SB), __DB>>
+              <(A, B) as diesel::Queryable<Record<(SA, SB)>, Pg>>
+              <(std::collections::Bound<T>, std::collections::Bound<T>) as diesel::Queryable<diesel::sql_types::Range<ST>, Pg>>
+    = note: required because of the requirements on the impl of `FromSqlRow<SelectBy<Post, _>, _>` for `(i32, std::string::String)`
+    = note: required because of the requirements on the impl of `CompatibleType<(i32, std::string::String), _>` for `SelectBy<Post, _>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, (i32, std::string::String)>` for `SelectStatement<posts::table, diesel::query_builder::select_clause::SelectClause<SelectBy<Post, _>>>`
+
+error[E0277]: the trait bound `SelectBy<Post, _>: SingleValue` is not satisfied
+   --> $DIR/selectable.rs:196:65
+    |
+196 |     let _ = posts::table.select(Post::as_select()).into_boxed().load::<(i32, String)>(&conn).unwrap();
+    |                                                                 ^^^^ the trait `SingleValue` is not implemented for `SelectBy<Post, _>`
+    |
+    = note: required because of the requirements on the impl of `CompatibleType<(i32, std::string::String), _>` for `SelectBy<Post, _>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, (i32, std::string::String)>` for `BoxedSelectStatement<'_, SelectBy<Post, _>, posts::table, _>`
+
+error[E0277]: the trait bound `(i32, std::string::String): diesel::Queryable<SelectBy<Post, _>, _>` is not satisfied
+   --> $DIR/selectable.rs:196:65
+    |
+196 |     let _ = posts::table.select(Post::as_select()).into_boxed().load::<(i32, String)>(&conn).unwrap();
+    |                                                                 ^^^^ the trait `diesel::Queryable<SelectBy<Post, _>, _>` is not implemented for `(i32, std::string::String)`
+    |
+    = help: the following implementations were found:
+              <(A, B) as diesel::Queryable<(SA, SB), __DB>>
+              <(A, B) as diesel::Queryable<Record<(SA, SB)>, Pg>>
+              <(std::collections::Bound<T>, std::collections::Bound<T>) as diesel::Queryable<diesel::sql_types::Range<ST>, Pg>>
+    = note: required because of the requirements on the impl of `FromSqlRow<SelectBy<Post, _>, _>` for `(i32, std::string::String)`
+    = note: required because of the requirements on the impl of `CompatibleType<(i32, std::string::String), _>` for `SelectBy<Post, _>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, (i32, std::string::String)>` for `BoxedSelectStatement<'_, SelectBy<Post, _>, posts::table, _>`
+
+error[E0277]: the trait bound `(SelectBy<Post, _>, diesel::sql_types::Text): CompatibleType<((i32, std::string::String), std::string::String), _>` is not satisfied
+   --> $DIR/selectable.rs:197:68
+    |
+197 |     let _ = posts::table.select((Post::as_select(), posts::title)).load::<((i32, String), String)>(&conn).unwrap();
+    |                                                                    ^^^^ the trait `CompatibleType<((i32, std::string::String), std::string::String), _>` is not implemented for `(SelectBy<Post, _>, diesel::sql_types::Text)`
+    |
+    = help: the following implementations were found:
+              <(SA, SB) as CompatibleType<__T, __DB>>
+    = note: required because of the requirements on the impl of `LoadQuery<_, ((i32, std::string::String), std::string::String)>` for `SelectStatement<posts::table, diesel::query_builder::select_clause::SelectClause<(SelectBy<Post, _>, posts::columns::title)>>`
+
+error[E0277]: the trait bound `SelectBy<Post, _>: SingleValue` is not satisfied
+   --> $DIR/selectable.rs:201:10
+    |
+201 |         .load::<(i32, String, i32)>(&conn)
+    |          ^^^^ the trait `SingleValue` is not implemented for `SelectBy<Post, _>`
+    |
+    = note: required because of the requirements on the impl of `CompatibleType<(i32, std::string::String, i32), _>` for `SelectBy<Post, _>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, (i32, std::string::String, i32)>` for `InsertStatement<posts::table, ValuesClause<ColumnInsertValue<posts::columns::title, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, posts::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<Post, _>>>`
+
+error[E0277]: the trait bound `(i32, std::string::String, i32): diesel::Queryable<SelectBy<Post, _>, _>` is not satisfied
+   --> $DIR/selectable.rs:201:10
+    |
+201 |         .load::<(i32, String, i32)>(&conn)
+    |          ^^^^ the trait `diesel::Queryable<SelectBy<Post, _>, _>` is not implemented for `(i32, std::string::String, i32)`
+    |
+    = help: the following implementations were found:
+              <(A, B, C) as diesel::Queryable<(SA, SB, SC), __DB>>
+              <(A, B, C) as diesel::Queryable<Record<(SA, SB, SC)>, Pg>>
+    = note: required because of the requirements on the impl of `FromSqlRow<SelectBy<Post, _>, _>` for `(i32, std::string::String, i32)`
+    = note: required because of the requirements on the impl of `CompatibleType<(i32, std::string::String, i32), _>` for `SelectBy<Post, _>`
+    = note: required because of the requirements on the impl of `LoadQuery<_, (i32, std::string::String, i32)>` for `InsertStatement<posts::table, ValuesClause<ColumnInsertValue<posts::columns::title, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>, posts::table>, diesel::query_builder::insert_statement::Insert, diesel::query_builder::returning_clause::ReturningClause<SelectBy<Post, _>>>`
+
+error[E0271]: type mismatch resolving `<diesel::SqliteConnection as diesel::Connection>::Backend == Pg`
+   --> $DIR/selectable.rs:210:10
+    |
+210 |         .load(&conn)
+    |          ^^^^ expected struct `Sqlite`, found struct `Pg`
+    |
+    = note: required because of the requirements on the impl of `LoadQuery<diesel::SqliteConnection, UserWithPostCount>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithPostCount, Pg>>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause<NoLimitClause, NoOffsetClause>, diesel::query_builder::group_by_clause::GroupByClause<users::columns::id>>`
+
+error[E0277]: the trait bound `Sqlite: HasSqlType<SelectBy<UserWithPostCount, Pg>>` is not satisfied
+   --> $DIR/selectable.rs:210:10
+    |
+210 |         .load(&conn)
+    |          ^^^^ the trait `HasSqlType<SelectBy<UserWithPostCount, Pg>>` is not implemented for `Sqlite`
+    |
+    = help: the following implementations were found:
+              <Sqlite as HasSqlType<BigInt>>
+              <Sqlite as HasSqlType<Bool>>
+              <Sqlite as HasSqlType<diesel::sql_types::Binary>>
+              <Sqlite as HasSqlType<diesel::sql_types::Date>>
+            and 8 others
+    = note: required because of the requirements on the impl of `QueryMetadata<SelectBy<UserWithPostCount, Pg>>` for `Sqlite`
+    = note: required because of the requirements on the impl of `LoadQuery<diesel::SqliteConnection, UserWithPostCount>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithPostCount, Pg>>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause<NoLimitClause, NoOffsetClause>, diesel::query_builder::group_by_clause::GroupByClause<users::columns::id>>`
+
+error[E0277]: the trait bound `SelectBy<UserWithPostCount, Pg>: SingleValue` is not satisfied
+   --> $DIR/selectable.rs:210:10
+    |
+210 |         .load(&conn)
+    |          ^^^^ the trait `SingleValue` is not implemented for `SelectBy<UserWithPostCount, Pg>`
+    |
+    = note: required because of the requirements on the impl of `QueryMetadata<SelectBy<UserWithPostCount, Pg>>` for `Sqlite`
+    = note: required because of the requirements on the impl of `LoadQuery<diesel::SqliteConnection, UserWithPostCount>` for `SelectStatement<JoinOn<diesel::query_source::joins::Join<users::table, posts::table, Inner>, Grouped<diesel::expression::operators::Eq<diesel::expression::nullable::Nullable<posts::columns::user_id>, diesel::expression::nullable::Nullable<users::columns::id>>>>, diesel::query_builder::select_clause::SelectClause<SelectBy<UserWithPostCount, Pg>>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause<NoLimitClause, NoOffsetClause>, diesel::query_builder::group_by_clause::GroupByClause<users::columns::id>>`
diff --git a/diesel_derives/tests/selectable.rs b/diesel_derives/tests/selectable.rs
new file mode 100644
index 000000000000..1567403ea078
--- /dev/null
+++ b/diesel_derives/tests/selectable.rs
@@ -0,0 +1,83 @@
+use diesel::*;
+
+use helpers::connection;
+
+table! {
+    my_structs (foo) {
+        foo -> Integer,
+        bar -> Integer,
+    }
+}
+
+#[test]
+fn named_struct_definition() {
+    #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)]
+    #[table_name = "my_structs"]
+    struct MyStruct {
+        foo: i32,
+        bar: i32,
+    }
+
+    let conn = connection();
+    let data = my_structs::table
+        .select(MyStruct::as_select())
+        .get_result(&conn);
+    assert!(data.is_err());
+}
+
+#[test]
+fn tuple_struct() {
+    #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)]
+    #[table_name = "my_structs"]
+    struct MyStruct(#[column_name = "foo"] i32, #[column_name = "bar"] i32);
+
+    let conn = connection();
+    let data = my_structs::table
+        .select(MyStruct::as_select())
+        .get_result(&conn);
+    assert!(data.is_err());
+}
+
+// FIXME: Test usage with renamed columns
+
+#[test]
+fn embedded_struct() {
+    #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)]
+    #[table_name = "my_structs"]
+    struct A {
+        foo: i32,
+        #[diesel(embed)]
+        b: B,
+    }
+
+    #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)]
+    #[table_name = "my_structs"]
+    struct B {
+        bar: i32,
+    }
+
+    let conn = connection();
+    let data = my_structs::table.select(A::as_select()).get_result(&conn);
+    assert!(data.is_err());
+}
+
+#[test]
+fn embedded_option() {
+    #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)]
+    #[table_name = "my_structs"]
+    struct A {
+        foo: i32,
+        #[diesel(embed)]
+        b: Option<B>,
+    }
+
+    #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable, Selectable)]
+    #[table_name = "my_structs"]
+    struct B {
+        bar: i32,
+    }
+
+    let conn = connection();
+    let data = my_structs::table.select(A::as_select()).get_result(&conn);
+    assert!(data.is_err());
+}
diff --git a/diesel_derives/tests/tests.rs b/diesel_derives/tests/tests.rs
index 636fea662bb6..561d0375a2a0 100644
--- a/diesel_derives/tests/tests.rs
+++ b/diesel_derives/tests/tests.rs
@@ -14,3 +14,4 @@ mod identifiable;
 mod insertable;
 mod queryable;
 mod queryable_by_name;
+mod selectable;
diff --git a/diesel_tests/tests/deserialization.rs b/diesel_tests/tests/deserialization.rs
index b72a581f700c..da373d70fa42 100644
--- a/diesel_tests/tests/deserialization.rs
+++ b/diesel_tests/tests/deserialization.rs
@@ -2,7 +2,8 @@ use crate::schema::*;
 use diesel::*;
 use std::borrow::Cow;
 
-#[derive(Queryable, PartialEq, Debug)]
+#[derive(Queryable, PartialEq, Debug, Selectable)]
+#[table_name = "users"]
 struct CowUser<'a> {
     id: i32,
     name: Cow<'a, str>,
@@ -21,4 +22,8 @@ fn generated_queryable_allows_lifetimes() {
         Ok(expected_user),
         users.select((id, name)).first(&connection)
     );
+    assert_eq!(
+        users.select((id, name)).first::<CowUser>(&connection),
+        users.select(CowUser::as_select()).first(&connection)
+    );
 }
diff --git a/diesel_tests/tests/distinct.rs b/diesel_tests/tests/distinct.rs
index 0729f473c971..586718e84767 100644
--- a/diesel_tests/tests/distinct.rs
+++ b/diesel_tests/tests/distinct.rs
@@ -41,3 +41,28 @@ fn distinct_on() {
 
     assert_eq!(expected_data, data);
 }
+
+#[cfg(feature = "postgres")]
+#[test]
+fn distinct_on_select_by() {
+    use crate::schema::users::dsl::*;
+
+    let connection = connection();
+    connection
+        .execute(
+            "INSERT INTO users (name, hair_color) VALUES ('Sean', 'black'), ('Sean', NULL), ('Tess', NULL), ('Tess', NULL)",
+        )
+        .unwrap();
+
+    let source = users
+        .select(NewUser::as_select())
+        .order(name)
+        .distinct_on(name);
+    let expected_data = vec![
+        NewUser::new("Sean", Some("black")),
+        NewUser::new("Tess", None),
+    ];
+    let data: Vec<_> = source.load(&connection).unwrap();
+
+    assert_eq!(expected_data, data);
+}
diff --git a/diesel_tests/tests/filter.rs b/diesel_tests/tests/filter.rs
index be2a6b079ce8..37c46785dcb8 100644
--- a/diesel_tests/tests/filter.rs
+++ b/diesel_tests/tests/filter.rs
@@ -210,6 +210,56 @@ fn filter_then_select() {
     );
 }
 
+#[test]
+fn select_by_then_filter() {
+    use crate::schema::users::dsl::*;
+
+    let connection = connection_with_sean_and_tess_in_users_table();
+
+    let source = users.select(UserName::as_select());
+    assert_eq!(
+        Ok(UserName::new("Sean")),
+        source.filter(name.eq("Sean")).first(&connection)
+    );
+    assert_eq!(
+        Ok(UserName::new("Tess")),
+        source.filter(name.eq("Tess")).first(&connection)
+    );
+    assert_eq!(
+        Err(NotFound),
+        source.filter(name.eq("Jim")).first::<UserName>(&connection)
+    );
+}
+
+#[test]
+fn filter_then_select_by() {
+    use crate::schema::users::dsl::*;
+
+    let connection = connection_with_sean_and_tess_in_users_table();
+
+    assert_eq!(
+        Ok(UserName::new("Sean")),
+        users
+            .filter(name.eq("Sean"))
+            .select(UserName::as_select())
+            .first(&connection)
+    );
+    assert_eq!(
+        Ok(UserName::new("Tess")),
+        users
+            .filter(name.eq("Tess"))
+            .select(UserName::as_select())
+            .first(&connection)
+    );
+    assert_eq!(
+        Err(NotFound),
+        users
+            .filter(name.eq("Jim"))
+            .select(UserName::as_select())
+            .first::<UserName>(&connection)
+    );
+}
+
 #[test]
 fn filter_on_multiple_columns() {
     use crate::schema::users::dsl::*;
diff --git a/diesel_tests/tests/find.rs b/diesel_tests/tests/find.rs
index 19392b552bd4..939c16a941d0 100644
--- a/diesel_tests/tests/find.rs
+++ b/diesel_tests/tests/find.rs
@@ -104,3 +104,21 @@ fn select_then_find() {
     assert_eq!(Ok(String::from("Sean")), sean);
     assert_eq!(Ok(String::from("Tess")), tess);
 }
+
+#[test]
+fn select_by_then_find() {
+    use crate::schema::users::dsl::*;
+
+    let connection = connection_with_sean_and_tess_in_users_table();
+    let sean = users
+        .select(UserName::as_select())
+        .find(1)
+        .first(&connection);
+    let tess = users
+        .select(UserName::as_select())
+        .find(2)
+        .first(&connection);
+
+    assert_eq!(Ok(UserName::new("Sean")), sean);
+    assert_eq!(Ok(UserName::new("Tess")), tess);
+}
diff --git a/diesel_tests/tests/lib.rs b/diesel_tests/tests/lib.rs
index cb3d6efedfd1..d0b6d742d3d7 100644
--- a/diesel_tests/tests/lib.rs
+++ b/diesel_tests/tests/lib.rs
@@ -42,6 +42,7 @@ mod schema;
 mod schema_dsl;
 mod schema_inference;
 mod select;
+mod select_by;
 mod serialize_as;
 #[cfg(not(feature = "mysql"))] // FIXME: Figure out how to handle tests that modify schema
 mod transactions;
diff --git a/diesel_tests/tests/schema/mod.rs b/diesel_tests/tests/schema/mod.rs
index d33cff07dfa4..5d0755705a92 100644
--- a/diesel_tests/tests/schema/mod.rs
+++ b/diesel_tests/tests/schema/mod.rs
@@ -11,7 +11,16 @@ include!("sqlite_schema.rs");
 include!("mysql_schema.rs");
 
 #[derive(
-    PartialEq, Eq, Debug, Clone, Queryable, Identifiable, Insertable, AsChangeset, QueryableByName,
+    PartialEq,
+    Eq,
+    Debug,
+    Clone,
+    Queryable,
+    Identifiable,
+    Insertable,
+    AsChangeset,
+    QueryableByName,
+    Selectable,
 )]
 #[table_name = "users"]
 pub struct User {
@@ -42,6 +51,16 @@ impl User {
     }
 }
 
+#[derive(PartialEq, Eq, Debug, Clone, Queryable, Selectable)]
+#[table_name = "users"]
+pub struct UserName(#[column_name = "name"] pub String);
+
+impl UserName {
+    pub fn new(name: &str) -> Self {
+        UserName(name.to_string())
+    }
+}
+
 #[derive(PartialEq, Eq, Debug, Clone, Queryable, Identifiable, Associations)]
 #[belongs_to(Post)]
 pub struct Comment {
@@ -78,7 +97,7 @@ mod backend_specifics;
 
 pub use self::backend_specifics::*;
 
-#[derive(Debug, PartialEq, Eq, Queryable, Clone, Insertable, AsChangeset)]
+#[derive(Debug, PartialEq, Eq, Queryable, Clone, Insertable, AsChangeset, Selectable)]
 #[table_name = "users"]
 pub struct NewUser {
     pub name: String,
diff --git a/diesel_tests/tests/select_by.rs b/diesel_tests/tests/select_by.rs
new file mode 100644
index 000000000000..820b31e739fd
--- /dev/null
+++ b/diesel_tests/tests/select_by.rs
@@ -0,0 +1,219 @@
+use super::schema::*;
+use diesel::*;
+
+#[test]
+fn selecting_basic_data() {
+    use crate::schema::users::dsl::*;
+
+    let connection = connection_with_sean_and_tess_in_users_table();
+
+    let expected_data = vec![
+        User {
+            id: 1,
+            name: "Sean".to_string(),
+            hair_color: None,
+        },
+        User {
+            id: 2,
+            name: "Tess".to_string(),
+            hair_color: None,
+        },
+    ];
+    let actual_data: Vec<_> = users.select(User::as_select()).load(&connection).unwrap();
+    assert_eq!(expected_data, actual_data);
+}
+
+#[test]
+fn with_safe_select() {
+    use crate::schema::users::dsl::*;
+
+    let connection = connection();
+    connection
+        .execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
+        .unwrap();
+
+    let select_name = users.select(UserName::as_select());
+    let names: Vec<UserName> = select_name.load(&connection).unwrap();
+
+    assert_eq!(vec![UserName::new("Sean"), UserName::new("Tess")], names);
+}
+
+table! {
+    select {
+        id -> Integer,
+        join -> Integer,
+    }
+}
+
+#[test]
+#[cfg(not(feature = "mysql"))] // FIXME: Figure out how to handle tests that modify schema
+fn selecting_columns_and_tables_with_reserved_names() {
+    use crate::schema_dsl::*;
+    let connection = connection();
+    create_table(
+        "select",
+        (
+            integer("id").primary_key().auto_increment(),
+            integer("join").not_null(),
+        ),
+    )
+    .execute(&connection)
+    .unwrap();
+    connection
+        .execute("INSERT INTO \"select\" (\"join\") VALUES (1), (2), (3)")
+        .unwrap();
+
+    #[derive(Debug, PartialEq, Queryable, Selectable)]
+    #[table_name = "select"]
+    struct Select {
+        join: i32,
+    }
+
+    let expected_data = vec![1, 2, 3]
+        .into_iter()
+        .map(|join| Select { join })
+        .collect::<Vec<_>>();
+    let actual_data: Vec<Select> = select::table
+        .select(Select::as_select())
+        .load(&connection)
+        .unwrap();
+    assert_eq!(expected_data, actual_data);
+}
+
+#[test]
+#[cfg(not(feature = "mysql"))] // FIXME: Figure out how to handle tests that modify schema
+fn selecting_columns_with_different_definition_order() {
+    use crate::schema_dsl::*;
+    let connection = connection();
+    drop_table_cascade(&connection, "users");
+    create_table(
+        "users",
+        (
+            integer("id").primary_key().auto_increment(),
+            string("hair_color"),
+            string("name").not_null(),
+        ),
+    )
+    .execute(&connection)
+    .unwrap();
+    let expected_user = User::with_hair_color(1, "Sean", "black");
+    insert_into(users::table)
+        .values(&NewUser::new("Sean", Some("black")))
+        .execute(&connection)
+        .unwrap();
+    let user_from_select = users::table.select(User::as_select()).first(&connection);
+
+    assert_eq!(Ok(&expected_user), user_from_select.as_ref());
+}
+
+#[test]
+fn selection_using_subselect() {
+    let connection = connection_with_sean_and_tess_in_users_table();
+    let ids: Vec<i32> = users::table.select(users::id).load(&connection).unwrap();
+    let query = format!(
+        "INSERT INTO posts (user_id, title) VALUES ({}, 'Hello'), ({}, 'World')",
+        ids[0], ids[1]
+    );
+    connection.execute(&query).unwrap();
+
+    #[derive(Debug, PartialEq, Queryable, Selectable)]
+    struct Post(#[column_name = "title"] String);
+
+    let users = users::table
+        .filter(users::name.eq("Sean"))
+        .select(users::id);
+    let data = posts::table
+        .select(Post::as_select())
+        .filter(posts::user_id.eq_any(users))
+        .load(&connection)
+        .unwrap();
+
+    assert_eq!(vec![Post("Hello".to_string())], data);
+}
+
+#[test]
+fn select_can_be_called_on_query_that_is_valid_subselect_but_invalid_query() {
+    let connection = connection_with_sean_and_tess_in_users_table();
+    let sean = find_user_by_name("Sean", &connection);
+    let tess = find_user_by_name("Tess", &connection);
+    insert_into(posts::table)
+        .values(&vec![
+            tess.new_post("Tess", None),
+            sean.new_post("Hi", None),
+        ])
+        .execute(&connection)
+        .unwrap();
+
+    let invalid_query_but_valid_subselect = posts::table
+        .select(posts::user_id)
+        .filter(posts::title.eq(users::name));
+    let users_with_post_using_name_as_title = users::table
+        .select(User::as_select())
+        .filter(users::id.eq_any(invalid_query_but_valid_subselect))
+        .load(&connection);
+
+    assert_eq!(Ok(vec![tess]), users_with_post_using_name_as_title);
+}
+
+#[test]
+fn selecting_multiple_aggregate_expressions_without_group_by() {
+    use self::users::dsl::*;
+    use diesel::dsl::{count_star, max, CountStar};
+    use diesel::helper_types::max;
+
+    #[derive(Queryable)]
+    struct CountAndMax {
+        count: i64,
+        max_name: Option<String>,
+    }
+    impl<DB> Selectable<DB> for CountAndMax
+    where
+        DB: diesel::backend::Backend,
+    {
+        type SelectExpression = (CountStar, max<name>);
+
+        fn construct_selection() -> Self::SelectExpression {
+            (count_star(), max(name))
+        }
+    }
+
+    let connection = connection_with_sean_and_tess_in_users_table();
+    let CountAndMax { count, max_name } = users
+        .select(CountAndMax::as_select())
+        .get_result(&connection)
+        .unwrap();
+
+    assert_eq!(2, count);
+    assert_eq!(Some(String::from("Tess")), max_name);
+}
+
+#[test]
+fn mixed_selectable_and_plain_select() {
+    use crate::schema::users::dsl::*;
+
+    let connection = connection_with_sean_and_tess_in_users_table();
+
+    let expected_data = vec![
+        (
+            User {
+                id: 1,
+                name: "Sean".to_string(),
+                hair_color: None,
+            },
+            "Sean".to_string(),
+        ),
+        (
+            User {
+                id: 2,
+                name: "Tess".to_string(),
+                hair_color: None,
+            },
+            "Tess".to_string(),
+        ),
+    ];
+    let actual_data: Vec<_> = users
+        .select((User::as_select(), name))
+        .load(&connection)
+        .unwrap();
+    assert_eq!(expected_data, actual_data);
+}