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

Issue:
MultiConnect Timestamp (NaiveDateTime, PrimitiveDateTime, OffsetDateTime) not working.
<!--
If you want to report a bug, we added some points below which help us track down the problem faster.
-->

## Setup

Simple small program using the MultiConnect derive with the following schema and table.
```rust
pub mod schema {
    table! {
        posts (id) {
            id -> Integer,
            title -> Varchar,
            body -> Text,
            updated -> Timestamp,
        }
    }
}

pub mod models {
    use chrono::NaiveDateTime;

    use crate::schema::posts;
    #[derive(Queryable, Insertable)]
    pub struct Post {
        pub id: i32,
        pub title: String,
        pub body: String,
        pub updated: NaiveDateTime,
    }
}
```

### Versions

- **Rust:** rustc 1.73.0-nightly (180dffba1 2023-08-14)
- **Diesel:** master branch (c99ebda62395a092e059d6ca494861afa559028b)
- **Database:** MultiConnection (sqlite, postgresql, mysql)
- **Operating System:** Linux

### Feature Flags

- **diesel:** "chrono", "r2d2"

## Problem Description
When trying to use `Timestamp` via `NaiveDateTime` using the `MultiConnection` it reports there is no `ToSql`, and probably it will also complain there is no `FromSql`, but i haven't gotten that far yet.

### What are you trying to accomplish?
Using the `MultiConnection` derive feature using a single schema/table layout.

### What is the expected output?
No errors, and a way to be working with at least `NaiveDateTime`.

### What is the actual output?
Compile error.

```
error[E0277]: the trait bound `NaiveDateTime: ToSql<diesel::sql_types::Timestamp, multi_connection_impl::backend::MultiBackend>` is not satisfied
    --> src/main.rs:245:26
     |
245  |                 .execute(conn).expect("Error during inserting user");
     |                  ------- ^^^^ the trait `ToSql<diesel::sql_types::Timestamp, multi_connection_impl::backend::MultiBackend>` is not implemented for `NaiveDateTime`
     |                  |
     |                  required by a bound introduced by this call
     |
     = help: the following other types implement trait `ToSql<A, DB>`:
               <NaiveDateTime as ToSql<diesel::sql_types::Timestamp, Mysql>>
               <NaiveDateTime as ToSql<diesel::sql_types::Timestamp, Pg>>
               <NaiveDateTime as ToSql<diesel::sql_types::Timestamp, Sqlite>>
               <NaiveDateTime as ToSql<diesel::sql_types::Nullable<diesel::sql_types::Timestamp>, __DB>>
               <NaiveDateTime as ToSql<diesel::sql_types::Nullable<Datetime>, __DB>>
               <NaiveDateTime as ToSql<diesel::sql_types::Nullable<diesel::sql_types::Timestamptz>, __DB>>
               <NaiveDateTime as ToSql<Datetime, Mysql>>
               <NaiveDateTime as ToSql<diesel::sql_types::Timestamptz, Pg>>
               <NaiveDateTime as ToSql<TimestamptzSqlite, Sqlite>>
     = note: required for `diesel::expression::bound::Bound<diesel::sql_types::Timestamp, NaiveDateTime>` to implement `QueryFragment<multi_connection_impl::backend::MultiBackend>`
     = note: 1 redundant requirement hidden
     = note: required for `ColumnInsertValue<schema::posts::columns::updated, diesel::expression::bound::Bound<diesel::sql_types::Timestamp, NaiveDateTime>>` to implement `QueryFragment<multi_connection_impl::backend::MultiBackend>`
     = note: required for `ColumnInsertValue<schema::posts::columns::updated, diesel::expression::bound::Bound<diesel::sql_types::Timestamp, NaiveDateTime>>` to implement `InsertValues<schema::posts::table, multi_connection_impl::backend::MultiBackend>`
     = note: 3 redundant requirements hidden
     = note: required for `InsertStatement<table, ValuesClause<(ColumnInsertValue<title, Bound<Text, String>>, ColumnInsertValue<body, Bound<Text, String>>, ColumnInsertValue<published, Bound<Bool, bool>>, ColumnInsertValue<updated, Bound<Timestamp, NaiveDateTime>>), table>>` to implement `QueryFragment<multi_connection_impl::backend::MultiBackend>`
     = note: the full type name has been written to '/home/mathijs/rust/dieselmulticrate/target/debug/deps/dieselmulticrate-ddcbaeaecb4c0ddd.long-type-901884936185368146.txt'
     = note: required for `InsertStatement<table, ValuesClause<(ColumnInsertValue<title, Bound<Text, String>>, ColumnInsertValue<body, Bound<Text, String>>, ColumnInsertValue<published, Bound<Bool, bool>>, ColumnInsertValue<updated, Bound<Timestamp, NaiveDateTime>>), table>>` to implement `ExecuteDsl<InferConnection, multi_connection_impl::backend::MultiBackend>`
     = note: the full type name has been written to '/home/mathijs/rust/dieselmulticrate/target/debug/deps/dieselmulticrate-ddcbaeaecb4c0ddd.long-type-901884936185368146.txt'
note: required by a bound in `diesel::RunQueryDsl::execute`
    --> /home/mathijs/rust/diesel/diesel/src/query_dsl/mod.rs:1432:15
     |
1429 |     fn execute(self, conn: &mut Conn) -> QueryResult<usize>
     |        ------- required by a bound in this associated function
...
1432 |         Self: methods::ExecuteDsl<Conn>,
     | 
```

### Are you seeing any additional errors?
No (not  yet)

### Steps to reproduce

Use the schema/table layout from above.
And this is a create table for Postgresql as an example.
```sql
CREATE TABLE posts (
  id            INTEGER   NOT NULL PRIMARY KEY,
  title         TEXT      NOT NULL,
  body          TEXT      NOT NULL,
  updated       TIMESTAMP NOT NULL
);
```

<!--
Please include as much of your codebase as needed to reproduce the error.  If the relevant files are large, please consider linking to a public repository or a [Gist](https://gist.github.com/). This includes normally the following parts:

* The exact code where your hit the problem
* Relevant parts your schema, so any `table!` macro calls required for
* Any other type definitions involved in the code, which produces your problem
-->

## Checklist

- [x] I have already looked over the [issue tracker](https://github.com/diesel-rs/diesel/issues) and the [discussion forum](https://github.com/diesel-rs/diesel/discussions) for similar possible closed issues.
<!--
If you are unsure if your issue is a duplicate of an existing issue please link the issue in question here
--> 
- [x] This issue can be reproduced on Rust's stable channel. (Your issue will be
  closed if this is not the case)
- [x] This issue can be reproduced without requiring a third party crate

<!--
Thank you for your submission!  You're helping make Diesel more robust 🎉

We'll try to respond as quickly as possible.
-->



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 322d62d727c7..051f715a1e2e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -221,7 +221,7 @@ jobs:
       - name: Test diesel-derives (nightly)
         if: matrix.rust == 'nightly'
         shell: bash
-        run: cargo +${{ matrix.rust }} test --manifest-path diesel_derives/Cargo.toml --no-default-features --features "diesel/${{ matrix.backend }} diesel/unstable"
+        run: cargo +${{ matrix.rust }} test --manifest-path diesel_derives/Cargo.toml --no-default-features --features "diesel/${{ matrix.backend }} diesel/unstable diesel/time time diesel/chrono chrono"
 
       - name: Test diesel-derives
         shell: bash
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ec93bcb08fb3..767602d188c2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,25 @@ Increasing the minimal supported Rust version will always be coupled at least wi
 
 ## Unreleased 
 
+## [2.1.1] 2023-08-xx
+
+## Fixed
+
+* Fixed an issue in diesel-cli that lead to using unquoted table names in one of the internal queries
+* Fixed a bug in `diesel print-schema` that lead to generating invalid `table!` macros if both the `#[sql_name]` and the `#[max_lenght]` attribute are present
+* Fixed an issue in diesel-cli that lead to ignoring certain foreign key constraints for postgresql
+* Fixed an crash while using `diesel print-schema` with really old sqlite versions
+* Fixed an issue where `#[diesel(check_for_backend)]` ignored `#[diesel(deserialize_as)]` attributes
+* Fixed several issues with the new `#[derive(MultiConnection)]` feature
+* Fixed some edge cases in our sqlite timestamp parsing behaviour
+* `diesel migration generate --diff-schema` now respects table filters as setup for `print-schema` via `diesel.toml` 
+* Fixed a potential breaking change around queries containing `DISTINCT ON` and `ORDER BY` clauses consisting of custom sql expressions (e.g. `diesel::dsl::sql`)
+
+## Added 
+
+* Support for bigdecimal 0.4
+
+
 ## [2.1.0] 2023-05-26
 
 ### Changed 
@@ -2018,10 +2037,11 @@ queries or set `PIPES_AS_CONCAT` manually.
 [1.4.8]: https://github.com/diesel-rs/diesel/compare/v1.4.7...v1.4.8
 [2.0.0 Rc0]: https://github.com/diesel-rs/diesel/compare/v.1.4.0...v2.0.0-rc0
 [2.0.0 Rc1]: https://github.com/diesel-rs/diesel/compare/v.2.0.0-rc0...v2.0.0-rc1
-[2.0.0]: https://github.com/diesel-rs/diesel/compare/v.1.4.0...v2.0.0
-[2.0.1]: https://github.com/diesel-rs/diesel/compare/v.2.0.0...v2.0.1
-[2.0.2]: https://github.com/diesel-rs/diesel/compare/v.2.0.1...v2.0.2
+[2.0.0]: https://github.com/diesel-rs/diesel/compare/v1.4.0...v2.0.0
+[2.0.1]: https://github.com/diesel-rs/diesel/compare/v2.0.0...v2.0.1
+[2.0.2]: https://github.com/diesel-rs/diesel/compare/v2.0.1...v2.0.2
 [diesel_derives 2.0.2]: https://github.com/diesel-rs/diesel/compare/v.2.0.2...diesel_derives_v2.0.2
-[2.0.3]: https://github.com/diesel-rs/diesel/compare/v.2.0.2...v2.0.3
-[2.0.4]: https://github.com/diesel-rs/diesel/compare/v.2.0.3...v2.0.4
-[2.1.0]: https://github.com/diesel-rs/diesel/compare/v.2.0.0...v2.1.0
+[2.0.3]: https://github.com/diesel-rs/diesel/compare/v2.0.2...v2.0.3
+[2.0.4]: https://github.com/diesel-rs/diesel/compare/v2.0.3...v2.0.4
+[2.1.0]: https://github.com/diesel-rs/diesel/compare/v2.0.0...v2.1.0
+[2.1.1]: https://github.com/diesel-rs/diesel/compare/v2.1.0...v2.1.1
diff --git a/README.md b/README.md
index da6fde8a2c35..e559f2ccb071 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
-[![](https://diesel.rs/assets/images/diesel_logo_stacked_black.png)](https://diesel.rs)
+[![diesel logo](https://diesel.rs/assets/images/diesel_logo_stacked_black.png)](https://diesel.rs)
+
+# A safe, extensible ORM and Query Builder for Rust
 
-A safe, extensible ORM and Query Builder for Rust
-==========================================================
 [![Build Status](https://github.com/diesel-rs/diesel/workflows/CI%20Tests/badge.svg)](https://github.com/diesel-rs/diesel/actions?query=workflow%3A%22CI+Tests%22+branch%3Amaster)
 [![Gitter](https://badges.gitter.im/diesel-rs/diesel.svg)](https://gitter.im/diesel-rs/diesel?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
 [![Crates.io](https://img.shields.io/crates/v/diesel.svg)](https://crates.io/crates/diesel)
@@ -21,6 +21,7 @@ Supported databases:
 3. [SQLite](https://docs.diesel.rs/master/diesel/sqlite/index.html)
 
 You can configure the database backend in `Cargo.toml`:
+
 ```toml
 [dependencies]
 diesel = { version = "<version>", features = ["<postgres|mysql|sqlite>"] }
@@ -33,19 +34,19 @@ Find our extensive Getting Started tutorial at
 Guides on more specific features are coming soon.
 
 ## Getting help
+
 If you run into problems, Diesel has a very active Gitter room.
 You can come ask for help at
 [gitter.im/diesel-rs/diesel](https://gitter.im/diesel-rs/diesel).
 For help with longer questions and discussion about the future of Diesel,
 open a discussion on [GitHub Discussions](https://github.com/diesel-rs/diesel/discussions).
 
-## Usage 
+## Usage
 
-### Simple queries 
+### Simple queries
 
 Simple queries are a complete breeze. Loading all users from a database:
 
-
 ```rust
 users::table.load(&mut connection)
 ```
@@ -86,6 +87,7 @@ let downloads = version_downloads
 ```
 
 Executed SQL:
+
 ```sql
 SELECT version_downloads.*
   WHERE date > (NOW() - '90 days')
@@ -172,7 +174,6 @@ INSERT INTO users (name, hair_color) VALUES
 
 If you need data from the rows you inserted, just change `execute` to `get_result` or `get_results`. Diesel will take care of the rest.
 
-
 ```rust
 let new_users = vec![
     NewUser { name: "Sean", hair_color: Some("Black") },
@@ -194,6 +195,7 @@ INSERT INTO users (name, hair_color) VALUES
 ```
 
 ### Updating data
+
 Diesel's codegen can generate several ways to update a row, letting you encapsulate your logic in the way that makes sense for your app.
 
 Modifying a struct:
@@ -219,7 +221,7 @@ update(Settings::belonging_to(current_user))
     .execute(&mut connection)
 ```
 
-### Raw SQL 
+### Raw SQL
 
 There will always be certain queries that are just easier to write as raw SQL, or can't be expressed with the query builder. Even in these cases, Diesel provides an easy to use API for writing raw SQL.
 
@@ -242,7 +244,6 @@ sql_query(include_str!("complex_users_by_organization.sql"))
     .load::<User>(&mut conn)?;
 ```
 
-
 ## Code of conduct
 
 Anyone who interacts with Diesel in any space, including but not limited to
@@ -258,6 +259,7 @@ Licensed under either of these:
    https://opensource.org/licenses/MIT)
 
 ### Contributing
+
 Before contributing, please read the [contributors guide](https://github.com/diesel-rs/diesel/blob/master/CONTRIBUTING.md)
 for useful information about setting up Diesel locally, coding style and common abbreviations.
 
diff --git a/diesel/Cargo.toml b/diesel/Cargo.toml
index d2e311f27baa..d90b17e8ca48 100644
--- a/diesel/Cargo.toml
+++ b/diesel/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "diesel"
-version = "2.1.0"
+version = "2.1.1"
 license = "MIT OR Apache-2.0"
 description = "A safe, extensible ORM and Query Builder for PostgreSQL, SQLite, and MySQL"
 readme = "README.md"
@@ -29,7 +29,7 @@ ipnet = { version = "2.5.0", optional = true }
 num-bigint = { version = ">=0.2.0, <0.5.0", optional = true }
 num-traits = { version = "0.2.0", optional = true }
 num-integer = { version = "0.1.39", optional = true }
-bigdecimal = { version = ">=0.0.13, < 0.4.0", optional = true }
+bigdecimal = { version = ">=0.0.13, < 0.5.0", optional = true }
 bitflags = { version = "2.0.0", optional = true }
 r2d2 = { version = ">= 0.8.2, < 0.9.0", optional = true }
 itoa = { version = "1.0.0", optional = true }
@@ -68,6 +68,8 @@ returning_clauses_for_sqlite_3_35 = []
 i-implement-a-third-party-backend-and-opt-into-breaking-changes = []
 nightly-error-messages = []
 r2d2 = ["diesel_derives/r2d2", "dep:r2d2"]
+chrono = ["diesel_derives/chrono", "dep:chrono"]
+time = ["diesel_derives/time", "dep:time"]
 
 [package.metadata.docs.rs]
 features = ["postgres", "mysql", "sqlite", "extras"]
diff --git a/diesel/src/connection/mod.rs b/diesel/src/connection/mod.rs
index 281b8cd3c6c6..79eaf28960f0 100644
--- a/diesel/src/connection/mod.rs
+++ b/diesel/src/connection/mod.rs
@@ -16,8 +16,8 @@ use crate::result::*;
 use std::fmt::Debug;
 
 pub use self::transaction_manager::{
-    AnsiTransactionManager, TransactionDepthChange, TransactionManager, TransactionManagerStatus,
-    ValidTransactionManagerStatus,
+    AnsiTransactionManager, InTransactionStatus, TransactionDepthChange, TransactionManager,
+    TransactionManagerStatus, ValidTransactionManagerStatus,
 };
 
 #[diesel_derives::__diesel_public_if(
diff --git a/diesel/src/connection/transaction_manager.rs b/diesel/src/connection/transaction_manager.rs
index 05ce81996c2b..506ff5ad3b7c 100644
--- a/diesel/src/connection/transaction_manager.rs
+++ b/diesel/src/connection/transaction_manager.rs
@@ -170,13 +170,29 @@ impl TransactionManagerStatus {
         *self = TransactionManagerStatus::InError
     }
 
-    fn transaction_state(&mut self) -> QueryResult<&mut ValidTransactionManagerStatus> {
+    /// Expose access to the inner transaction state
+    ///
+    /// This function returns an error if the Transaction manager is in a broken
+    /// state
+    #[diesel_derives::__diesel_public_if(
+        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
+    )]
+    pub(self) fn transaction_state(&mut self) -> QueryResult<&mut ValidTransactionManagerStatus> {
         match self {
             TransactionManagerStatus::Valid(valid_status) => Ok(valid_status),
             TransactionManagerStatus::InError => Err(Error::BrokenTransactionManager),
         }
     }
 
+    /// This function allows to flag a transaction manager
+    /// in such a way that it contains a test transaction.
+    ///
+    /// This will disable some checks in regards to open transactions
+    /// to allow `Connection::begin_test_transaction` to work with
+    /// pooled connections as well
+    #[diesel_derives::__diesel_public_if(
+        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
+    )]
     pub(crate) fn set_test_transaction_flag(&mut self) {
         if let TransactionManagerStatus::Valid(ValidTransactionManagerStatus {
             in_transaction: Some(s),
@@ -190,17 +206,34 @@ impl TransactionManagerStatus {
 /// Valid transaction status for the manager. Can return the current transaction depth
 #[allow(missing_copy_implementations)]
 #[derive(Debug, Default)]
+#[diesel_derives::__diesel_public_if(
+    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
+    public_fields(in_transaction)
+)]
 pub struct ValidTransactionManagerStatus {
+    /// Inner status, or `None` if no transaction is runnin
     in_transaction: Option<InTransactionStatus>,
 }
 
+/// Various status fields to track the status of
+/// a transaction manager with a started transaction
 #[allow(missing_copy_implementations)]
 #[derive(Debug)]
-struct InTransactionStatus {
+#[diesel_derives::__diesel_public_if(
+    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
+    public_fields(
+        test_transaction,
+        transaction_depth,
+        requires_rollback_maybe_up_to_top_level
+    )
+)]
+pub struct InTransactionStatus {
+    /// The current depth of nested transactions
     transaction_depth: NonZeroU32,
     /// If that is registered, savepoints rollbacks will still be attempted, but failure to do so
     /// will not result in an error. (Some may succeed, some may not.)
     requires_rollback_maybe_up_to_top_level: bool,
+    /// Is this transaction manager status marked as test-transaction?
     test_transaction: bool,
 }
 
diff --git a/diesel/src/internal/derives.rs b/diesel/src/internal/derives.rs
index be8929c8e486..216936cf6ac0 100644
--- a/diesel/src/internal/derives.rs
+++ b/diesel/src/internal/derives.rs
@@ -69,4 +69,12 @@ pub mod multiconnection {
 
     #[doc(hidden)]
     pub use crate::query_builder::select_statement::SelectStatementAccessor;
+
+    #[doc(hidden)]
+    #[cfg(feature = "chrono")]
+    pub use chrono;
+
+    #[doc(hidden)]
+    #[cfg(feature = "time")]
+    pub use time;
 }
diff --git a/diesel/src/mysql/connection/bind.rs b/diesel/src/mysql/connection/bind.rs
index 90c5b55d721e..3b002d7e2a27 100644
--- a/diesel/src/mysql/connection/bind.rs
+++ b/diesel/src/mysql/connection/bind.rs
@@ -734,9 +734,6 @@ fn known_buffer_size_for_ffi_type(tpe: ffi::enum_field_types) -> Option<usize> {
 
 #[cfg(test)]
 mod tests {
-    #[cfg(feature = "bigdecimal")]
-    use bigdecimal::FromPrimitive;
-
     use super::MysqlValue;
     use super::*;
     use crate::connection::statement_cache::MaybeCached;
@@ -744,6 +741,8 @@ mod tests {
     use crate::mysql::connection::stmt::Statement;
     use crate::prelude::*;
     use crate::sql_types::*;
+    #[cfg(feature = "bigdecimal")]
+    use std::str::FromStr;
 
     fn to_value<ST, T>(
         bind: &BindData,
@@ -935,7 +934,7 @@ mod tests {
         assert!(!numeric_col.flags.contains(Flags::UNSIGNED_FLAG));
         assert_eq!(
             to_value::<Numeric, bigdecimal::BigDecimal>(numeric_col).unwrap(),
-            bigdecimal::BigDecimal::from_f32(-999.999).unwrap()
+            bigdecimal::BigDecimal::from_str("-999.99900").unwrap()
         );
 
         let decimal_col = &results[8].0;
@@ -947,7 +946,7 @@ mod tests {
         assert!(!decimal_col.flags.contains(Flags::UNSIGNED_FLAG));
         assert_eq!(
             to_value::<Numeric, bigdecimal::BigDecimal>(decimal_col).unwrap(),
-            bigdecimal::BigDecimal::from_f32(3.14).unwrap()
+            bigdecimal::BigDecimal::from_str("3.14000").unwrap()
         );
 
         let float_col = &results[9].0;
diff --git a/diesel/src/pg/query_builder/distinct_on.rs b/diesel/src/pg/query_builder/distinct_on.rs
index 0177c8652cb3..63652575d972 100644
--- a/diesel/src/pg/query_builder/distinct_on.rs
+++ b/diesel/src/pg/query_builder/distinct_on.rs
@@ -7,6 +7,7 @@ use crate::query_builder::{
 use crate::query_dsl::methods::DistinctOnDsl;
 use crate::query_dsl::order_dsl::ValidOrderingForDistinct;
 use crate::result::QueryResult;
+use crate::sql_types::SingleValue;
 use crate::QuerySource;
 use diesel::query_builder::order_clause::OrderClause;
 
@@ -17,23 +18,79 @@ pub struct DistinctOnClause<T>(pub(crate) T);
 
 impl<T> ValidOrderingForDistinct<DistinctOnClause<T>> for NoOrderClause {}
 impl<T> ValidOrderingForDistinct<DistinctOnClause<T>> for OrderClause<(T,)> {}
-impl<T> ValidOrderingForDistinct<DistinctOnClause<T>> for OrderClause<T> where T: crate::Column {}
-impl<T> ValidOrderingForDistinct<DistinctOnClause<T>> for OrderClause<crate::helper_types::Desc<T>> where
-    T: crate::Column
+impl<T> ValidOrderingForDistinct<DistinctOnClause<T>> for OrderClause<T> where T: crate::Expression {}
+
+impl<T> ValidOrderingForDistinct<DistinctOnClause<T>>
+    for OrderClause<crate::expression::operators::Asc<T>>
+where
+    T: crate::Expression,
+    T::SqlType: SingleValue,
 {
 }
-impl<T> ValidOrderingForDistinct<DistinctOnClause<T>> for OrderClause<crate::helper_types::Asc<T>> where
-    T: crate::Column
+
+impl<T> ValidOrderingForDistinct<DistinctOnClause<T>>
+    for OrderClause<crate::expression::operators::Desc<T>>
+where
+    T: crate::Expression,
+    T::SqlType: SingleValue,
 {
 }
+
 macro_rules! valid_ordering {
+    // Special-case: rule out the all ident case:
+    (@impl_one:
+     $generics:tt
+     [$($T_:ident, )*]
+    ) => {
+        /* skip this one */
+    };
+    (@impl_one:
+     [generics: $($T:ident)*]
+     [$($Ty:ty, )*]
+    ) => {
+        impl<$($T,)*> ValidOrderingForDistinct<DistinctOnClause<($($T, )*)>>
+            for OrderClause<($($Ty, )*)>
+        {}
+    };
+    (
+        @perm:
+        $generics:tt
+        [acc: $([$($acc:tt)*])*]
+        $T:ident
+        $($rest:tt)*
+    ) => {
+        valid_ordering! {
+            @perm:
+            $generics
+                [acc:
+                 $(
+                     [$($acc)* crate::expression::operators::Asc<$T>, ]
+                     [$($acc)*     $T  , ]
+                     [$($acc)* crate::expression::operators::Desc<$T>, ]
+                 )*
+                ]
+                $($rest)*
+        }
+    };
+    (
+        @perm:
+        $generics:tt
+        [acc: $($Tys:tt)*]
+        /* nothing left */
+    ) => (
+        $(
+            valid_ordering! {@impl_one:
+                $generics
+                $Tys
+            }
+        )*
+    );
     (@skip: ($ST1: ident, $($ST:ident,)*), $T1:ident, ) => {};
     (@skip: ($ST1: ident, $($ST:ident,)*), $T1:ident, $($T:ident,)+) => {
         valid_ordering!(($($ST,)*), ($ST1,), $($T,)*);
     };
     (($ST1: ident,), ($($OT:ident,)*), $T1:ident,) => {
-        #[allow(unused_parens)]
-        impl<$T1, $ST1, $($OT,)*> ValidOrderingForDistinct<DistinctOnClause<($ST1, $($OT,)*)>> for OrderClause<($T1)>
+        impl<$T1, $ST1, $($OT,)*> ValidOrderingForDistinct<DistinctOnClause<($ST1, $($OT,)*)>> for OrderClause<$T1>
         where $T1: crate::pg::OrderDecorator<Column = $ST1>,
         {}
         impl<$T1, $ST1, $($OT,)*> ValidOrderingForDistinct<DistinctOnClause<($ST1, $($OT,)*)>> for OrderClause<($T1,)>
@@ -65,10 +122,8 @@ macro_rules! valid_ordering {
         }
     )+) => {
         $(
-            impl<$($T,)* $($ST,)*> ValidOrderingForDistinct<DistinctOnClause<($($T,)*)>> for OrderClause<($($ST,)*)>
-            where $($ST: crate::pg::OrderDecorator<Column = $T>,)*
-            {}
             valid_ordering!(@skip: ($($ST,)*), $($T,)*);
+            valid_ordering!(@perm: [generics: $($T)*] [acc: []] $($T)*);
         )*
     }
 }
diff --git a/diesel/src/query_builder/ast_pass.rs b/diesel/src/query_builder/ast_pass.rs
index ffe8bc60385a..9e4de8fc37ed 100644
--- a/diesel/src/query_builder/ast_pass.rs
+++ b/diesel/src/query_builder/ast_pass.rs
@@ -227,6 +227,14 @@ where
         Ok(())
     }
 
+    /// Push a value onto the given query to be sent separate from the SQL
+    ///
+    /// This method affects multiple AST passes. It should be called at the
+    /// point in the raw SQL is inserted. This assumes the parameter placeholder
+    /// (`$1` on PG, `?` on other backends) is already inserted.
+    #[diesel_derives::__diesel_public_if(
+        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
+    )]
     pub(crate) fn push_bind_param_value_only<T, U>(&mut self, bind: &'b U) -> QueryResult<()>
     where
         DB: HasSqlType<T>,
diff --git a/diesel/src/query_dsl/mod.rs b/diesel/src/query_dsl/mod.rs
index cf30d40005ae..a8c7eab5b0a8 100644
--- a/diesel/src/query_dsl/mod.rs
+++ b/diesel/src/query_dsl/mod.rs
@@ -173,7 +173,7 @@ pub trait QueryDsl: Sized {
     ///                    Animal::new("spider", None, 8)]), distinct_animals);
     /// # }
     /// ```
-    #[cfg(feature = "postgres")]
+    #[cfg(feature = "postgres_backend")]
     fn distinct_on<Expr>(self, expr: Expr) -> DistinctOn<Self, Expr>
     where
         Self: methods::DistinctOnDsl<Expr>,
diff --git a/diesel/src/row.rs b/diesel/src/row.rs
index 02d48ea07859..a48c697c6bc1 100644
--- a/diesel/src/row.rs
+++ b/diesel/src/row.rs
@@ -10,6 +10,7 @@ use std::ops::Range;
 pub use self::private::{PartialRow, RowSealed};
 
 #[cfg(not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))]
+#[allow(unused_imports)]
 pub(crate) use self::private::{PartialRow, RowSealed};
 
 /// Representing a way to index into database rows
diff --git a/diesel/src/sql_types/mod.rs b/diesel/src/sql_types/mod.rs
index e8f7b666648a..d74b4d0fb968 100644
--- a/diesel/src/sql_types/mod.rs
+++ b/diesel/src/sql_types/mod.rs
@@ -332,14 +332,14 @@ pub struct Time;
 /// - [`std::time::SystemTime`][SystemTime] (PG only)
 /// - [`chrono::NaiveDateTime`][NaiveDateTime] with `feature = "chrono"`
 /// - [`time::PrimitiveDateTime`] with `feature = "time"`
-/// - [`time::OffsetDateTime`] with `feature = "time"`
+/// - [`time::OffsetDateTime`] with `feature = "time"` (MySQL only)
 ///
 /// ### [`FromSql`](crate::deserialize::FromSql) impls
 ///
 /// - [`std::time::SystemTime`][SystemTime] (PG only)
 /// - [`chrono::NaiveDateTime`][NaiveDateTime] with `feature = "chrono"`
 /// - [`time::PrimitiveDateTime`] with `feature = "time"`
-/// - [`time::OffsetDateTime`] with `feature = "time"`
+/// - [`time::OffsetDateTime`] with `feature = "time"` (MySQL only)
 ///
 /// [SystemTime]: std::time::SystemTime
 #[cfg_attr(
diff --git a/diesel/src/sqlite/connection/raw.rs b/diesel/src/sqlite/connection/raw.rs
index 06517a9e0f47..43d1671b7eee 100644
--- a/diesel/src/sqlite/connection/raw.rs
+++ b/diesel/src/sqlite/connection/raw.rs
@@ -579,5 +579,5 @@ where
 
 extern "C" fn destroy_boxed<F>(data: *mut libc::c_void) {
     let ptr = data as *mut F;
-    unsafe { Box::from_raw(ptr) };
+    unsafe { std::mem::drop(Box::from_raw(ptr)) };
 }
diff --git a/diesel/src/sqlite/types/date_and_time/chrono.rs b/diesel/src/sqlite/types/date_and_time/chrono.rs
index 74f859fba0e0..5b57bea681fe 100644
--- a/diesel/src/sqlite/types/date_and_time/chrono.rs
+++ b/diesel/src/sqlite/types/date_and_time/chrono.rs
@@ -1,4 +1,5 @@
 extern crate chrono;
+
 use self::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
 
 use crate::backend::Backend;
@@ -7,13 +8,81 @@ use crate::serialize::{self, IsNull, Output, ToSql};
 use crate::sql_types::{Date, Time, Timestamp, TimestamptzSqlite};
 use crate::sqlite::Sqlite;
 
-const SQLITE_DATE_FORMAT: &str = "%F";
+/// Warning to future editors:
+/// Changes in the following formats need to be kept in sync
+/// with the formats of the ["time"](super::time) module.
+/// We do not need a distinction between whole second and
+/// subsecond since %.f will only print the dot if needed.
+/// We always print as many subsecond as his given to us,
+/// this means the subsecond part can be 3, 6 or 9 digits.
+const DATE_FORMAT: &str = "%F";
+
+const ENCODE_TIME_FORMAT: &str = "%T%.f";
+
+const TIME_FORMATS: [&str; 9] = [
+    // Most likely formats
+    "%T%.f", "%T", // All other valid formats in order of increasing specificity
+    "%R", "%RZ", "%R%:z", "%TZ", "%T%:z", "%T%.fZ", "%T%.f%:z",
+];
+
+const ENCODE_NAIVE_DATETIME_FORMAT: &str = "%F %T%.f";
+
+const ENCODE_DATETIME_FORMAT: &str = "%F %T%.f%:z";
+
+const NAIVE_DATETIME_FORMATS: [&str; 18] = [
+    // Most likely formats
+    "%F %T%.f",
+    "%F %T%.f%:z",
+    "%F %T",
+    "%F %T%:z",
+    // All other formats in order of increasing specificity
+    "%F %R",
+    "%F %RZ",
+    "%F %R%:z",
+    "%F %TZ",
+    "%F %T%.fZ",
+    "%FT%R",
+    "%FT%RZ",
+    "%FT%R%:z",
+    "%FT%T",
+    "%FT%TZ",
+    "%FT%T%:z",
+    "%FT%T%.f",
+    "%FT%T%.fZ",
+    "%FT%T%.f%:z",
+];
+
+const DATETIME_FORMATS: [&str; 12] = [
+    // Most likely formats
+    "%F %T%.f%:z",
+    "%F %T%:z",
+    // All other formats in order of increasing specificity
+    "%F %RZ",
+    "%F %R%:z",
+    "%F %TZ",
+    "%F %T%.fZ",
+    "%FT%RZ",
+    "%FT%R%:z",
+    "%FT%TZ",
+    "%FT%T%:z",
+    "%FT%T%.fZ",
+    "%FT%T%.f%:z",
+];
+
+fn parse_julian(julian_days: f64) -> Option<NaiveDateTime> {
+    const EPOCH_IN_JULIAN_DAYS: f64 = 2_440_587.5;
+    const SECONDS_IN_DAY: f64 = 86400.0;
+    let timestamp = (julian_days - EPOCH_IN_JULIAN_DAYS) * SECONDS_IN_DAY;
+    let seconds = timestamp as i64;
+    let nanos = (timestamp.fract() * 1E9) as u32;
+    NaiveDateTime::from_timestamp_opt(seconds, nanos)
+}
 
 #[cfg(all(feature = "sqlite", feature = "chrono"))]
 impl FromSql<Date, Sqlite> for NaiveDate {
     fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
         value
-            .parse_string(|s| Self::parse_from_str(s, SQLITE_DATE_FORMAT))
+            .parse_string(|s| Self::parse_from_str(s, DATE_FORMAT))
             .map_err(Into::into)
     }
 }
@@ -21,7 +90,7 @@ impl FromSql<Date, Sqlite> for NaiveDate {
 #[cfg(all(feature = "sqlite", feature = "chrono"))]
 impl ToSql<Date, Sqlite> for NaiveDate {
     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
-        out.set_value(self.format(SQLITE_DATE_FORMAT).to_string());
+        out.set_value(self.format(DATE_FORMAT).to_string());
         Ok(IsNull::No)
     }
 }
@@ -30,13 +99,7 @@ impl ToSql<Date, Sqlite> for NaiveDate {
 impl FromSql<Time, Sqlite> for NaiveTime {
     fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
         value.parse_string(|text| {
-            let valid_time_formats = &[
-                // Most likely
-                "%T%.f", // All other valid formats in order of documentation
-                "%R", "%RZ", "%T%.fZ", "%R%:z", "%T%.f%:z",
-            ];
-
-            for format in valid_time_formats {
+            for format in TIME_FORMATS {
                 if let Ok(time) = Self::parse_from_str(text, format) {
                     return Ok(time);
                 }
@@ -50,7 +113,7 @@ impl FromSql<Time, Sqlite> for NaiveTime {
 #[cfg(all(feature = "sqlite", feature = "chrono"))]
 impl ToSql<Time, Sqlite> for NaiveTime {
     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
-        out.set_value(self.format("%T%.f").to_string());
+        out.set_value(self.format(ENCODE_TIME_FORMAT).to_string());
         Ok(IsNull::No)
     }
 }
@@ -59,36 +122,14 @@ impl ToSql<Time, Sqlite> for NaiveTime {
 impl FromSql<Timestamp, Sqlite> for NaiveDateTime {
     fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
         value.parse_string(|text| {
-            let sqlite_datetime_formats = &[
-                // Most likely format
-                "%F %T%.f",
-                // Other formats in order of appearance in docs
-                "%F %R",
-                "%F %RZ",
-                "%F %R%:z",
-                "%F %T%.fZ",
-                "%F %T%.f%:z",
-                "%FT%R",
-                "%FT%RZ",
-                "%FT%R%:z",
-                "%FT%T%.f",
-                "%FT%T%.fZ",
-                "%FT%T%.f%:z",
-            ];
-
-            for format in sqlite_datetime_formats {
+            for format in NAIVE_DATETIME_FORMATS {
                 if let Ok(dt) = Self::parse_from_str(text, format) {
                     return Ok(dt);
                 }
             }
 
             if let Ok(julian_days) = text.parse::<f64>() {
-                let epoch_in_julian_days = 2_440_587.5;
-                let seconds_in_day = 86400.0;
-                let timestamp = (julian_days - epoch_in_julian_days) * seconds_in_day;
-                let seconds = timestamp as i64;
-                let nanos = (timestamp.fract() * 1E9) as u32;
-                if let Some(timestamp) = Self::from_timestamp_opt(seconds, nanos) {
+                if let Some(timestamp) = parse_julian(julian_days) {
                     return Ok(timestamp);
                 }
             }
@@ -101,7 +142,7 @@ impl FromSql<Timestamp, Sqlite> for NaiveDateTime {
 #[cfg(all(feature = "sqlite", feature = "chrono"))]
 impl ToSql<Timestamp, Sqlite> for NaiveDateTime {
     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
-        out.set_value(self.format("%F %T%.f").to_string());
+        out.set_value(self.format(ENCODE_NAIVE_DATETIME_FORMAT).to_string());
         Ok(IsNull::No)
     }
 }
@@ -110,34 +151,14 @@ impl ToSql<Timestamp, Sqlite> for NaiveDateTime {
 impl FromSql<TimestamptzSqlite, Sqlite> for NaiveDateTime {
     fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
         value.parse_string(|text| {
-            let sqlite_datetime_formats = &[
-                "%F %RZ",
-                "%F %R%:z",
-                "%F %TZ",
-                "%F %T%:z",
-                "%F %T%.fZ",
-                "%F %T%.f%:z",
-                "%FT%RZ",
-                "%FT%R%:z",
-                "%FT%TZ",
-                "%FT%T%:z",
-                "%FT%T%.fZ",
-                "%FT%T%.f%:z",
-            ];
-
-            for format in sqlite_datetime_formats {
+            for format in NAIVE_DATETIME_FORMATS {
                 if let Ok(dt) = Self::parse_from_str(text, format) {
                     return Ok(dt);
                 }
             }
 
             if let Ok(julian_days) = text.parse::<f64>() {
-                let epoch_in_julian_days = 2_440_587.5;
-                let seconds_in_day = 86400.0;
-                let timestamp = (julian_days - epoch_in_julian_days) * seconds_in_day;
-                let seconds = timestamp as i64;
-                let nanos = (timestamp.fract() * 1E9) as u32;
-                if let Some(timestamp) = Self::from_timestamp_opt(seconds, nanos) {
+                if let Some(timestamp) = parse_julian(julian_days) {
                     return Ok(timestamp);
                 }
             }
@@ -150,7 +171,7 @@ impl FromSql<TimestamptzSqlite, Sqlite> for NaiveDateTime {
 #[cfg(all(feature = "sqlite", feature = "chrono"))]
 impl ToSql<TimestamptzSqlite, Sqlite> for NaiveDateTime {
     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
-        out.set_value(self.format("%F %T%.f+00:00").to_string());
+        out.set_value(self.format(ENCODE_NAIVE_DATETIME_FORMAT).to_string());
         Ok(IsNull::No)
     }
 }
@@ -158,6 +179,20 @@ impl ToSql<TimestamptzSqlite, Sqlite> for NaiveDateTime {
 #[cfg(all(feature = "sqlite", feature = "chrono"))]
 impl FromSql<TimestamptzSqlite, Sqlite> for DateTime<Utc> {
     fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
+        // First try to parse the timezone
+        if let Ok(dt) = value.parse_string(|text| {
+            for format in DATETIME_FORMATS {
+                if let Ok(dt) = DateTime::parse_from_str(text, format) {
+                    return Ok(dt.with_timezone(&Utc));
+                }
+            }
+
+            Err(())
+        }) {
+            return Ok(dt);
+        }
+
+        // Fallback on assuming Utc
         let naive_date_time =
             <NaiveDateTime as FromSql<TimestamptzSqlite, Sqlite>>::from_sql(value)?;
         Ok(DateTime::from_utc(naive_date_time, Utc))
@@ -167,6 +202,20 @@ impl FromSql<TimestamptzSqlite, Sqlite> for DateTime<Utc> {
 #[cfg(all(feature = "sqlite", feature = "chrono"))]
 impl FromSql<TimestamptzSqlite, Sqlite> for DateTime<Local> {
     fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
+        // First try to parse the timezone
+        if let Ok(dt) = value.parse_string(|text| {
+            for format in DATETIME_FORMATS {
+                if let Ok(dt) = DateTime::parse_from_str(text, format) {
+                    return Ok(dt.with_timezone(&Local));
+                }
+            }
+
+            Err(())
+        }) {
+            return Ok(dt);
+        }
+
+        // Fallback on assuming Local
         let naive_date_time =
             <NaiveDateTime as FromSql<TimestamptzSqlite, Sqlite>>::from_sql(value)?;
         Ok(Local::from_utc_datetime(&Local, &naive_date_time))
@@ -176,7 +225,9 @@ impl FromSql<TimestamptzSqlite, Sqlite> for DateTime<Local> {
 #[cfg(all(feature = "sqlite", feature = "chrono"))]
 impl<TZ: TimeZone> ToSql<TimestamptzSqlite, Sqlite> for DateTime<TZ> {
     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
-        out.set_value(self.naive_utc().format("%F %T%.f+00:00").to_string());
+        // Converting to UTC ensures consistency
+        let dt_utc = self.with_timezone(&Utc);
+        out.set_value(dt_utc.format(ENCODE_DATETIME_FORMAT).to_string());
         Ok(IsNull::No)
     }
 }
@@ -636,22 +687,6 @@ mod tests {
             "1970-01-01T00:00:00+00:00",
             "1970-01-01T00:00:00.000+00:00",
             "1970-01-01T00:00:00.000000+00:00",
-            "1970-01-01 00:00+01:00",
-            "1970-01-01 00:00:00+01:00",
-            "1970-01-01 00:00:00.000+01:00",
-            "1970-01-01 00:00:00.000000+01:00",
-            "1970-01-01T00:00+01:00",
-            "1970-01-01T00:00:00+01:00",
-            "1970-01-01T00:00:00.000+01:00",
-            "1970-01-01T00:00:00.000000+01:00",
-            "1970-01-01T00:00-01:00",
-            "1970-01-01T00:00:00-01:00",
-            "1970-01-01T00:00:00.000-01:00",
-            "1970-01-01T00:00:00.000000-01:00",
-            "1970-01-01T00:00-01:00",
-            "1970-01-01T00:00:00-01:00",
-            "1970-01-01T00:00:00.000-01:00",
-            "1970-01-01T00:00:00.000000-01:00",
             "2440587.5",
         ];
 
diff --git a/diesel/src/sqlite/types/date_and_time/mod.rs b/diesel/src/sqlite/types/date_and_time/mod.rs
index 98facfd4387c..3b4a0617873e 100644
--- a/diesel/src/sqlite/types/date_and_time/mod.rs
+++ b/diesel/src/sqlite/types/date_and_time/mod.rs
@@ -92,3 +92,312 @@ impl ToSql<sql_types::TimestamptzSqlite, Sqlite> for String {
         <str as ToSql<sql_types::TimestamptzSqlite, Sqlite>>::to_sql(self as &str, out)
     }
 }
+
+#[cfg(all(test, feature = "chrono", feature = "time"))]
+mod tests {
+    extern crate chrono;
+    extern crate time;
+
+    use chrono::{
+        DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc,
+    };
+    use time::{
+        macros::{date, datetime, offset, time},
+        Date, OffsetDateTime, PrimitiveDateTime, Time,
+    };
+
+    use crate::insert_into;
+    use crate::prelude::*;
+    use crate::test_helpers::connection;
+
+    crate::table! {
+        table_timestamp_tz(id) {
+            id -> Integer,
+            timestamp_with_tz -> TimestamptzSqlite,
+        }
+    }
+    crate::table! {
+      table_timestamp(id) {
+          id -> Integer,
+          timestamp -> Timestamp
+      }
+    }
+    crate::table! {
+        table_date(id) {
+            id -> Integer,
+            date -> Date
+        }
+    }
+    crate::table! {
+        table_time(id) {
+            id -> Integer,
+            time -> Time
+        }
+    }
+
+    fn eq_date(left: Date, right: NaiveDate) -> bool {
+        left.year() == right.year()
+            && left.month() as u8 == right.month() as u8
+            && left.day() == right.day() as u8
+    }
+
+    fn eq_time(left: Time, right: NaiveTime) -> bool {
+        left.hour() == right.hour() as u8
+            && left.minute() == right.minute() as u8
+            && left.second() == right.second() as u8
+            && left.nanosecond() == right.nanosecond()
+    }
+
+    fn eq_datetime(left: PrimitiveDateTime, right: NaiveDateTime) -> bool {
+        eq_date(left.date(), right.date()) && eq_time(left.time(), right.time())
+    }
+
+    fn eq_datetime_utc(left: OffsetDateTime, right: DateTime<Utc>) -> bool {
+        left.unix_timestamp_nanos() == right.timestamp_nanos() as i128
+    }
+
+    fn eq_datetime_offset(left: OffsetDateTime, right: DateTime<FixedOffset>) -> bool {
+        left.unix_timestamp_nanos() == right.timestamp_nanos() as i128
+    }
+
+    fn create_tables(conn: &mut SqliteConnection) {
+        crate::sql_query(
+            "CREATE TABLE table_timestamp_tz(id INTEGER PRIMARY KEY, timestamp_with_tz TEXT);",
+        )
+        .execute(conn)
+        .unwrap();
+
+        crate::sql_query("CREATE TABLE table_timestamp(id INTEGER PRIMARY KEY, timestamp TEXT);")
+            .execute(conn)
+            .unwrap();
+
+        crate::sql_query("CREATE TABLE table_date(id INTEGER PRIMARY KEY, date TEXT);")
+            .execute(conn)
+            .unwrap();
+
+        crate::sql_query("CREATE TABLE table_time(id INTEGER PRIMARY KEY, time TEXT);")
+            .execute(conn)
+            .unwrap();
+    }
+
+    #[test]
+    fn time_to_chrono_date() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = date!(2000 - 1 - 1);
+
+        insert_into(table_date::table)
+            .values(vec![(table_date::id.eq(1), table_date::date.eq(original))])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_date::table
+            .select(table_date::date)
+            .get_result::<NaiveDate>(conn)
+            .unwrap();
+
+        assert!(eq_date(original, translated))
+    }
+
+    #[test]
+    fn chrono_to_time_date() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
+
+        insert_into(table_date::table)
+            .values(vec![(table_date::id.eq(1), table_date::date.eq(original))])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_date::table
+            .select(table_date::date)
+            .get_result::<Date>(conn)
+            .unwrap();
+
+        assert!(eq_date(translated, original))
+    }
+
+    #[test]
+    fn time_to_chrono_time() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = time!(1:1:1.001);
+
+        insert_into(table_time::table)
+            .values(vec![(table_time::id.eq(1), table_time::time.eq(original))])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_time::table
+            .select(table_time::time)
+            .get_result::<NaiveTime>(conn)
+            .unwrap();
+
+        assert!(eq_time(original, translated))
+    }
+
+    #[test]
+    fn chrono_to_time_time() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = NaiveTime::from_hms_milli_opt(1, 1, 1, 1).unwrap();
+
+        insert_into(table_time::table)
+            .values(vec![(table_time::id.eq(1), table_time::time.eq(original))])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_time::table
+            .select(table_time::time)
+            .get_result::<Time>(conn)
+            .unwrap();
+
+        assert!(eq_time(translated, original))
+    }
+
+    #[test]
+    fn time_to_chrono_datetime() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = datetime!(2000-1-1 1:1:1.001);
+
+        insert_into(table_timestamp::table)
+            .values(vec![(
+                table_timestamp::id.eq(1),
+                table_timestamp::timestamp.eq(original),
+            )])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_timestamp::table
+            .select(table_timestamp::timestamp)
+            .get_result::<NaiveDateTime>(conn)
+            .unwrap();
+
+        assert!(eq_datetime(original, translated))
+    }
+
+    #[test]
+    fn chrono_to_time_datetime() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = NaiveDate::from_ymd_opt(2000, 1, 1)
+            .unwrap()
+            .and_hms_milli_opt(1, 1, 1, 1)
+            .unwrap();
+
+        insert_into(table_timestamp::table)
+            .values(vec![(
+                table_timestamp::id.eq(1),
+                table_timestamp::timestamp.eq(original),
+            )])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_timestamp::table
+            .select(table_timestamp::timestamp)
+            .get_result::<PrimitiveDateTime>(conn)
+            .unwrap();
+
+        assert!(eq_datetime(translated, original))
+    }
+
+    #[test]
+    fn chrono_to_time_datetime_utc() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = Utc::now();
+
+        insert_into(table_timestamp_tz::table)
+            .values(vec![(
+                table_timestamp_tz::id.eq(1),
+                table_timestamp_tz::timestamp_with_tz.eq(original),
+            )])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_timestamp_tz::table
+            .select(table_timestamp_tz::timestamp_with_tz)
+            .get_result::<OffsetDateTime>(conn)
+            .unwrap();
+
+        assert!(eq_datetime_utc(translated, original))
+    }
+
+    #[test]
+    fn time_to_chrono_datetime_utc() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = OffsetDateTime::now_utc();
+
+        insert_into(table_timestamp_tz::table)
+            .values(vec![(
+                table_timestamp_tz::id.eq(1),
+                table_timestamp_tz::timestamp_with_tz.eq(original),
+            )])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_timestamp_tz::table
+            .select(table_timestamp_tz::timestamp_with_tz)
+            .get_result::<DateTime<Utc>>(conn)
+            .unwrap();
+
+        assert!(eq_datetime_utc(original, translated))
+    }
+
+    #[test]
+    fn chrono_to_time_datetime_timezone() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = Utc::now().with_timezone(&FixedOffset::east_opt(5 * 3600).unwrap());
+
+        insert_into(table_timestamp_tz::table)
+            .values(vec![(
+                table_timestamp_tz::id.eq(1),
+                table_timestamp_tz::timestamp_with_tz.eq(original),
+            )])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_timestamp_tz::table
+            .select(table_timestamp_tz::timestamp_with_tz)
+            .get_result::<OffsetDateTime>(conn)
+            .unwrap();
+
+        assert!(eq_datetime_offset(translated, original))
+    }
+
+    #[test]
+    fn time_to_chrono_datetime_offset() {
+        let conn = &mut connection();
+        create_tables(conn);
+
+        let original = OffsetDateTime::now_utc().to_offset(offset!(+5));
+
+        insert_into(table_timestamp_tz::table)
+            .values(vec![(
+                table_timestamp_tz::id.eq(1),
+                table_timestamp_tz::timestamp_with_tz.eq(original),
+            )])
+            .execute(conn)
+            .unwrap();
+
+        let translated = table_timestamp_tz::table
+            .select(table_timestamp_tz::timestamp_with_tz)
+            .get_result::<DateTime<Utc>>(conn)
+            .unwrap();
+
+        assert!(eq_datetime_utc(original, translated))
+    }
+}
diff --git a/diesel/src/sqlite/types/date_and_time/time.rs b/diesel/src/sqlite/types/date_and_time/time.rs
index be71b7f266c3..41c993f6bb92 100644
--- a/diesel/src/sqlite/types/date_and_time/time.rs
+++ b/diesel/src/sqlite/types/date_and_time/time.rs
@@ -14,23 +14,30 @@ use crate::serialize::{self, IsNull, Output, ToSql};
 use crate::sql_types::{Date, Time, Timestamp, TimestamptzSqlite};
 use crate::sqlite::Sqlite;
 
+/// Warning to future editors:
+/// Changes in the following formats need to be kept in sync
+/// with the formats of the ["chrono"](super::chrono) module.
+/// We need a distinction between whole second and subsecond
+/// since there is no format option to forgo the dot.
+/// We always print as many subsecond as his given to us,
+/// this means the subsecond part can be between 1 and 9 digits.
 const DATE_FORMAT: &[FormatItem<'_>] = format_description!("[year]-[month]-[day]");
+
 const ENCODE_TIME_FORMAT_WHOLE_SECOND: &[FormatItem<'_>] =
     format_description!("[hour]:[minute]:[second]");
 const ENCODE_TIME_FORMAT_SUBSECOND: &[FormatItem<'_>] =
-    format_description!("[hour]:[minute]:[second].[subsecond digits:6]");
+    format_description!("[hour]:[minute]:[second].[subsecond]");
 
 const TIME_FORMATS: [&[FormatItem<'_>]; 9] = [
-    // Most likely
+    // Most likely formats
     format_description!("[hour]:[minute]:[second].[subsecond]"),
+    format_description!("[hour]:[minute]:[second]"),
     // All other valid formats in order of increasing specificity
     format_description!("[hour]:[minute]"),
     format_description!("[hour]:[minute]Z"),
     format_description!("[hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
-    format_description!("[hour]:[minute]:[second]"),
     format_description!("[hour]:[minute]:[second]Z"),
     format_description!("[hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
-    // here is where the most likely format would go according to the pattern
     format_description!("[hour]:[minute]:[second].[subsecond]Z"),
     format_description!(
         "[hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"
@@ -40,28 +47,26 @@ const TIME_FORMATS: [&[FormatItem<'_>]; 9] = [
 const ENCODE_PRIMITIVE_DATETIME_FORMAT_WHOLE_SECOND: &[FormatItem<'_>] =
     format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
 const ENCODE_PRIMITIVE_DATETIME_FORMAT_SUBSECOND: &[FormatItem<'_>] =
-    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:6]");
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
 
 const ENCODE_DATETIME_FORMAT_WHOLE_SECOND: &[FormatItem<'_>] = format_description!(
     "[year]-[month]-[day] [hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"
 );
 const ENCODE_DATETIME_FORMAT_SUBSECOND: &[FormatItem<'_>] =
-    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3][offset_hour sign:mandatory]:[offset_minute]");
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]");
 
-const DATETIME_FORMATS: [&[FormatItem<'_>]; 18] = [
-    // Most likely format
+const PRIMITIVE_DATETIME_FORMATS: [&[FormatItem<'_>]; 18] = [
+    // Most likely formats
     format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"),
-    // Other formats in order of increasing specificity
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"),
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"),
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
+    // All other formats in order of increasing specificity
     format_description!("[year]-[month]-[day] [hour]:[minute]"),
     format_description!("[year]-[month]-[day] [hour]:[minute]Z"),
     format_description!("[year]-[month]-[day] [hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
-    format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"),
     format_description!("[year]-[month]-[day] [hour]:[minute]:[second]Z"),
-    format_description!("[year]-[month]-[day] [hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
-    // here is where the most likely format would go according to the pattern
     format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]Z"),
-    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"),
-    // now the same thing, with T
     format_description!("[year]-[month]-[day]T[hour]:[minute]"),
     format_description!("[year]-[month]-[day]T[hour]:[minute]Z"),
     format_description!("[year]-[month]-[day]T[hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
@@ -73,6 +78,23 @@ const DATETIME_FORMATS: [&[FormatItem<'_>]; 18] = [
     format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"),
 ];
 
+const DATETIME_FORMATS: [&[FormatItem<'_>]; 12] = [
+    // Most likely formats
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"),
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
+    // All other formats in order of increasing specificity
+    format_description!("[year]-[month]-[day] [hour]:[minute]Z"),
+    format_description!("[year]-[month]-[day] [hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second]Z"),
+    format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]Z"),
+    format_description!("[year]-[month]-[day]T[hour]:[minute]Z"),
+    format_description!("[year]-[month]-[day]T[hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
+    format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z"),
+    format_description!("[year]-[month]-[day]T[hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
+    format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond]Z"),
+    format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"),
+];
+
 fn naive_utc(dt: OffsetDateTime) -> PrimitiveDateTime {
     let dt = dt.to_offset(UtcOffset::UTC);
     PrimitiveDateTime::new(dt.date(), dt.time())
@@ -134,7 +156,7 @@ impl ToSql<Time, Sqlite> for NaiveTime {
 impl FromSql<Timestamp, Sqlite> for PrimitiveDateTime {
     fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
         value.parse_string(|text| {
-            for format in DATETIME_FORMATS {
+            for format in PRIMITIVE_DATETIME_FORMATS {
                 if let Ok(dt) = Self::parse(text, format) {
                     return Ok(dt);
                 }
@@ -154,7 +176,7 @@ impl FromSql<Timestamp, Sqlite> for PrimitiveDateTime {
 #[cfg(all(feature = "sqlite", feature = "time"))]
 impl ToSql<Timestamp, Sqlite> for PrimitiveDateTime {
     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
-        let format = if self.microsecond() == 0 {
+        let format = if self.nanosecond() == 0 {
             ENCODE_PRIMITIVE_DATETIME_FORMAT_WHOLE_SECOND
         } else {
             ENCODE_PRIMITIVE_DATETIME_FORMAT_SUBSECOND
@@ -168,7 +190,7 @@ impl ToSql<Timestamp, Sqlite> for PrimitiveDateTime {
 impl FromSql<TimestamptzSqlite, Sqlite> for PrimitiveDateTime {
     fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
         value.parse_string(|text| {
-            for format in DATETIME_FORMATS {
+            for format in PRIMITIVE_DATETIME_FORMATS {
                 if let Ok(dt) = Self::parse(text, format) {
                     return Ok(dt);
                 }
@@ -188,7 +210,7 @@ impl FromSql<TimestamptzSqlite, Sqlite> for PrimitiveDateTime {
 #[cfg(all(feature = "sqlite", feature = "time"))]
 impl ToSql<TimestamptzSqlite, Sqlite> for PrimitiveDateTime {
     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
-        let format = if self.microsecond() == 0 {
+        let format = if self.nanosecond() == 0 {
             ENCODE_PRIMITIVE_DATETIME_FORMAT_WHOLE_SECOND
         } else {
             ENCODE_PRIMITIVE_DATETIME_FORMAT_SUBSECOND
@@ -201,6 +223,20 @@ impl ToSql<TimestamptzSqlite, Sqlite> for PrimitiveDateTime {
 #[cfg(all(feature = "sqlite", feature = "time"))]
 impl FromSql<TimestamptzSqlite, Sqlite> for OffsetDateTime {
     fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
+        // First try to parse the timezone
+        if let Ok(dt) = value.parse_string(|text| {
+            for format in DATETIME_FORMATS {
+                if let Ok(dt) = OffsetDateTime::parse(text, format) {
+                    return Ok(dt);
+                }
+            }
+
+            Err(())
+        }) {
+            return Ok(dt);
+        }
+
+        // Fallback on assuming UTC
         let primitive_date_time =
             <PrimitiveDateTime as FromSql<TimestamptzSqlite, Sqlite>>::from_sql(value)?;
         Ok(primitive_date_time.assume_utc())
@@ -210,8 +246,9 @@ impl FromSql<TimestamptzSqlite, Sqlite> for OffsetDateTime {
 #[cfg(all(feature = "sqlite", feature = "time"))]
 impl ToSql<TimestamptzSqlite, Sqlite> for OffsetDateTime {
     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
+        // Converting to UTC ensures consistency
         let dt_utc = self.to_offset(UtcOffset::UTC);
-        let format = if self.millisecond() == 0 {
+        let format = if self.nanosecond() == 0 {
             ENCODE_DATETIME_FORMAT_WHOLE_SECOND
         } else {
             ENCODE_DATETIME_FORMAT_SUBSECOND
@@ -322,15 +359,15 @@ mod tests {
         let connection = &mut connection();
 
         let midnight = NaiveTime::from_hms(0, 0, 0).unwrap();
-        let query = select(time("00:00:00.000000").eq(midnight));
+        let query = select(time("00:00:00").eq(midnight));
         assert_eq!(query.get_result::<bool>(connection).unwrap(), true);
 
         let noon = NaiveTime::from_hms(12, 0, 0).unwrap();
-        let query = select(time("12:00:00.000000").eq(noon));
+        let query = select(time("12:00:00").eq(noon));
         assert_eq!(query.get_result::<bool>(connection).unwrap(), true);
 
         let roughly_half_past_eleven = NaiveTime::from_hms_micro(23, 37, 4, 2200).unwrap();
-        let query = select(sql::<Time>("'23:37:04.002200'").eq(roughly_half_past_eleven));
+        let query = select(sql::<Time>("'23:37:04.0022'").eq(roughly_half_past_eleven));
         assert_eq!(query.get_result::<bool>(connection).unwrap(), true);
     }
 
@@ -467,19 +504,19 @@ mod tests {
     fn datetimes_encode_correctly() {
         let connection = &mut connection();
         let january_first_2000 = datetime!(2000-1-1 0:0:0);
-        let query = select(datetime("2000-01-01 00:00:00.000000").eq(january_first_2000));
+        let query = select(datetime("2000-01-01 00:00:00").eq(january_first_2000));
         assert!(query.get_result::<bool>(connection).unwrap());
 
         let distant_past = datetime!(0-4-11 20:00:20);
-        let query = select(datetime("0000-04-11 20:00:20.000000").eq(distant_past));
+        let query = select(datetime("0000-04-11 20:00:20").eq(distant_past));
         assert!(query.get_result::<bool>(connection).unwrap());
 
         let january_first_2018 = datetime!(2018 - 1 - 1 12:00:00.0005);
-        let query = select(sql::<Timestamp>("'2018-01-01 12:00:00.000500'").eq(january_first_2018));
+        let query = select(sql::<Timestamp>("'2018-01-01 12:00:00.0005'").eq(january_first_2018));
         assert!(query.get_result::<bool>(connection).unwrap());
 
         let distant_future = datetime!(9999-1-8 0:0:0);
-        let query = select(datetime("9999-01-08 00:00:00.000000").eq(distant_future));
+        let query = select(datetime("9999-01-08 00:00:00").eq(distant_future));
         assert!(query.get_result::<bool>(connection).unwrap());
     }
 
@@ -632,22 +669,6 @@ mod tests {
             "1970-01-01T00:00:00+00:00",
             "1970-01-01T00:00:00.000+00:00",
             "1970-01-01T00:00:00.000000+00:00",
-            "1970-01-01 00:00+01:00",
-            "1970-01-01 00:00:00+01:00",
-            "1970-01-01 00:00:00.000+01:00",
-            "1970-01-01 00:00:00.000000+01:00",
-            "1970-01-01T00:00+01:00",
-            "1970-01-01T00:00:00+01:00",
-            "1970-01-01T00:00:00.000+01:00",
-            "1970-01-01T00:00:00.000000+01:00",
-            "1970-01-01T00:00-01:00",
-            "1970-01-01T00:00:00-01:00",
-            "1970-01-01T00:00:00.000-01:00",
-            "1970-01-01T00:00:00.000000-01:00",
-            "1970-01-01T00:00-01:00",
-            "1970-01-01T00:00:00-01:00",
-            "1970-01-01T00:00:00.000-01:00",
-            "1970-01-01T00:00:00.000000-01:00",
             "2440587.5",
         ];
 
diff --git a/diesel/src/type_impls/date_and_time.rs b/diesel/src/type_impls/date_and_time.rs
index b189fc275e1e..651a2cc33c87 100644
--- a/diesel/src/type_impls/date_and_time.rs
+++ b/diesel/src/type_impls/date_and_time.rs
@@ -79,8 +79,16 @@ mod time {
     #[cfg_attr(feature = "mysql_backend", diesel(sql_type = crate::sql_types::Datetime))]
     struct NaiveDateTimeProxy(PrimitiveDateTime);
 
-    #[derive(AsExpression, FromSqlRow)]
+    #[derive(FromSqlRow)]
     #[diesel(foreign_derive)]
+    #[cfg_attr(
+        any(
+            feature = "postgres_backend",
+            feature = "sqlite",
+            feature = "mysql_backend"
+        ),
+        derive(AsExpression)
+    )]
     #[cfg_attr(
         feature = "postgres_backend",
         diesel(sql_type = crate::sql_types::Timestamptz)
diff --git a/diesel_cli/Cargo.toml b/diesel_cli/Cargo.toml
index 88604370ce3a..e2308dec08bc 100644
--- a/diesel_cli/Cargo.toml
+++ b/diesel_cli/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "diesel_cli"
-version = "2.1.0"
+version = "2.1.1"
 license = "MIT OR Apache-2.0"
 description = "Provides the CLI for the Diesel crate"
 readme = "README.md"
diff --git a/diesel_cli/src/cli.rs b/diesel_cli/src/cli.rs
index af9b63c7ec4f..25bf28de6028 100644
--- a/diesel_cli/src/cli.rs
+++ b/diesel_cli/src/cli.rs
@@ -98,6 +98,8 @@ pub fn build_cli() -> Command {
                 )
                 .arg(
                     Arg::new("MIGRATION_NAME")
+                        .index(1)
+                        .num_args(1)
                         .help("The name of the migration to create.")
                         .required(true),
                 )
@@ -145,6 +147,29 @@ pub fn build_cli() -> Command {
                         .default_missing_value("NOT_SET")
                         .num_args(0..=1)
                         .require_equals(true),
+                )
+                .arg(
+                    Arg::new("table-name")
+                        .index(2)
+                        .num_args(1..)
+                        .action(clap::ArgAction::Append)
+                        .help("Table names to filter."),
+                )
+                .arg(
+                    Arg::new("only-tables")
+                        .short('o')
+                        .long("only-tables")
+                        .action(ArgAction::SetTrue)
+                        .help("Only include tables from table-name that matches regexp.")
+                        .conflicts_with("except-tables"),
+                )
+                .arg(
+                    Arg::new("except-tables")
+                        .short('e')
+                        .long("except-tables")
+                        .action(ArgAction::SetTrue)
+                        .help("Exclude tables from table-name that matches regex.")
+                        .conflicts_with("only-tables"),
                 ),
         )
         .subcommand_required(true)
@@ -198,7 +223,7 @@ pub fn build_cli() -> Command {
                 .index(1)
                 .num_args(1..)
                 .action(clap::ArgAction::Append)
-                .help("Table names to filter (default only-tables if not empty)."),
+                .help("Table names to filter."),
         )
         .arg(
             Arg::new("only-tables")
diff --git a/diesel_cli/src/config.rs b/diesel_cli/src/config.rs
index 9a8206e4b7af..9486a55a0e8f 100644
--- a/diesel_cli/src/config.rs
+++ b/diesel_cli/src/config.rs
@@ -1,11 +1,14 @@
 use clap::ArgMatches;
-use serde::Deserialize;
-use std::env;
+use serde::de::{self, MapAccess, Visitor};
+use serde::{Deserialize, Deserializer};
+use serde_regex::Serde as RegexWrapper;
 use std::error::Error;
 use std::fs;
 use std::path::{Path, PathBuf};
+use std::{env, fmt};
 
 use super::find_project_root;
+use crate::infer_schema_internals::TableName;
 use crate::print_schema;
 use crate::print_schema::ColumnSorting;
 
@@ -46,6 +49,26 @@ impl Config {
             migration.set_relative_path_base(base);
         }
     }
+
+    pub fn set_filter(
+        mut self,
+        matches: &ArgMatches,
+    ) -> Result<Self, Box<dyn Error + Send + Sync + 'static>> {
+        let table_names = matches
+            .get_many::<String>("table-name")
+            .unwrap_or_default()
+            .map(|table_name_regex| regex::Regex::new(table_name_regex).map(Into::into))
+            .collect::<Result<Vec<Regex>, _>>()
+            .map_err(|e| format!("invalid argument for table filtering regex: {e}"))?;
+
+        if matches.get_flag("only-tables") {
+            self.print_schema.filter = Filtering::OnlyTables(table_names)
+        } else if matches.get_flag("except-tables") {
+            self.print_schema.filter = Filtering::ExceptTables(table_names)
+        }
+
+        Ok(self)
+    }
 }
 
 #[derive(Default, Deserialize)]
@@ -56,7 +79,7 @@ pub struct PrintSchema {
     #[serde(default)]
     pub with_docs: print_schema::DocConfig,
     #[serde(default)]
-    pub filter: print_schema::Filtering,
+    pub filter: Filtering,
     #[serde(default)]
     pub column_sorting: ColumnSorting,
     #[serde(default)]
@@ -129,3 +152,86 @@ impl MigrationsDirectory {
         }
     }
 }
+
+type Regex = RegexWrapper<::regex::Regex>;
+
+#[derive(Clone)]
+pub enum Filtering {
+    OnlyTables(Vec<Regex>),
+    ExceptTables(Vec<Regex>),
+    None,
+}
+
+#[allow(clippy::derivable_impls)] // that's not supported on rust 1.65
+impl Default for Filtering {
+    fn default() -> Self {
+        Filtering::None
+    }
+}
+
+impl Filtering {
+    pub fn should_ignore_table(&self, name: &TableName) -> bool {
+        use self::Filtering::*;
+
+        match *self {
+            OnlyTables(ref regexes) => !regexes.iter().any(|regex| regex.is_match(&name.sql_name)),
+            ExceptTables(ref regexes) => regexes.iter().any(|regex| regex.is_match(&name.sql_name)),
+            None => false,
+        }
+    }
+}
+
+impl<'de> Deserialize<'de> for Filtering {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct FilteringVisitor;
+
+        impl<'de> Visitor<'de> for FilteringVisitor {
+            type Value = Filtering;
+
+            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
+                f.write_str("either only_tables or except_tables")
+            }
+
+            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
+            where
+                V: MapAccess<'de>,
+            {
+                let mut only_tables = None::<Vec<Regex>>;
+                let mut except_tables = None::<Vec<Regex>>;
+                while let Some(key) = map.next_key::<String>()? {
+                    match &key as &str {
+                        "only_tables" => {
+                            if only_tables.is_some() {
+                                return Err(de::Error::duplicate_field("only_tables"));
+                            }
+                            only_tables = Some(map.next_value()?);
+                        }
+                        "except_tables" => {
+                            if except_tables.is_some() {
+                                return Err(de::Error::duplicate_field("except_tables"));
+                            }
+                            except_tables = Some(map.next_value()?);
+                        }
+                        _ => {
+                            return Err(de::Error::unknown_field(
+                                &key,
+                                &["only_tables", "except_tables"],
+                            ))
+                        }
+                    }
+                }
+                match (only_tables, except_tables) {
+                    (Some(t), None) => Ok(Filtering::OnlyTables(t)),
+                    (None, Some(t)) => Ok(Filtering::ExceptTables(t)),
+                    (None, None) => Ok(Filtering::None),
+                    _ => Err(de::Error::duplicate_field("only_tables except_tables")),
+                }
+            }
+        }
+
+        deserializer.deserialize_map(FilteringVisitor)
+    }
+}
diff --git a/diesel_cli/src/database.rs b/diesel_cli/src/database.rs
index 034597758325..42185cc19921 100644
--- a/diesel_cli/src/database.rs
+++ b/diesel_cli/src/database.rs
@@ -121,8 +121,20 @@ pub enum InferConnection {
 impl InferConnection {
     pub fn from_matches(matches: &ArgMatches) -> Self {
         let database_url = database_url(matches);
-        Self::establish(&database_url)
-            .unwrap_or_else(|err| handle_error_with_database_url(&database_url, err))
+        // we construct `InferConnection` manually
+        // here because that allows us to emit better error messages
+        // than the generic ` Invalid connection url for multiconnection`
+        // error generated by `InferConnection::establish`
+        let result = match Backend::for_url(&database_url) {
+            #[cfg(feature = "postgres")]
+            Backend::Pg => PgConnection::establish(&database_url).map(Self::Pg),
+            #[cfg(feature = "mysql")]
+            Backend::Mysql => MysqlConnection::establish(&database_url).map(Self::Mysql),
+            #[cfg(feature = "sqlite")]
+            Backend::Sqlite => SqliteConnection::establish(&database_url).map(Self::Sqlite),
+        };
+
+        result.unwrap_or_else(|err| handle_error_with_database_url(&database_url, err))
     }
 }
 
diff --git a/diesel_cli/src/infer_schema_internals/inference.rs b/diesel_cli/src/infer_schema_internals/inference.rs
index 55327403fd67..37d5094a2174 100644
--- a/diesel_cli/src/infer_schema_internals/inference.rs
+++ b/diesel_cli/src/infer_schema_internals/inference.rs
@@ -4,6 +4,9 @@ use diesel::result::Error::NotFound;
 
 use super::data_structures::*;
 use super::table_data::*;
+
+use crate::config::Filtering;
+
 use crate::database::InferConnection;
 use crate::print_schema::{ColumnSorting, DocConfig};
 
@@ -124,6 +127,13 @@ pub fn load_table_names(
     }
 }
 
+pub fn filter_table_names(table_names: Vec<TableName>, table_filter: &Filtering) -> Vec<TableName> {
+    table_names
+        .into_iter()
+        .filter(|t| !table_filter.should_ignore_table(t))
+        .collect::<_>()
+}
+
 fn get_table_comment(
     conn: &mut InferConnection,
     table: &TableName,
diff --git a/diesel_cli/src/infer_schema_internals/load_foreign_keys.sql b/diesel_cli/src/infer_schema_internals/load_foreign_keys.sql
new file mode 100644
index 000000000000..c71b04275b68
--- /dev/null
+++ b/diesel_cli/src/infer_schema_internals/load_foreign_keys.sql
@@ -0,0 +1,19 @@
+SELECT
+   sch.nspname                                   AS "self_schema",
+   tbl.relname                                   AS "self_table",
+   ARRAY_AGG(col.attname ORDER BY u.attposition) AS "self_columns",
+   f_sch.nspname                                 AS "foreign_schema",
+   f_tbl.relname                                 AS "foreign_table",
+   ARRAY_AGG(f_col.attname ORDER BY f_u.attposition) AS "foreign_columns"
+FROM pg_constraint c
+       LEFT JOIN LATERAL UNNEST(c.conkey) WITH ORDINALITY AS u(attnum, attposition) ON TRUE
+       LEFT JOIN LATERAL UNNEST(c.confkey) WITH ORDINALITY AS f_u(attnum, attposition) ON f_u.attposition = u.attposition
+       JOIN pg_class tbl ON tbl.oid = c.conrelid
+       JOIN pg_namespace sch ON sch.oid = tbl.relnamespace
+       LEFT JOIN pg_attribute col ON (col.attrelid = tbl.oid AND col.attnum = u.attnum)
+       LEFT JOIN pg_class f_tbl ON f_tbl.oid = c.confrelid
+       LEFT JOIN pg_namespace f_sch ON f_sch.oid = f_tbl.relnamespace
+       LEFT JOIN pg_attribute f_col ON (f_col.attrelid = f_tbl.oid AND f_col.attnum = f_u.attnum)
+WHERE sch.nspname = $1 and f_sch.nspname = $1 and c.contype = 'f'
+GROUP BY "self_schema", "self_table", "foreign_schema", "foreign_table"
+ORDER BY "self_schema", "self_table";
diff --git a/diesel_cli/src/infer_schema_internals/pg.rs b/diesel_cli/src/infer_schema_internals/pg.rs
index bb0635979aa6..79a69133ae4a 100644
--- a/diesel_cli/src/infer_schema_internals/pg.rs
+++ b/diesel_cli/src/infer_schema_internals/pg.rs
@@ -2,14 +2,13 @@ use super::data_structures::*;
 use super::information_schema::DefaultSchema;
 use super::TableName;
 use crate::print_schema::ColumnSorting;
-use diesel::{
-    deserialize::{self, FromStaticSqlRow, Queryable},
-    dsl::AsExprOf,
-    expression::AsExpression,
-    pg::Pg,
-    prelude::*,
-    sql_types,
-};
+use diesel::connection::DefaultLoadingMode;
+use diesel::deserialize::{self, FromStaticSqlRow, Queryable};
+use diesel::dsl::AsExprOf;
+use diesel::expression::AsExpression;
+use diesel::pg::Pg;
+use diesel::prelude::*;
+use diesel::sql_types::{self, Array, Text};
 use heck::ToUpperCamelCase;
 use std::borrow::Cow;
 use std::error::Error;
@@ -62,8 +61,13 @@ pub fn determine_column_type(
 diesel::postfix_operator!(Regclass, "::regclass", sql_types::Oid, backend: Pg);
 
 fn regclass(table: &TableName) -> Regclass<AsExprOf<String, sql_types::Text>> {
+    let table_name = match table.schema {
+        Some(ref schema_name) => format!("\"{}\".\"{}\"", schema_name, table.sql_name),
+        None => format!("\"{}\"", table.sql_name),
+    };
+
     Regclass::new(<String as AsExpression<sql_types::Text>>::as_expression(
-        table.full_sql_name(),
+        table_name,
     ))
 }
 
@@ -173,72 +177,48 @@ pub fn load_foreign_key_constraints(
     connection: &mut PgConnection,
     schema_name: Option<&str>,
 ) -> QueryResult<Vec<ForeignKeyConstraint>> {
-    use super::information_schema::information_schema::key_column_usage as kcu;
-    use super::information_schema::information_schema::referential_constraints as rc;
-    use super::information_schema::information_schema::table_constraints as tc;
+    #[derive(QueryableByName)]
+    struct ForeignKeyList {
+        #[diesel(sql_type = Text)]
+        self_schema: String,
+        #[diesel(sql_type = Text)]
+        self_table: String,
+        #[diesel(sql_type = Array<Text>)]
+        self_columns: Vec<String>,
+        #[diesel(sql_type = Text)]
+        foreign_schema: String,
+        #[diesel(sql_type = Text)]
+        foreign_table: String,
+        #[diesel(sql_type = Array<Text>)]
+        foreign_columns: Vec<String>,
+    }
 
     let default_schema = Pg::default_schema(connection)?;
     let schema_name = schema_name.unwrap_or(&default_schema);
 
-    let constraint_names = tc::table
-        .filter(tc::constraint_type.eq("FOREIGN KEY"))
-        .filter(tc::table_schema.eq(schema_name))
-        .inner_join(
-            rc::table.on(tc::constraint_schema
-                .eq(rc::constraint_schema)
-                .and(tc::constraint_name.eq(rc::constraint_name))),
-        )
-        .select((
-            rc::constraint_schema,
-            rc::constraint_name,
-            rc::unique_constraint_schema,
-            rc::unique_constraint_name,
-        ))
-        .load::<(String, String, Option<String>, Option<String>)>(connection)?;
-
-    constraint_names
-        .into_iter()
-        .map(
-            |(foreign_key_schema, foreign_key_name, primary_key_schema, primary_key_name)| {
-                let foreign_key = kcu::table
-                    .filter(kcu::constraint_schema.eq(&foreign_key_schema))
-                    .filter(kcu::constraint_name.eq(&foreign_key_name))
-                    .group_by((kcu::table_name, kcu::table_schema))
-                    .select((
-                        kcu::table_name,
-                        kcu::table_schema,
-                        array_agg(kcu::column_name),
-                    ))
-                    .first::<(String, String, Vec<String>)>(connection)?;
-                let primary_key = kcu::table
-                    .filter(kcu::constraint_schema.nullable().eq(primary_key_schema))
-                    .filter(kcu::constraint_name.nullable().eq(primary_key_name))
-                    .group_by((kcu::table_name, kcu::table_schema))
-                    .select((
-                        kcu::table_name,
-                        kcu::table_schema,
-                        array_agg(kcu::column_name),
-                    ))
-                    .first::<(String, String, Vec<String>)>(connection)?;
-
-                let mut primary_key_table = TableName::new(primary_key.0, primary_key.1);
-                primary_key_table.strip_schema_if_matches(&default_schema);
-                let mut foreign_key_table = TableName::new(foreign_key.0, foreign_key.1);
-                foreign_key_table.strip_schema_if_matches(&default_schema);
-
-                let primary_key_columns = primary_key.2;
-                let foreign_key_columns = foreign_key.2;
-
-                Ok(ForeignKeyConstraint {
-                    child_table: foreign_key_table,
-                    parent_table: primary_key_table,
-                    foreign_key_columns_rust: foreign_key_columns.clone(),
-                    foreign_key_columns,
-                    primary_key_columns,
-                })
-            },
-        )
-        .filter(|e| !matches!(e, Err(diesel::result::Error::NotFound)))
+    diesel::sql_query(include_str!("load_foreign_keys.sql"))
+        .bind::<Text, _>(schema_name)
+        .load_iter::<ForeignKeyList, DefaultLoadingMode>(connection)?
+        .map(|f| {
+            let f = f?;
+            let mut child_table = TableName::new(f.self_table, f.self_schema);
+            child_table.strip_schema_if_matches(&default_schema);
+            let mut parent_table = TableName::new(f.foreign_table, f.foreign_schema);
+            parent_table.strip_schema_if_matches(&default_schema);
+
+            let foreign_key_columns_rust = f
+                .self_columns
+                .iter()
+                .map(|s| super::inference::rust_name_for_sql_name(s))
+                .collect();
+            Ok(ForeignKeyConstraint {
+                child_table,
+                parent_table,
+                foreign_key_columns: f.self_columns,
+                foreign_key_columns_rust,
+                primary_key_columns: f.foreign_columns,
+            })
+        })
         .collect()
 }
 
@@ -380,4 +360,54 @@ mod test {
             load_foreign_key_constraints(&mut connection, Some("test_schema"))
         );
     }
+
+    #[test]
+    fn get_foreign_keys_loads_foreign_keys_with_same_name() {
+        let mut connection = connection();
+
+        diesel::sql_query("CREATE SCHEMA test_schema")
+            .execute(&mut connection)
+            .unwrap();
+        diesel::sql_query("CREATE TABLE test_schema.table_1 (id SERIAL PRIMARY KEY)")
+            .execute(&mut connection)
+            .unwrap();
+        diesel::sql_query(
+            "CREATE TABLE test_schema.table_2 (\
+                    id SERIAL PRIMARY KEY,\
+                    fk_id INTEGER NOT NULL,\
+                    CONSTRAINT fk FOREIGN KEY (fk_id) REFERENCES test_schema.table_1 (id))",
+        )
+        .execute(&mut connection)
+        .unwrap();
+        diesel::sql_query(
+            "CREATE TABLE test_schema.table_3 (\
+                    id SERIAL PRIMARY KEY,\
+                    fk_id INTEGER NOT NULL,\
+                    CONSTRAINT fk FOREIGN KEY (fk_id) REFERENCES test_schema.table_1 (id))",
+        )
+        .execute(&mut connection)
+        .unwrap();
+
+        let table_1 = TableName::new("table_1", "test_schema");
+        let table_2 = TableName::new("table_2", "test_schema");
+        let table_3 = TableName::new("table_3", "test_schema");
+        let fk_one = ForeignKeyConstraint {
+            child_table: table_2,
+            parent_table: table_1.clone(),
+            foreign_key_columns: vec!["fk_id".into()],
+            foreign_key_columns_rust: vec!["fk_id".into()],
+            primary_key_columns: vec!["id".into()],
+        };
+        let fk_two = ForeignKeyConstraint {
+            child_table: table_3,
+            parent_table: table_1,
+            foreign_key_columns: vec!["fk_id".into()],
+            foreign_key_columns_rust: vec!["fk_id".into()],
+            primary_key_columns: vec!["id".into()],
+        };
+        assert_eq!(
+            Ok(vec![fk_one, fk_two]),
+            load_foreign_key_constraints(&mut connection, Some("test_schema"))
+        );
+    }
 }
diff --git a/diesel_cli/src/infer_schema_internals/sqlite.rs b/diesel_cli/src/infer_schema_internals/sqlite.rs
index cbc64e267988..f1eb436d9c9e 100644
--- a/diesel_cli/src/infer_schema_internals/sqlite.rs
+++ b/diesel_cli/src/infer_schema_internals/sqlite.rs
@@ -1,7 +1,9 @@
 use std::error::Error;
 
-use diesel::deserialize::{self, FromStaticSqlRow, Queryable};
+use diesel::deserialize::{self, Queryable};
 use diesel::dsl::sql;
+use diesel::row::NamedRow;
+use diesel::sql_types::{Bool, Text};
 use diesel::sqlite::Sqlite;
 use diesel::*;
 
@@ -15,18 +17,6 @@ table! {
     }
 }
 
-table! {
-    pragma_table_info (cid) {
-        cid ->Integer,
-        name -> VarChar,
-        type_name -> VarChar,
-        notnull -> Bool,
-        dflt_value -> Nullable<VarChar>,
-        pk -> Bool,
-        hidden -> Integer,
-    }
-}
-
 table! {
     pragma_foreign_key_list {
         id -> Integer,
@@ -141,7 +131,10 @@ pub fn get_table_data(
     } else {
         format!("PRAGMA TABLE_INFO('{}')", &table.sql_name)
     };
-    let mut result = sql::<pragma_table_info::SqlType>(&query).load(conn)?;
+
+    // See: https://github.com/diesel-rs/diesel/issues/3579 as to why we use a direct
+    // `sql_query` with `QueryableByName` instead of using `sql::<pragma_table_info::SqlType>`.
+    let mut result = sql_query(query).load::<ColumnInformation>(conn)?;
     match column_sorting {
         ColumnSorting::OrdinalPosition => {}
         ColumnSorting::Name => {
@@ -153,28 +146,35 @@ pub fn get_table_data(
     Ok(result)
 }
 
-impl<ST> Queryable<ST, Sqlite> for ColumnInformation
-where
-    (i32, String, String, bool, Option<String>, bool, i32): FromStaticSqlRow<ST, Sqlite>,
-{
-    type Row = (i32, String, String, bool, Option<String>, bool, i32);
-
-    fn build(row: Self::Row) -> deserialize::Result<Self> {
-        Ok(ColumnInformation::new(
-            row.1, row.2, None, !row.3, None, None,
+impl QueryableByName<Sqlite> for ColumnInformation {
+    fn build<'a>(row: &impl NamedRow<'a, Sqlite>) -> deserialize::Result<Self> {
+        let column_name = NamedRow::get::<Text, String>(row, "name")?;
+        let type_name = NamedRow::get::<Text, String>(row, "type")?;
+        let notnull = NamedRow::get::<Bool, bool>(row, "notnull")?;
+
+        Ok(Self::new(
+            column_name,
+            type_name,
+            None,
+            !notnull,
+            None,
+            None,
         ))
     }
 }
 
-#[derive(Queryable)]
-struct FullTableInfo {
-    _cid: i32,
+struct PrimaryKeyInformation {
     name: String,
-    _type_name: String,
-    _not_null: bool,
-    _dflt_value: Option<String>,
     primary_key: bool,
-    _hidden: i32,
+}
+
+impl QueryableByName<Sqlite> for PrimaryKeyInformation {
+    fn build<'a>(row: &impl NamedRow<'a, Sqlite>) -> deserialize::Result<Self> {
+        let name = NamedRow::get::<Text, String>(row, "name")?;
+        let primary_key = NamedRow::get::<Bool, bool>(row, "pk")?;
+
+        Ok(Self { name, primary_key })
+    }
 }
 
 #[derive(Queryable)]
@@ -199,7 +199,7 @@ pub fn get_primary_keys(
     } else {
         format!("PRAGMA TABLE_INFO('{}')", &table.sql_name)
     };
-    let results = sql::<pragma_table_info::SqlType>(&query).load::<FullTableInfo>(conn)?;
+    let results = sql_query(query).load::<PrimaryKeyInformation>(conn)?;
     Ok(results
         .into_iter()
         .filter_map(|i| if i.primary_key { Some(i.name) } else { None })
@@ -407,3 +407,18 @@ fn load_foreign_key_constraints_loads_foreign_keys() {
     let fks = load_foreign_key_constraints(&mut connection, None).unwrap();
     assert_eq!(vec![fk_one, fk_two], fks);
 }
+
+#[test]
+fn all_rowid_aliases_used_empty_result() {
+    let mut connection = SqliteConnection::establish(":memory:").unwrap();
+
+    diesel::sql_query("CREATE TABLE table_1 (rowid TEXT, oid TEXT, _rowid_ TEXT)")
+        .execute(&mut connection)
+        .unwrap();
+
+    let table_1 = TableName::from_name("table_1");
+
+    let res = get_primary_keys(&mut connection, &table_1);
+    assert!(res.is_ok());
+    assert!(res.unwrap().is_empty());
+}
diff --git a/diesel_cli/src/main.rs b/diesel_cli/src/main.rs
index fc5e82fbdee2..62f4fb844ea7 100644
--- a/diesel_cli/src/main.rs
+++ b/diesel_cli/src/main.rs
@@ -31,7 +31,6 @@ use database::InferConnection;
 use diesel::backend::Backend;
 use diesel::Connection;
 use diesel_migrations::{FileBasedMigrations, HarnessWithOutput, MigrationHarness};
-use regex::Regex;
 use std::error::Error;
 use std::fmt::Display;
 use std::io::stdout;
@@ -220,25 +219,12 @@ fn run_infer_schema(matches: &ArgMatches) -> Result<(), Box<dyn Error + Send + S
     use crate::print_schema::*;
 
     let mut conn = InferConnection::from_matches(matches);
-    let mut config = Config::read(matches)?.print_schema;
+    let mut config = Config::read(matches)?.set_filter(matches)?.print_schema;
 
     if let Some(schema_name) = matches.get_one::<String>("schema") {
         config.schema = Some(schema_name.clone())
     }
 
-    let filter = matches
-        .get_many::<String>("table-name")
-        .unwrap_or_default()
-        .map(|table_name_regex| Regex::new(table_name_regex).map(Into::into))
-        .collect::<Result<_, _>>()
-        .map_err(|e| format!("invalid argument for table filtering regex: {e}"));
-
-    if matches.get_flag("only-tables") {
-        config.filter = Filtering::OnlyTables(filter?)
-    } else if matches.get_flag("except-tables") {
-        config.filter = Filtering::ExceptTables(filter?)
-    }
-
     if matches.get_flag("with-docs") {
         config.with_docs = DocConfig::DatabaseCommentsFallbackToAutoGeneratedDocComment;
     }
@@ -282,8 +268,8 @@ fn regenerate_schema_if_file_specified(
 ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
     use std::io::Read;
 
-    let config = Config::read(matches)?;
-    if let Some(ref path) = config.print_schema.file {
+    let config = Config::read(matches)?.print_schema;
+    if let Some(ref path) = config.file {
         let mut connection = InferConnection::from_matches(matches);
         if let Some(parent) = path.parent() {
             fs::create_dir_all(parent)?;
@@ -291,7 +277,7 @@ fn regenerate_schema_if_file_specified(
 
         if matches.get_flag("LOCKED_SCHEMA") {
             let mut buf = Vec::new();
-            print_schema::run_print_schema(&mut connection, &config.print_schema, &mut buf)?;
+            print_schema::run_print_schema(&mut connection, &config, &mut buf)?;
 
             let mut old_buf = Vec::new();
             let mut file = fs::File::open(path)?;
@@ -309,7 +295,7 @@ fn regenerate_schema_if_file_specified(
             use std::io::Write;
 
             let mut file = fs::File::create(path)?;
-            let schema = print_schema::output_schema(&mut connection, &config.print_schema)?;
+            let schema = print_schema::output_schema(&mut connection, &config)?;
             file.write_all(schema.as_bytes())?;
         }
     }
diff --git a/diesel_cli/src/migrations/diff_schema.rs b/diesel_cli/src/migrations/diff_schema.rs
index c2945492ab68..36e381889a89 100644
--- a/diesel_cli/src/migrations/diff_schema.rs
+++ b/diesel_cli/src/migrations/diff_schema.rs
@@ -13,7 +13,8 @@ use syn::visit::Visit;
 use crate::config::Config;
 use crate::database::InferConnection;
 use crate::infer_schema_internals::{
-    ColumnDefinition, ColumnType, ForeignKeyConstraint, TableData, TableName,
+    filter_table_names, load_table_names, ColumnDefinition, ColumnType, ForeignKeyConstraint,
+    TableData, TableName,
 };
 use crate::print_schema::DocConfig;
 
@@ -27,10 +28,12 @@ fn compatible_type_list() -> HashMap<&'static str, Vec<&'static str>> {
 }
 
 pub fn generate_sql_based_on_diff_schema(
-    _config: Config,
+    config: Config,
     matches: &ArgMatches,
     schema_file_path: &Path,
 ) -> Result<(String, String), Box<dyn Error + Send + Sync>> {
+    let config = config.set_filter(matches)?;
+
     let project_root = crate::find_project_root()?;
 
     let schema_path = project_root.join(schema_file_path);
@@ -43,7 +46,11 @@ pub fn generate_sql_based_on_diff_schema(
 
     tables_from_schema.visit_file(&syn_file);
     let mut conn = InferConnection::from_matches(matches);
-    let tables_from_database = crate::infer_schema_internals::load_table_names(&mut conn, None)?;
+    let tables_from_database = filter_table_names(
+        load_table_names(&mut conn, None)?,
+        &config.print_schema.filter,
+    );
+
     let foreign_keys =
         crate::infer_schema_internals::load_foreign_key_constraints(&mut conn, None)?;
     let foreign_key_map = foreign_keys.into_iter().fold(HashMap::new(), |mut acc, t| {
diff --git a/diesel_cli/src/migrations/mod.rs b/diesel_cli/src/migrations/mod.rs
index b12e7308c104..bcbea6868a30 100644
--- a/diesel_cli/src/migrations/mod.rs
+++ b/diesel_cli/src/migrations/mod.rs
@@ -88,7 +88,7 @@ pub(super) fn run_migration_command(
                 if let Some(diff_schema) = diff_schema {
                     self::diff_schema::generate_sql_based_on_diff_schema(
                         config,
-                        matches,
+                        args,
                         &diff_schema,
                     )?
                 } else {
diff --git a/diesel_cli/src/print_schema.rs b/diesel_cli/src/print_schema.rs
index c1dca8a4f893..b11d1ee84846 100644
--- a/diesel_cli/src/print_schema.rs
+++ b/diesel_cli/src/print_schema.rs
@@ -2,9 +2,7 @@ use crate::config;
 use crate::database::{Backend, InferConnection};
 use crate::infer_schema_internals::*;
 
-use serde::de::{self, MapAccess, Visitor};
-use serde::{Deserialize, Deserializer, Serialize};
-use serde_regex::Serde as RegexWrapper;
+use serde::{Deserialize, Serialize};
 use std::collections::HashSet;
 use std::error::Error;
 use std::fmt::{self, Display, Formatter, Write};
@@ -12,33 +10,6 @@ use std::io::Write as IoWrite;
 
 const SCHEMA_HEADER: &str = "// @generated automatically by Diesel CLI.\n";
 
-type Regex = RegexWrapper<::regex::Regex>;
-
-pub enum Filtering {
-    OnlyTables(Vec<Regex>),
-    ExceptTables(Vec<Regex>),
-    None,
-}
-
-#[allow(clippy::derivable_impls)] // that's not supported on rust 1.65
-impl Default for Filtering {
-    fn default() -> Self {
-        Filtering::None
-    }
-}
-
-impl Filtering {
-    pub fn should_ignore_table(&self, name: &TableName) -> bool {
-        use self::Filtering::*;
-
-        match *self {
-            OnlyTables(ref regexes) => !regexes.iter().any(|regex| regex.is_match(&name.sql_name)),
-            ExceptTables(ref regexes) => regexes.iter().any(|regex| regex.is_match(&name.sql_name)),
-            None => false,
-        }
-    }
-}
-
 /// How to sort columns when querying the table schema.
 #[derive(Debug, Deserialize, Serialize)]
 pub enum ColumnSorting {
@@ -176,10 +147,11 @@ pub fn output_schema(
     connection: &mut InferConnection,
     config: &config::PrintSchema,
 ) -> Result<String, Box<dyn Error + Send + Sync + 'static>> {
-    let table_names = load_table_names(connection, config.schema_name())?
-        .into_iter()
-        .filter(|t| !config.filter.should_ignore_table(t))
-        .collect::<Vec<_>>();
+    let table_names = filter_table_names(
+        load_table_names(connection, config.schema_name())?,
+        &config.filter,
+    );
+
     let foreign_keys = load_foreign_key_constraints(connection, config.schema_name())?;
     let foreign_keys =
         remove_unsafe_foreign_keys_for_codegen(connection, &foreign_keys, &table_names);
@@ -697,20 +669,11 @@ impl<'a> Display for ColumnDefinitions<'a> {
                 }
 
                 // Write out attributes
-                if column.rust_name != column.sql_name || column.ty.max_length.is_some() {
-                    let mut is_first = true;
-                    write!(out, r#"#["#)?;
-                    if column.rust_name != column.sql_name {
-                        write!(out, r#"sql_name = {:?}"#, column.sql_name)?;
-                        is_first = false;
-                    }
-                    if let Some(max_length) = column.ty.max_length {
-                        if !is_first {
-                            write!(out, ", ")?;
-                        }
-                        write!(out, "max_length = {}", max_length)?;
-                    }
-                    writeln!(out, r#"]"#)?;
+                if column.rust_name != column.sql_name {
+                    writeln!(out, r#"#[sql_name = "{}"]"#, column.sql_name)?;
+                }
+                if let Some(max_length) = column.ty.max_length {
+                    writeln!(out, r#"#[max_length = {}]"#, max_length)?;
                 }
 
                 writeln!(out, "{} -> {},", column.rust_name, column_type)?;
@@ -781,61 +744,6 @@ impl<'a, 'b: 'a> Write for PadAdapter<'a, 'b> {
     }
 }
 
-impl<'de> Deserialize<'de> for Filtering {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        struct FilteringVisitor;
-
-        impl<'de> Visitor<'de> for FilteringVisitor {
-            type Value = Filtering;
-
-            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
-                f.write_str("either only_tables or except_tables")
-            }
-
-            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
-            where
-                V: MapAccess<'de>,
-            {
-                let mut only_tables = None::<Vec<Regex>>;
-                let mut except_tables = None::<Vec<Regex>>;
-                while let Some(key) = map.next_key::<String>()? {
-                    match &key as &str {
-                        "only_tables" => {
-                            if only_tables.is_some() {
-                                return Err(de::Error::duplicate_field("only_tables"));
-                            }
-                            only_tables = Some(map.next_value()?);
-                        }
-                        "except_tables" => {
-                            if except_tables.is_some() {
-                                return Err(de::Error::duplicate_field("except_tables"));
-                            }
-                            except_tables = Some(map.next_value()?);
-                        }
-                        _ => {
-                            return Err(de::Error::unknown_field(
-                                &key,
-                                &["only_tables", "except_tables"],
-                            ))
-                        }
-                    }
-                }
-                match (only_tables, except_tables) {
-                    (Some(t), None) => Ok(Filtering::OnlyTables(t)),
-                    (None, Some(t)) => Ok(Filtering::ExceptTables(t)),
-                    (None, None) => Ok(Filtering::None),
-                    _ => Err(de::Error::duplicate_field("only_tables except_tables")),
-                }
-            }
-        }
-
-        deserializer.deserialize_map(FilteringVisitor)
-    }
-}
-
 impl DocConfig {
     pub const VARIANTS_STR: &'static [&'static str] = &[
         "database-comments-fallback-to-auto-generated-doc-comment",
diff --git a/diesel_derives/Cargo.toml b/diesel_derives/Cargo.toml
index 3a7b573da586..c3278b1ce875 100644
--- a/diesel_derives/Cargo.toml
+++ b/diesel_derives/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "diesel_derives"
-version = "2.1.0"
+version = "2.1.1"
 license = "MIT OR Apache-2.0"
 description = "You should not use this crate directly, it is internal to Diesel."
 documentation = "https://diesel.rs/guides/"
@@ -43,3 +43,5 @@ mysql = []
 without-deprecated = []
 with-deprecated = []
 r2d2 = []
+chrono = []
+time = []
diff --git a/diesel_derives/src/multiconnection.rs b/diesel_derives/src/multiconnection.rs
index 86e5014d5cb6..053ee64b5113 100644
--- a/diesel_derives/src/multiconnection.rs
+++ b/diesel_derives/src/multiconnection.rs
@@ -565,7 +565,7 @@ fn generate_row(connection_types: &[ConnectionVariant]) -> TokenStream {
 }
 
 fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStream {
-    let to_sql_impls = vec![
+    let mut to_sql_impls = vec![
         (
             quote::quote!(diesel::sql_types::SmallInt),
             quote::quote!(i16),
@@ -582,11 +582,41 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
             quote::quote!(diesel::sql_types::Binary),
             quote::quote!([u8]),
         ),
-    ]
-    .into_iter()
-    .map(|t| generate_to_sql_impls(t, connection_types));
+        (quote::quote!(diesel::sql_types::Bool), quote::quote!(bool)),
+    ];
+    if cfg!(feature = "chrono") {
+        to_sql_impls.push((
+            quote::quote!(diesel::sql_types::Timestamp),
+            quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDateTime),
+        ));
+        to_sql_impls.push((
+            quote::quote!(diesel::sql_types::Date),
+            quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDate),
+        ));
+        to_sql_impls.push((
+            quote::quote!(diesel::sql_types::Time),
+            quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveTime),
+        ));
+    }
+    if cfg!(feature = "time") {
+        to_sql_impls.push((
+            quote::quote!(diesel::sql_types::Timestamp),
+            quote::quote!(diesel::internal::derives::multiconnection::time::PrimitiveDateTime),
+        ));
+        to_sql_impls.push((
+            quote::quote!(diesel::sql_types::Time),
+            quote::quote!(diesel::internal::derives::multiconnection::time::Time),
+        ));
+        to_sql_impls.push((
+            quote::quote!(diesel::sql_types::Date),
+            quote::quote!(diesel::internal::derives::multiconnection::time::Date),
+        ));
+    }
+    let to_sql_impls = to_sql_impls
+        .into_iter()
+        .map(|t| generate_to_sql_impls(t, connection_types));
 
-    let from_sql_impls = vec![
+    let mut from_sql_impls = vec![
         (
             quote::quote!(diesel::sql_types::SmallInt),
             quote::quote!(i16),
@@ -606,9 +636,37 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
             quote::quote!(diesel::sql_types::Binary),
             quote::quote!(Vec<u8>),
         ),
-    ]
-    .into_iter()
-    .map(generate_from_sql_impls);
+        (quote::quote!(diesel::sql_types::Bool), quote::quote!(bool)),
+    ];
+    if cfg!(feature = "chrono") {
+        from_sql_impls.push((
+            quote::quote!(diesel::sql_types::Timestamp),
+            quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDateTime),
+        ));
+        from_sql_impls.push((
+            quote::quote!(diesel::sql_types::Date),
+            quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDate),
+        ));
+        from_sql_impls.push((
+            quote::quote!(diesel::sql_types::Time),
+            quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveTime),
+        ));
+    }
+    if cfg!(feature = "time") {
+        from_sql_impls.push((
+            quote::quote!(diesel::sql_types::Timestamp),
+            quote::quote!(diesel::internal::derives::multiconnection::time::PrimitiveDateTime),
+        ));
+        from_sql_impls.push((
+            quote::quote!(diesel::sql_types::Time),
+            quote::quote!(diesel::internal::derives::multiconnection::time::Time),
+        ));
+        from_sql_impls.push((
+            quote::quote!(diesel::sql_types::Date),
+            quote::quote!(diesel::internal::derives::multiconnection::time::Date),
+        ));
+    }
+    let from_sql_impls = from_sql_impls.into_iter().map(generate_from_sql_impls);
 
     let into_bind_value_bounds = connection_types.iter().map(|c| {
         let ty = c.ty;
@@ -655,14 +713,15 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
         let ty = c.ty;
         quote::quote! {
             Self::#ident(ref mut bc) => {
-                let out = out.inner.unwrap();
+                let out = out.inner.expect("This inner value is set via our custom `ToSql` impls");
                 let callback = out.push_bound_value_to_collector;
                 let value = out.value;
                 <_ as PushBoundValueToCollectorDB<<#ty as diesel::Connection>::Backend>>::push_bound_value(
                      callback,
                      value,
                      bc,
-                     <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(metadata_lookup).unwrap()
+                     <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(metadata_lookup)
+                        .expect("We can downcast the metadata lookup to the right type")
                  )?
             }
         }
@@ -707,6 +766,8 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
         where DB: diesel::backend::Backend
                   + diesel::sql_types::HasSqlType<ST>,
               T: diesel::serialize::ToSql<ST, DB> + 'static,
+              Option<T>: diesel::serialize::ToSql<diesel::sql_types::Nullable<ST>, DB> + 'static,
+              ST: diesel::sql_types::SqlType,
         {
             fn push_bound_value<'a: 'b, 'b>(
                 &self,
@@ -715,11 +776,15 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
                 lookup: &mut <DB as diesel::sql_types::TypeMetadata>::MetadataLookup,
             ) -> diesel::result::QueryResult<()> {
                 use diesel::query_builder::BindCollector;
-                if let InnerBindValueKind::Sized(v) = v {
-                    let v = v.downcast_ref::<T>().expect("We know the type statically here");
-                    collector.push_bound_value::<ST, T>(v, lookup)
-                } else {
-                    unreachable!("We set the value to `InnerBindValueKind::Sized`")
+                match v {
+                    InnerBindValueKind::Sized(v) => {
+                        let v = v.downcast_ref::<T>().expect("We know the type statically here");
+                        collector.push_bound_value::<ST, T>(v, lookup)
+                    }
+                    InnerBindValueKind::Null => {
+                        collector.push_bound_value::<diesel::sql_types::Nullable<ST>, Option<T>>(&None, lookup)
+                    },
+                    _ => unreachable!("We set the value to `InnerBindValueKind::Sized` or `InnerBindValueKind::Null`")
                 }
             }
         }
@@ -783,6 +848,7 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
             Sized(&'a (dyn std::any::Any + std::marker::Send + std::marker::Sync)),
             Str(&'a str),
             Bytes(&'a [u8]),
+            Null,
         }
 
         impl<'a> From<(diesel::sql_types::Text, &'a str)> for BindValue<'a> {
@@ -814,7 +880,7 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
         impl<'a, T, ST> From<(ST, &'a T)> for BindValue<'a>
         where
             T: std::any::Any #(+ #into_bind_value_bounds)* + Send + Sync + 'static,
-            ST: Send + 'static,
+            ST: Send + diesel::sql_types::SqlType<IsNull = diesel::sql_types::is_nullable::NotNull> + 'static,
             #(#has_sql_type_bounds,)*
         {
             fn from((_, v): (ST, &'a T)) -> Self {
@@ -845,15 +911,35 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
                     let out = multi_connection_impl::bind_collector::BindValue::default();
                     let mut out =
                         diesel::serialize::Output::<MultiBackend>::new(out, metadata_lookup);
-
-                    bind.to_sql(&mut out).unwrap();
-                    out.into_inner()
+                    let bind_is_null = bind.to_sql(&mut out).map_err(diesel::result::Error::SerializationError)?;
+                    if matches!(bind_is_null, diesel::serialize::IsNull::Yes) {
+                        // nulls are special and need a special handling because
+                        // there is a wildcard `ToSql` impl in diesel. That means we won't
+                        // set the `inner` field of `BindValue` to something for the `None`
+                        // case. Therefore we need to handle that explicitly here.
+                        //
+                        // We just use a specific sql + rust type here to workaround
+                        // the fact that rustc is not able to see that the underlying DBMS
+                        // must support that sql + rust type combination. All tested DBMS
+                        // (postgres, sqlite, mysql, oracle) seems to not care about the
+                        // actual type here and coerce null values to the "right" type
+                        // anyway
+                        BindValue {
+                            inner: Some(InnerBindValue {
+                                value: InnerBindValueKind::Null,
+                                push_bound_value_to_collector: &PushBoundValueToCollectorImpl {
+                                    p: std::marker::PhantomData::<(diesel::sql_types::Integer, i32)>
+                                }
+                            })
+                        }
+                    } else {
+                        out.into_inner()
+                    }
                 };
                 match self {
                     #(#push_to_inner_collector)*
                 }
 
-
                 Ok(())
             }
         }
@@ -1264,6 +1350,7 @@ fn generate_backend(connection_types: &[ConnectionVariant]) -> TokenStream {
         quote::quote!(diesel::sql_types::Date),
         quote::quote!(diesel::sql_types::Time),
         quote::quote!(diesel::sql_types::Timestamp),
+        quote::quote!(diesel::sql_types::Bool),
     ]
     .into_iter()
     .map(generate_has_sql_type_impls);
@@ -1311,7 +1398,10 @@ fn generate_backend(connection_types: &[ConnectionVariant]) -> TokenStream {
                         super::bind_collector::MultiBindCollector::#lower_ident,
                         super::query_builder::MultiQueryBuilder::#lower_ident,
                         super::backend::MultiBackend::#lower_ident,
-                        |l| <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(l).unwrap(),
+                        |l| {
+                            <#ty as diesel::internal::derives::multiconnection::MultiConnectionHelper>::from_any(l)
+                                .expect("It's possible to downcast the metadata lookup type to the correct type")
+                        },
                     ),
                 )
             }
diff --git a/diesel_derives/src/selectable.rs b/diesel_derives/src/selectable.rs
index 5edc93138c10..4d0b92b4c637 100644
--- a/diesel_derives/src/selectable.rs
+++ b/diesel_derives/src/selectable.rs
@@ -50,7 +50,7 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
             .filter(|(f, _)| !f.embed())
             .flat_map(|(f, ty)| {
                 backends.iter().map(move |b| {
-                    let field_ty = to_field_ty_bound(&f.ty);
+                    let field_ty = to_field_ty_bound(f.ty_for_deserialize());
                     let span = field_ty.span();
                     quote::quote_spanned! {span =>
                         #field_ty: diesel::deserialize::FromSqlRow<diesel::dsl::SqlTypeOf<#ty>, #b>
diff --git a/diesel_derives/src/table.rs b/diesel_derives/src/table.rs
index 377004bc99b5..a0b372164408 100644
--- a/diesel_derives/src/table.rs
+++ b/diesel_derives/src/table.rs
@@ -44,12 +44,43 @@ pub(crate) fn expand(input: TableDecl) -> TokenStream {
         .collect::<Vec<_>>();
     let column_names = &column_names;
     let primary_key: TokenStream = match input.primary_keys.as_ref() {
-        None => {
+        None if column_names.contains(&&syn::Ident::new(
+            DEFAULT_PRIMARY_KEY_NAME,
+            proc_macro2::Span::call_site(),
+        )) =>
+        {
             let id = syn::Ident::new(DEFAULT_PRIMARY_KEY_NAME, proc_macro2::Span::call_site());
             parse_quote! {
                 #id
             }
         }
+        None => {
+            let mut message = format!(
+                "Neither an explict primary key found nor does an `id` column exist.\n\
+                 Consider explicitly defining a primary key. \n\
+                 For example for specifying `{}` as primary key:\n\n\
+                 table! {{\n",
+                column_names[0],
+            );
+            message += &format!("\t{table_name} ({}) {{\n", &column_names[0]);
+            for c in &input.column_defs {
+                let tpe = c
+                    .tpe
+                    .path
+                    .segments
+                    .iter()
+                    .map(|p| p.ident.to_string())
+                    .collect::<Vec<_>>()
+                    .join("::");
+                message += &format!("\t\t{} -> {tpe},\n", c.column_name);
+            }
+            message += "\t}\n}";
+
+            let span = input.table_name.span();
+            return quote::quote_spanned! {span=>
+                compile_error!(#message);
+            };
+        }
         Some(a) if a.keys.len() == 1 => {
             let k = a.keys.first().unwrap();
             parse_quote! {


Test patch:
diff --git a/diesel_cli/tests/database_url_errors.rs b/diesel_cli/tests/database_url_errors.rs
index ffa87b328c2b..35f6a182fa5c 100644
--- a/diesel_cli/tests/database_url_errors.rs
+++ b/diesel_cli/tests/database_url_errors.rs
@@ -25,9 +25,9 @@ fn missing_sqlite_panic_scheme() {
         .command_without_database_url("setup")
         .env("DATABASE_URL", "sqlite://example.db")
         .run();
-    assert!(result
-        .stderr()
-        .contains("panicked at 'Database url `sqlite://example.db` requires the `sqlite` feature but it's not enabled.'"));
+    assert!(result.stderr().contains(
+        "Database url `sqlite://example.db` requires the `sqlite` feature but it's not enabled."
+    ));
 }
 
 #[test]
@@ -38,9 +38,9 @@ fn missing_postgres_panic_postgres() {
         .command_without_database_url("setup")
         .env("DATABASE_URL", "postgres://localhost")
         .run();
-    assert!(result
-        .stderr()
-        .contains("panicked at 'Database url `postgres://localhost` requires the `postgres` feature but it's not enabled.'"));
+    assert!(result.stderr().contains(
+        "Database url `postgres://localhost` requires the `postgres` feature but it's not enabled."
+    ));
 }
 
 #[test]
@@ -53,7 +53,7 @@ fn missing_postgres_panic_postgresql() {
         .run();
     assert!(result
         .stderr()
-        .contains("panicked at 'Database url `postgresql://localhost` requires the `postgres` feature but it's not enabled.'"));
+        .contains("Database url `postgresql://localhost` requires the `postgres` feature but it's not enabled."));
 }
 
 #[test]
@@ -64,9 +64,9 @@ fn missing_mysql_panic() {
         .command_without_database_url("setup")
         .env("DATABASE_URL", "mysql://localhost")
         .run();
-    assert!(result
-        .stderr()
-        .contains("panicked at 'Database url `mysql://localhost` requires the `mysql` feature but it's not enabled.'"));
+    assert!(result.stderr().contains(
+        "Database url `mysql://localhost` requires the `mysql` feature but it's not enabled."
+    ));
 }
 
 #[test]
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/diesel.toml b/diesel_cli/tests/generate_migrations/diff_except_tables/diesel.toml
new file mode 100644
index 000000000000..885aaed16632
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/diesel.toml
@@ -0,0 +1,2 @@
+[print_schema]
+filter = { except_tables = ["table_b", "table_c"] }
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/down.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/down.sql/expected.snap
new file mode 100644
index 000000000000..97ad299447dd
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/down.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_except_tables"
+---
+-- This file should undo anything in `up.sql`
+ALTER TABLE `table_a` DROP COLUMN `script`;
+ALTER TABLE `table_a` ADD COLUMN `code` TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/initial_schema.sql b/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/initial_schema.sql
new file mode 100644
index 000000000000..aa1640d6ea45
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/initial_schema.sql
@@ -0,0 +1,3 @@
+CREATE TABLE table_a(id INTEGER NOT NULL PRIMARY KEY, code TEXT NOT NULL);
+CREATE TABLE table_b(id INTEGER NOT NULL PRIMARY KEY);
+CREATE TABLE table_c(id INTEGER NOT NULL PRIMARY KEY);
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/schema_out.rs/expected.snap b/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/schema_out.rs/expected.snap
new file mode 100644
index 000000000000..6a964649cc86
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/schema_out.rs/expected.snap
@@ -0,0 +1,13 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_except_tables"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    table_a (id) {
+        id -> Integer,
+        script -> Text,
+    }
+}
+
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/up.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/up.sql/expected.snap
new file mode 100644
index 000000000000..0c3cb1b956a4
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/mysql/up.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_except_tables"
+---
+-- Your SQL goes here
+ALTER TABLE `table_a` DROP COLUMN `code`;
+ALTER TABLE `table_a` ADD COLUMN `script` TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/down.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/down.sql/expected.snap
new file mode 100644
index 000000000000..39788a83da29
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/down.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_except_tables"
+---
+-- This file should undo anything in `up.sql`
+ALTER TABLE "table_a" DROP COLUMN "script";
+ALTER TABLE "table_a" ADD COLUMN "code" TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/initial_schema.sql b/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/initial_schema.sql
new file mode 100644
index 000000000000..aa1640d6ea45
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/initial_schema.sql
@@ -0,0 +1,3 @@
+CREATE TABLE table_a(id INTEGER NOT NULL PRIMARY KEY, code TEXT NOT NULL);
+CREATE TABLE table_b(id INTEGER NOT NULL PRIMARY KEY);
+CREATE TABLE table_c(id INTEGER NOT NULL PRIMARY KEY);
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/schema_out.rs/expected.snap b/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/schema_out.rs/expected.snap
new file mode 100644
index 000000000000..a5c7110333b3
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/schema_out.rs/expected.snap
@@ -0,0 +1,13 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_except_tables"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    table_a (id) {
+        id -> Int4,
+        script -> Text,
+    }
+}
+
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/up.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/up.sql/expected.snap
new file mode 100644
index 000000000000..4aee338f541d
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/postgres/up.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_except_tables"
+---
+-- Your SQL goes here
+ALTER TABLE "table_a" DROP COLUMN "code";
+ALTER TABLE "table_a" ADD COLUMN "script" TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/schema.rs b/diesel_cli/tests/generate_migrations/diff_except_tables/schema.rs
new file mode 100644
index 000000000000..eea0858b09b3
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/schema.rs
@@ -0,0 +1,6 @@
+diesel::table! {
+    table_a (id) {
+        id -> Integer,
+        script -> Text,
+    }
+}
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/down.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/down.sql/expected.snap
new file mode 100644
index 000000000000..97ad299447dd
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/down.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_except_tables"
+---
+-- This file should undo anything in `up.sql`
+ALTER TABLE `table_a` DROP COLUMN `script`;
+ALTER TABLE `table_a` ADD COLUMN `code` TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/initial_schema.sql b/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/initial_schema.sql
new file mode 100644
index 000000000000..aa1640d6ea45
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/initial_schema.sql
@@ -0,0 +1,3 @@
+CREATE TABLE table_a(id INTEGER NOT NULL PRIMARY KEY, code TEXT NOT NULL);
+CREATE TABLE table_b(id INTEGER NOT NULL PRIMARY KEY);
+CREATE TABLE table_c(id INTEGER NOT NULL PRIMARY KEY);
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/schema_out.rs/expected.snap b/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/schema_out.rs/expected.snap
new file mode 100644
index 000000000000..6a964649cc86
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/schema_out.rs/expected.snap
@@ -0,0 +1,13 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_except_tables"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    table_a (id) {
+        id -> Integer,
+        script -> Text,
+    }
+}
+
diff --git a/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/up.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/up.sql/expected.snap
new file mode 100644
index 000000000000..0c3cb1b956a4
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_except_tables/sqlite/up.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_except_tables"
+---
+-- Your SQL goes here
+ALTER TABLE `table_a` DROP COLUMN `code`;
+ALTER TABLE `table_a` ADD COLUMN `script` TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/diesel.toml b/diesel_cli/tests/generate_migrations/diff_only_tables/diesel.toml
new file mode 100644
index 000000000000..66ee2f8dc43f
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/diesel.toml
@@ -0,0 +1,2 @@
+[print_schema]
+filter = { only_tables = ["table_a"] }
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/down.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/down.sql/expected.snap
new file mode 100644
index 000000000000..6a5aef194160
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/down.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_only_tables"
+---
+-- This file should undo anything in `up.sql`
+ALTER TABLE `table_a` DROP COLUMN `script`;
+ALTER TABLE `table_a` ADD COLUMN `code` TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/initial_schema.sql b/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/initial_schema.sql
new file mode 100644
index 000000000000..aa1640d6ea45
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/initial_schema.sql
@@ -0,0 +1,3 @@
+CREATE TABLE table_a(id INTEGER NOT NULL PRIMARY KEY, code TEXT NOT NULL);
+CREATE TABLE table_b(id INTEGER NOT NULL PRIMARY KEY);
+CREATE TABLE table_c(id INTEGER NOT NULL PRIMARY KEY);
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/schema_out.rs/expected.snap b/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/schema_out.rs/expected.snap
new file mode 100644
index 000000000000..f9623dc70f1d
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/schema_out.rs/expected.snap
@@ -0,0 +1,13 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_only_tables"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    table_a (id) {
+        id -> Integer,
+        script -> Text,
+    }
+}
+
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/up.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/up.sql/expected.snap
new file mode 100644
index 000000000000..5aa41c0e26a4
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/mysql/up.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_only_tables"
+---
+-- Your SQL goes here
+ALTER TABLE `table_a` DROP COLUMN `code`;
+ALTER TABLE `table_a` ADD COLUMN `script` TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/down.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/down.sql/expected.snap
new file mode 100644
index 000000000000..a9e89d29b22a
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/down.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_only_tables"
+---
+-- This file should undo anything in `up.sql`
+ALTER TABLE "table_a" DROP COLUMN "script";
+ALTER TABLE "table_a" ADD COLUMN "code" TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/initial_schema.sql b/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/initial_schema.sql
new file mode 100644
index 000000000000..aa1640d6ea45
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/initial_schema.sql
@@ -0,0 +1,3 @@
+CREATE TABLE table_a(id INTEGER NOT NULL PRIMARY KEY, code TEXT NOT NULL);
+CREATE TABLE table_b(id INTEGER NOT NULL PRIMARY KEY);
+CREATE TABLE table_c(id INTEGER NOT NULL PRIMARY KEY);
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/schema_out.rs/expected.snap b/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/schema_out.rs/expected.snap
new file mode 100644
index 000000000000..1356f0468ec6
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/schema_out.rs/expected.snap
@@ -0,0 +1,13 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_only_tables"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    table_a (id) {
+        id -> Int4,
+        script -> Text,
+    }
+}
+
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/up.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/up.sql/expected.snap
new file mode 100644
index 000000000000..bac54f32c019
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/postgres/up.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_only_tables"
+---
+-- Your SQL goes here
+ALTER TABLE "table_a" DROP COLUMN "code";
+ALTER TABLE "table_a" ADD COLUMN "script" TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/schema.rs b/diesel_cli/tests/generate_migrations/diff_only_tables/schema.rs
new file mode 100644
index 000000000000..eea0858b09b3
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/schema.rs
@@ -0,0 +1,6 @@
+diesel::table! {
+    table_a (id) {
+        id -> Integer,
+        script -> Text,
+    }
+}
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/down.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/down.sql/expected.snap
new file mode 100644
index 000000000000..6a5aef194160
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/down.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_only_tables"
+---
+-- This file should undo anything in `up.sql`
+ALTER TABLE `table_a` DROP COLUMN `script`;
+ALTER TABLE `table_a` ADD COLUMN `code` TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/initial_schema.sql b/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/initial_schema.sql
new file mode 100644
index 000000000000..aa1640d6ea45
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/initial_schema.sql
@@ -0,0 +1,3 @@
+CREATE TABLE table_a(id INTEGER NOT NULL PRIMARY KEY, code TEXT NOT NULL);
+CREATE TABLE table_b(id INTEGER NOT NULL PRIMARY KEY);
+CREATE TABLE table_c(id INTEGER NOT NULL PRIMARY KEY);
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/schema_out.rs/expected.snap b/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/schema_out.rs/expected.snap
new file mode 100644
index 000000000000..f9623dc70f1d
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/schema_out.rs/expected.snap
@@ -0,0 +1,13 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_only_tables"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    table_a (id) {
+        id -> Integer,
+        script -> Text,
+    }
+}
+
diff --git a/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/up.sql/expected.snap b/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/up.sql/expected.snap
new file mode 100644
index 000000000000..5aa41c0e26a4
--- /dev/null
+++ b/diesel_cli/tests/generate_migrations/diff_only_tables/sqlite/up.sql/expected.snap
@@ -0,0 +1,9 @@
+---
+source: diesel_cli/tests/migration_generate.rs
+description: "Test: diff_only_tables"
+---
+-- Your SQL goes here
+ALTER TABLE `table_a` DROP COLUMN `code`;
+ALTER TABLE `table_a` ADD COLUMN `script` TEXT NOT NULL;
+
+
diff --git a/diesel_cli/tests/migration_generate.rs b/diesel_cli/tests/migration_generate.rs
index c3402993dece..68ea79808485 100644
--- a/diesel_cli/tests/migration_generate.rs
+++ b/diesel_cli/tests/migration_generate.rs
@@ -4,7 +4,7 @@ use std::{fs::File, io::Read};
 use chrono::prelude::*;
 use regex::Regex;
 
-use crate::support::project;
+use crate::support::{project, Project};
 pub static TIMESTAMP_FORMAT: &str = "%Y-%m-%d-%H%M%S";
 
 #[test]
@@ -260,6 +260,16 @@ fn migration_generate_from_diff_drop_table_composite_key() {
     test_generate_migration("diff_drop_table_composite_key", Vec::new());
 }
 
+#[test]
+fn migration_generate_from_diff_only_tables() {
+    test_generate_migration("diff_only_tables", vec!["-o", "table_a"]);
+}
+
+#[test]
+fn migration_generate_from_diff_except_tables() {
+    test_generate_migration("diff_except_tables", vec!["-e", "table_b", "table_c"]);
+}
+
 #[cfg(feature = "sqlite")]
 const BACKEND: &str = "sqlite";
 #[cfg(feature = "postgres")]
@@ -278,6 +288,24 @@ fn backend_file_path(test_name: &str, file: &str) -> PathBuf {
 
 fn test_generate_migration(test_name: &str, args: Vec<&str>) {
     let p = project(test_name).build();
+    run_generate_migration_test(test_name, args, p);
+
+    let config_path = Path::new(env!("CARGO_MANIFEST_DIR"))
+        .join("tests")
+        .join("generate_migrations")
+        .join(test_name)
+        .join("diesel.toml");
+
+    if Path::new(&config_path).exists() {
+        let p = project(test_name)
+            .file("diesel.toml", &read_file(&config_path))
+            .build();
+
+        run_generate_migration_test(test_name, Vec::new(), p);
+    }
+}
+
+fn run_generate_migration_test(test_name: &str, args: Vec<&str>, p: Project) {
     let db = crate::support::database(&p.database_url());
 
     p.command("setup").run();
@@ -306,7 +334,7 @@ fn test_generate_migration(test_name: &str, args: Vec<&str>) {
             "--diff-schema={schema_rs}",
             schema_rs = schema_rs.display()
         ))
-        .args(args)
+        .args(args.clone())
         .run();
 
     assert!(result.is_success(), "Result was unsuccessful {:?}", result);
@@ -338,7 +366,7 @@ fn test_generate_migration(test_name: &str, args: Vec<&str>) {
     assert!(result.is_success(), "Result was unsuccessful {:?}", result);
 
     // check that we get back the expected schema
-    let result = p.command("print-schema").run();
+    let result = p.command("print-schema").args(args).run();
     assert!(result.is_success(), "Result was unsuccessful {:?}", result);
     let result = result.stdout().replace("\r\n", "\n");
 
diff --git a/diesel_cli/tests/print_schema.rs b/diesel_cli/tests/print_schema.rs
index 577d847511f8..c91cf4a8b989 100644
--- a/diesel_cli/tests/print_schema.rs
+++ b/diesel_cli/tests/print_schema.rs
@@ -250,6 +250,12 @@ fn print_schema_generated_columns_with_generated_always() {
     test_print_schema("print_schema_generated_columns_generated_always", vec![])
 }
 
+#[test]
+#[cfg(feature = "postgres")]
+fn print_schema_multiple_annotations() {
+    test_print_schema("print_schema_multiple_annotations", vec![])
+}
+
 #[test]
 #[cfg(feature = "postgres")]
 fn print_schema_array_type() {
@@ -315,6 +321,23 @@ fn print_schema_several_keys_with_compound_key() {
     test_print_schema("print_schema_several_keys_with_compound_key", vec![])
 }
 
+// some mysql versions concert quoted table names to lowercase
+// anyway
+#[cfg(any(feature = "postgres", feature = "sqlite"))]
+#[test]
+fn print_schema_quoted_table_name() {
+    test_print_schema("print_schema_quoted_table_name", vec![])
+}
+
+#[cfg(feature = "postgres")]
+#[test]
+fn print_schema_quoted_schema_and_table_name() {
+    test_print_schema(
+        "print_schema_quoted_schema_and_table_name",
+        vec!["--schema", "CustomSchema"],
+    )
+}
+
 #[cfg(feature = "sqlite")]
 const BACKEND: &str = "sqlite";
 #[cfg(feature = "postgres")]
diff --git a/diesel_cli/tests/print_schema/print_schema_multiple_annotations/diesel.toml b/diesel_cli/tests/print_schema/print_schema_multiple_annotations/diesel.toml
new file mode 100644
index 000000000000..3985231b860a
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_multiple_annotations/diesel.toml
@@ -0,0 +1,3 @@
+[print_schema]
+file = "src/schema.rs"
+with_docs = false
diff --git a/diesel_cli/tests/print_schema/print_schema_multiple_annotations/postgres/expected.snap b/diesel_cli/tests/print_schema/print_schema_multiple_annotations/postgres/expected.snap
new file mode 100644
index 000000000000..dacb7bcf46b2
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_multiple_annotations/postgres/expected.snap
@@ -0,0 +1,15 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_multiple_annotations"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    example (id) {
+        id -> Int4,
+        #[sql_name = "type"]
+        #[max_length = 10]
+        type_ -> Nullable<Varchar>,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_multiple_annotations/postgres/schema.sql b/diesel_cli/tests/print_schema/print_schema_multiple_annotations/postgres/schema.sql
new file mode 100644
index 000000000000..e7099ebf6626
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_multiple_annotations/postgres/schema.sql
@@ -0,0 +1,4 @@
+CREATE TABLE example (
+    id SERIAL PRIMARY KEY,
+    "type" VARCHAR(10)
+);
diff --git a/diesel_cli/tests/print_schema/print_schema_quoted_schema_and_table_name/diesel.toml b/diesel_cli/tests/print_schema/print_schema_quoted_schema_and_table_name/diesel.toml
new file mode 100644
index 000000000000..ad5457677877
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_quoted_schema_and_table_name/diesel.toml
@@ -0,0 +1,3 @@
+[print_schema]
+file = "src/schema.rs"
+schema = "CustomSchema"
diff --git a/diesel_cli/tests/print_schema/print_schema_quoted_schema_and_table_name/postgres/expected.snap b/diesel_cli/tests/print_schema/print_schema_quoted_schema_and_table_name/postgres/expected.snap
new file mode 100644
index 000000000000..9022c22b4da9
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_quoted_schema_and_table_name/postgres/expected.snap
@@ -0,0 +1,14 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_quoted_schema_and_table_name"
+---
+// @generated automatically by Diesel CLI.
+
+pub mod CustomSchema {
+    diesel::table! {
+        CustomSchema.User (id) {
+            id -> Int4,
+        }
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_quoted_schema_and_table_name/postgres/schema.sql b/diesel_cli/tests/print_schema/print_schema_quoted_schema_and_table_name/postgres/schema.sql
new file mode 100644
index 000000000000..90b508e2e51a
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_quoted_schema_and_table_name/postgres/schema.sql
@@ -0,0 +1,5 @@
+CREATE SCHEMA "CustomSchema";
+
+CREATE TABLE "CustomSchema"."User" (
+    id integer PRIMARY KEY NOT NULL
+);
diff --git a/diesel_cli/tests/print_schema/print_schema_quoted_table_name/diesel.toml b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/diesel.toml
new file mode 100644
index 000000000000..f57985adb185
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/diesel.toml
@@ -0,0 +1,2 @@
+[print_schema]
+file = "src/schema.rs"
diff --git a/diesel_cli/tests/print_schema/print_schema_quoted_table_name/postgres/expected.snap b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/postgres/expected.snap
new file mode 100644
index 000000000000..e2813cd9adb5
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/postgres/expected.snap
@@ -0,0 +1,12 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_quoted_table_name"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    User (id) {
+        id -> Int4,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_quoted_table_name/postgres/schema.sql b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/postgres/schema.sql
new file mode 100644
index 000000000000..5ab84f70c7ee
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/postgres/schema.sql
@@ -0,0 +1,3 @@
+CREATE TABLE "User" (
+    id integer PRIMARY KEY NOT NULL
+);
diff --git a/diesel_cli/tests/print_schema/print_schema_quoted_table_name/sqlite/expected.snap b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/sqlite/expected.snap
new file mode 100644
index 000000000000..2f59dc08d2fe
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/sqlite/expected.snap
@@ -0,0 +1,12 @@
+---
+source: diesel_cli/tests/print_schema.rs
+description: "Test: print_schema_quoted_table_name"
+---
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+    User (id) {
+        id -> Integer,
+    }
+}
+
diff --git a/diesel_cli/tests/print_schema/print_schema_quoted_table_name/sqlite/schema.sql b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/sqlite/schema.sql
new file mode 100644
index 000000000000..5ab84f70c7ee
--- /dev/null
+++ b/diesel_cli/tests/print_schema/print_schema_quoted_table_name/sqlite/schema.sql
@@ -0,0 +1,3 @@
+CREATE TABLE "User" (
+    id integer PRIMARY KEY NOT NULL
+);
diff --git a/diesel_cli/tests/support/mod.rs b/diesel_cli/tests/support/mod.rs
index 56f407bf9cce..10e86f9b2ff2 100644
--- a/diesel_cli/tests/support/mod.rs
+++ b/diesel_cli/tests/support/mod.rs
@@ -30,7 +30,7 @@ mod postgres_database;
 #[cfg(rustfmt)]
 mod sqlite_database;
 
-pub use self::project_builder::project;
+pub use self::project_builder::{project, Project};
 
 pub fn database(url: &str) -> database::Database {
     database::Database::new(url)
diff --git a/diesel_cli/tests/support/project_builder.rs b/diesel_cli/tests/support/project_builder.rs
index e1eeada3bff1..c1199b1aba17 100644
--- a/diesel_cli/tests/support/project_builder.rs
+++ b/diesel_cli/tests/support/project_builder.rs
@@ -102,8 +102,8 @@ impl Project {
         use std::env;
         dotenv().ok();
 
-        let mut db_url =
-            url::Url::parse(&env::var_os(var).unwrap().into_string().unwrap()).unwrap();
+        let var_os = env::var(var).unwrap();
+        let mut db_url = url::Url::parse(&var_os).unwrap();
         db_url.set_path(&format!("/diesel_{}", &self.name));
         db_url
     }
diff --git a/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.rs b/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.rs
index b6fbbbb30cb6..af2067314858 100644
--- a/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.rs
+++ b/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.rs
@@ -69,6 +69,11 @@ fn main() {
         .distinct_on(users::name)
         .into_boxed();
 
+    // order by and distinct on with sql literal
+    let _ = users::table
+        .order(dsl::sql::<sql_types::Bool>("name"))
+        .distinct_on(dsl::sql::<sql_types::Bool>("name"));
+
     // verify that this all works with boxed queries
     let _ = users::table.distinct_on(users::name).into_boxed();
     let _ = users::table
@@ -96,6 +101,11 @@ fn main() {
         .then_order_by(users::id)
         .distinct_on(users::name)
         .into_boxed();
+    // order by and distinct on with sql literal
+    let _ = users::table
+        .order(dsl::sql::<sql_types::Bool>("name"))
+        .distinct_on(dsl::sql::<sql_types::Bool>("name"))
+        .into_boxed();
 
     // compile fail section
     //
diff --git a/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.stderr b/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.stderr
index 780196688dbc..1ad308ba0257 100644
--- a/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.stderr
+++ b/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.stderr
@@ -1,20 +1,20 @@
 error[E0277]: the trait bound `diesel::query_builder::order_clause::OrderClause<columns::id>: query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>` is not satisfied
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:103:58
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:113:58
     |
-103 |     let _ = users::table.order_by(users::id).distinct_on(users::name);
+113 |     let _ = users::table.order_by(users::id).distinct_on(users::name);
     |                                              ----------- ^^^^^^^^^^^ the trait `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>` is not implemented for `diesel::query_builder::order_clause::OrderClause<columns::id>`
     |                                              |
     |                                              required by a bound introduced by this call
     |
     = help: the following other types implement trait `query_dsl::order_dsl::ValidOrderingForDistinct<D>`:
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3, ST4)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3, T4)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0,)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T1>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST4, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3, T4)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2,)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T2>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST3, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2, T3)>>>
             and $N others
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, ..., ...>` to implement `DistinctOnDsl<columns::name>`
 note: required by a bound in `diesel::QueryDsl::distinct_on`
@@ -24,40 +24,40 @@ note: required by a bound in `diesel::QueryDsl::distinct_on`
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `QueryDsl::distinct_on`
 
 error[E0271]: type mismatch resolving `<id as OrderDecorator>::Column == name`
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:107:61
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:117:61
     |
-107 |     let _ = users::table.order_by((users::id, users::name)).distinct_on(users::name);
+117 |     let _ = users::table.order_by((users::id, users::name)).distinct_on(users::name);
     |                                                             ^^^^^^^^^^^ expected struct `id`, found struct `name`
     |
     = note: required for `diesel::query_builder::order_clause::OrderClause<(columns::id, columns::name)>` to implement `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>`
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, ..., ...>` to implement `DistinctOnDsl<columns::name>`
 
 error[E0271]: type mismatch resolving `<id as OrderDecorator>::Column == name`
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:113:10
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:123:10
     |
-113 |         .distinct_on(users::name);
+123 |         .distinct_on(users::name);
     |          ^^^^^^^^^^^ expected struct `id`, found struct `name`
     |
     = note: required for `diesel::query_builder::order_clause::OrderClause<(columns::id, columns::name)>` to implement `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>`
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, ..., ...>` to implement `DistinctOnDsl<columns::name>`
 
 error[E0277]: the trait bound `diesel::query_builder::order_clause::OrderClause<columns::id>: query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>` is not satisfied
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:117:60
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:127:60
     |
-117 |     let _ = users::table.distinct_on(users::name).order_by(users::id);
+127 |     let _ = users::table.distinct_on(users::name).order_by(users::id);
     |                                                   -------- ^^^^^^^^^ the trait `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>` is not implemented for `diesel::query_builder::order_clause::OrderClause<columns::id>`
     |                                                   |
     |                                                   required by a bound introduced by this call
     |
     = help: the following other types implement trait `query_dsl::order_dsl::ValidOrderingForDistinct<D>`:
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3, ST4)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3, T4)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0,)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T1>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST4, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3, T4)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2,)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T2>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST3, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2, T3)>>>
             and $N others
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, DistinctOnClause<name>>` to implement `OrderDsl<columns::id>`
 note: required by a bound in `order_by`
@@ -67,22 +67,22 @@ note: required by a bound in `order_by`
     |               ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `QueryDsl::order_by`
 
 error[E0277]: the trait bound `diesel::query_builder::order_clause::OrderClause<columns::id>: query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>` is not satisfied
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:120:58
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:130:58
     |
-120 |     let _ = users::table.order_by(users::id).distinct_on(users::name).into_boxed();
+130 |     let _ = users::table.order_by(users::id).distinct_on(users::name).into_boxed();
     |                                              ----------- ^^^^^^^^^^^ the trait `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>` is not implemented for `diesel::query_builder::order_clause::OrderClause<columns::id>`
     |                                              |
     |                                              required by a bound introduced by this call
     |
     = help: the following other types implement trait `query_dsl::order_dsl::ValidOrderingForDistinct<D>`:
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3, ST4)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3, T4)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0,)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T1>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST4, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3, T4)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2,)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T2>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST3, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2, T3)>>>
             and $N others
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, ..., ...>` to implement `DistinctOnDsl<columns::name>`
 note: required by a bound in `diesel::QueryDsl::distinct_on`
@@ -92,31 +92,31 @@ note: required by a bound in `diesel::QueryDsl::distinct_on`
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `QueryDsl::distinct_on`
 
 error[E0271]: type mismatch resolving `<id as OrderDecorator>::Column == name`
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:126:10
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:136:10
     |
-126 |         .distinct_on(users::name)
+136 |         .distinct_on(users::name)
     |          ^^^^^^^^^^^ expected struct `id`, found struct `name`
     |
     = note: required for `diesel::query_builder::order_clause::OrderClause<(columns::id, columns::name)>` to implement `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>`
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, ..., ...>` to implement `DistinctOnDsl<columns::name>`
 
 error[E0277]: the trait bound `diesel::query_builder::order_clause::OrderClause<(columns::id, columns::name)>: query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(columns::name, columns::id)>>` is not satisfied
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:133:22
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:143:22
     |
-133 |         .distinct_on((users::name, users::id))
+143 |         .distinct_on((users::name, users::id))
     |          ----------- ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(columns::name, columns::id)>>` is not implemented for `diesel::query_builder::order_clause::OrderClause<(columns::id, columns::name)>`
     |          |
     |          required by a bound introduced by this call
     |
     = help: the following other types implement trait `query_dsl::order_dsl::ValidOrderingForDistinct<D>`:
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3, ST4)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3, T4)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0,)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T1>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST4, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3, T4)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2,)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T2>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST3, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2, T3)>>>
             and $N others
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, ..., ...>` to implement `DistinctOnDsl<(columns::name, columns::id)>`
 note: required by a bound in `diesel::QueryDsl::distinct_on`
@@ -126,40 +126,40 @@ note: required by a bound in `diesel::QueryDsl::distinct_on`
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `QueryDsl::distinct_on`
 
 error[E0271]: type mismatch resolving `<id as OrderDecorator>::Column == name`
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:140:10
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:150:10
     |
-140 |         .distinct_on((users::name, users::id))
+150 |         .distinct_on((users::name, users::id))
     |          ^^^^^^^^^^^ expected struct `id`, found struct `name`
     |
     = note: required for `diesel::query_builder::order_clause::OrderClause<columns::id>` to implement `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(columns::name, columns::id)>>`
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, ..., ...>` to implement `DistinctOnDsl<(columns::name, columns::id)>`
 
 error[E0271]: type mismatch resolving `<id as OrderDecorator>::Column == name`
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:147:10
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:157:10
     |
-147 |         .distinct_on(users::name)
+157 |         .distinct_on(users::name)
     |          ^^^^^^^^^^^ expected struct `id`, found struct `name`
     |
     = note: required for `diesel::query_builder::order_clause::OrderClause<(columns::id, columns::name)>` to implement `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>`
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, ..., ...>` to implement `DistinctOnDsl<columns::name>`
 
 error[E0277]: the trait bound `diesel::query_builder::order_clause::OrderClause<columns::id>: query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>` is not satisfied
-   --> tests/fail/distinct_on_requires_matching_order_clause.rs:154:19
+   --> tests/fail/distinct_on_requires_matching_order_clause.rs:164:19
     |
-154 |         .order_by(users::id)
+164 |         .order_by(users::id)
     |          -------- ^^^^^^^^^ the trait `query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<columns::name>>` is not implemented for `diesel::query_builder::order_clause::OrderClause<columns::id>`
     |          |
     |          required by a bound introduced by this call
     |
     = help: the following other types implement trait `query_dsl::order_dsl::ValidOrderingForDistinct<D>`:
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0, ST1, ST2, ST3, ST4)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0, T1, T2, T3, T4)>>>
-              <diesel::query_builder::order_clause::OrderClause<(ST0,)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T0,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1,)>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T1>>>
               <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST1, ST2, ST3, ST4, ST0)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T1, T2, T3, T4)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2,)>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<T2>>>
+              <diesel::query_builder::order_clause::OrderClause<(ST2, ST3, ST0, ST1)> as query_dsl::order_dsl::ValidOrderingForDistinct<DistinctOnClause<(T2, T3)>>>
             and $N others
     = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, DistinctOnClause<name>>` to implement `OrderDsl<columns::id>`
 note: required by a bound in `order_by`
diff --git a/diesel_compile_tests/tests/fail/table_without_primary_key.rs b/diesel_compile_tests/tests/fail/table_without_primary_key.rs
new file mode 100644
index 000000000000..691c75b69242
--- /dev/null
+++ b/diesel_compile_tests/tests/fail/table_without_primary_key.rs
@@ -0,0 +1,10 @@
+#[macro_use] extern crate diesel;
+
+table! {
+    user {
+        user_id -> Integer,
+        name -> Text,
+    }
+}
+
+fn main() {}
diff --git a/diesel_compile_tests/tests/fail/table_without_primary_key.stderr b/diesel_compile_tests/tests/fail/table_without_primary_key.stderr
new file mode 100644
index 000000000000..832158cb7a6d
--- /dev/null
+++ b/diesel_compile_tests/tests/fail/table_without_primary_key.stderr
@@ -0,0 +1,14 @@
+error: Neither an explict primary key found nor does an `id` column exist.
+       Consider explicitly defining a primary key.
+       For example for specifying `user_id` as primary key:
+
+       table! {
+           user (user_id) {
+               user_id -> Integer,
+               name -> Text,
+           }
+       }
+ --> tests/fail/table_without_primary_key.rs:4:5
+  |
+4 |     user {
+  |     ^^^^
diff --git a/diesel_derives/tests/helpers.rs b/diesel_derives/tests/helpers.rs
index 361f68d09ca8..79715735f8c7 100644
--- a/diesel_derives/tests/helpers.rs
+++ b/diesel_derives/tests/helpers.rs
@@ -1,6 +1,9 @@
 use diesel::prelude::*;
 use diesel::sql_query;
 
+#[allow(dead_code)] // that's used in one of the compile tests
+pub type TestBackend = <TestConnection as diesel::Connection>::Backend;
+
 cfg_if! {
     if #[cfg(feature = "sqlite")] {
         pub type TestConnection = SqliteConnection;
diff --git a/diesel_derives/tests/multiconnection.rs b/diesel_derives/tests/multiconnection.rs
index 56f3249c69c6..51c93836d116 100644
--- a/diesel_derives/tests/multiconnection.rs
+++ b/diesel_derives/tests/multiconnection.rs
@@ -19,16 +19,7 @@ pub struct User {
 
 #[test]
 fn check_queries_work() {
-    let database_url = if cfg!(feature = "mysql") {
-        dotenvy::var("MYSQL_UNIT_TEST_DATABASE_URL").or_else(|_| dotenvy::var("DATABASE_URL"))
-    } else if cfg!(feature = "postgres") {
-        dotenvy::var("PG_DATABASE_URL").or_else(|_| dotenvy::var("DATABASE_URL"))
-    } else {
-        Ok(dotenvy::var("DATABASE_URL").unwrap_or_else(|_| ":memory:".to_owned()))
-    };
-    let database_url = database_url.expect("DATABASE_URL must be set in order to run tests");
-
-    let mut conn = InferConnection::establish(&database_url).unwrap();
+    let mut conn = establish_connection();
 
     diesel::sql_query(
         "CREATE TEMPORARY TABLE users(\
@@ -106,3 +97,324 @@ fn check_queries_work() {
     // delete
     diesel::delete(users::table).execute(&mut conn).unwrap();
 }
+
+fn establish_connection() -> InferConnection {
+    let database_url = if cfg!(feature = "mysql") {
+        dotenvy::var("MYSQL_UNIT_TEST_DATABASE_URL").or_else(|_| dotenvy::var("DATABASE_URL"))
+    } else if cfg!(feature = "postgres") {
+        dotenvy::var("PG_DATABASE_URL").or_else(|_| dotenvy::var("DATABASE_URL"))
+    } else {
+        Ok(dotenvy::var("DATABASE_URL").unwrap_or_else(|_| ":memory:".to_owned()))
+    };
+    let database_url = database_url.expect("DATABASE_URL must be set in order to run tests");
+
+    InferConnection::establish(&database_url).unwrap()
+}
+
+#[cfg(all(feature = "chrono", feature = "time"))]
+fn make_test_table(conn: &mut InferConnection) {
+    match conn {
+        #[cfg(feature = "postgres")]
+        InferConnection::Pg(ref mut conn) => {
+            diesel::sql_query(
+                "CREATE TEMPORARY TABLE type_test( \
+                     small_int SMALLINT,\
+                     integer INTEGER,\
+                     big_int BIGINT,\
+                     float FLOAT4,\
+                     double FLOAT8,\
+                     string TEXT,\
+                     blob BYTEA,\
+                     timestamp1 TIMESTAMP,\
+                     date1 DATE,\
+                     time1 TIME,\
+                     timestamp2 TIMESTAMP,\
+                     date2 DATE,\
+                     time2 TIME
+                 )",
+            )
+            .execute(conn)
+            .unwrap();
+        }
+        #[cfg(feature = "sqlite")]
+        InferConnection::Sqlite(ref mut conn) => {
+            diesel::sql_query(
+                "CREATE TEMPORARY TABLE type_test( \
+                     small_int SMALLINT,\
+                     integer INTEGER,\
+                     big_int BIGINT,\
+                     float FLOAT4,\
+                     double FLOAT8,\
+                     string TEXT,\
+                     blob BLOB,\
+                     timestamp1 TIMESTAMP,\
+                     date1 DATE,\
+                     time1 TIME,\
+                     timestamp2 TIMESTAMP,\
+                     date2 DATE,\
+                     time2 TIME
+                 )",
+            )
+            .execute(conn)
+            .unwrap();
+        }
+        #[cfg(feature = "mysql")]
+        InferConnection::Mysql(ref mut conn) => {
+            diesel::sql_query(
+                "CREATE TEMPORARY TABLE type_test( \
+                     `small_int` SMALLINT,\
+                     `integer` INT,\
+                     `big_int` BIGINT,\
+                     `float` FLOAT,\
+                     `double` DOUBLE,\
+                     `string` TEXT,\
+                     `blob` BLOB,\
+                     `timestamp1` DATETIME,
+                     `date1` DATE,\
+                     `time1` TIME,\
+                     `timestamp2` DATETIME,
+                     `date2` DATE,\
+                     `time2` TIME
+                 )",
+            )
+            .execute(conn)
+            .unwrap();
+        }
+    }
+}
+
+#[cfg(all(feature = "chrono", feature = "time"))]
+#[test]
+fn type_checks() {
+    use diesel::internal::derives::multiconnection::{chrono, time};
+
+    table! {
+        type_test(integer) {
+            small_int -> SmallInt,
+            integer -> Integer,
+            big_int -> BigInt,
+            float -> Float,
+            double -> Double,
+            string -> Text,
+            blob -> Blob,
+            timestamp1 -> Timestamp,
+            time1 -> Time,
+            date1 -> Date,
+            timestamp2 -> Timestamp,
+            time2 -> Time,
+            date2 -> Date,
+        }
+    }
+
+    let mut conn = establish_connection();
+    make_test_table(&mut conn);
+    conn.begin_test_transaction().unwrap();
+    let small_int = 1_i16;
+    let integer = 2_i32;
+    let big_int = 3_i64;
+    let float = 4.0_f32;
+    let double = 5.0_f64;
+    let string = String::from("bar");
+    let blob = vec![1_u8, 2, 3, 4];
+    let date1 = chrono::NaiveDate::from_ymd_opt(2023, 08, 17).unwrap();
+    let time1 = chrono::NaiveTime::from_hms_opt(07, 50, 12).unwrap();
+    let timestamp1 = chrono::NaiveDateTime::new(date1, time1);
+    let time2 = time::Time::from_hms(12, 22, 23).unwrap();
+    let date2 = time::Date::from_calendar_date(2023, time::Month::August, 26).unwrap();
+    let timestamp2 = time::PrimitiveDateTime::new(date2, time2);
+
+    diesel::insert_into(type_test::table)
+        .values((
+            type_test::small_int.eq(small_int),
+            type_test::integer.eq(integer),
+            type_test::big_int.eq(big_int),
+            type_test::float.eq(float),
+            type_test::double.eq(double),
+            type_test::string.eq(&string),
+            type_test::blob.eq(&blob),
+            type_test::timestamp1.eq(timestamp1),
+            type_test::time1.eq(time1),
+            type_test::date1.eq(date1),
+            type_test::timestamp2.eq(timestamp2),
+            type_test::time2.eq(time2),
+            type_test::date2.eq(date2),
+        ))
+        .execute(&mut conn)
+        .unwrap();
+
+    let result = type_test::table
+        .get_result::<(
+            i16,                     //0
+            i32,                     //1
+            i64,                     //2
+            f32,                     //3
+            f64,                     //4
+            String,                  //5
+            Vec<u8>,                 //6
+            chrono::NaiveDateTime,   //7
+            chrono::NaiveTime,       //8
+            chrono::NaiveDate,       //9
+            time::PrimitiveDateTime, //10
+            time::Time,              //11
+            time::Date,              //12
+        )>(&mut conn)
+        .unwrap();
+
+    assert_eq!(small_int, result.0);
+    assert_eq!(integer, result.1);
+    assert_eq!(big_int, result.2);
+    assert_eq!(float, result.3);
+    assert_eq!(double, result.4);
+    assert_eq!(string, result.5);
+    assert_eq!(blob, result.6);
+    assert_eq!(timestamp1, result.7);
+    assert_eq!(time1, result.8);
+    assert_eq!(date1, result.9);
+    assert_eq!(timestamp2, result.10);
+    assert_eq!(time2, result.11);
+    assert_eq!(date2, result.12);
+}
+
+#[cfg(all(feature = "chrono", feature = "time"))]
+#[test]
+fn nullable_type_checks() {
+    use diesel::internal::derives::multiconnection::{chrono, time};
+
+    table! {
+        type_test(integer) {
+            small_int -> Nullable<SmallInt>,
+            integer -> Nullable<Integer>,
+            big_int -> Nullable<BigInt>,
+            float -> Nullable<Float>,
+            double -> Nullable<Double>,
+            string -> Nullable<Text>,
+            blob -> Nullable<Blob>,
+            timestamp1 -> Nullable<Timestamp>,
+            time1 -> Nullable<Time>,
+            date1 -> Nullable<Date>,
+            timestamp2 -> Nullable<Timestamp>,
+            time2 -> Nullable<Time>,
+            date2 -> Nullable<Date>,
+        }
+    }
+
+    let mut conn = establish_connection();
+    make_test_table(&mut conn);
+    conn.begin_test_transaction().unwrap();
+
+    let small_int = Some(1_i16);
+    let integer = Some(2_i32);
+    let big_int = Some(3_i64);
+    let float = Some(4.0_f32);
+    let double = Some(5.0_f64);
+    let string = Some(String::from("bar"));
+    let blob = Some(vec![1_u8, 2, 3, 4]);
+    let date1 = Some(chrono::NaiveDate::from_ymd_opt(2023, 08, 17).unwrap());
+    let time1 = Some(chrono::NaiveTime::from_hms_opt(07, 50, 12).unwrap());
+    let timestamp1 = Some(chrono::NaiveDateTime::new(date1.unwrap(), time1.unwrap()));
+    let time2 = Some(time::Time::from_hms(12, 22, 23).unwrap());
+    let date2 = Some(time::Date::from_calendar_date(2023, time::Month::August, 26).unwrap());
+    let timestamp2 = Some(time::PrimitiveDateTime::new(date2.unwrap(), time2.unwrap()));
+
+    diesel::insert_into(type_test::table)
+        .values((
+            type_test::small_int.eq(small_int),
+            type_test::integer.eq(integer),
+            type_test::big_int.eq(big_int),
+            type_test::float.eq(float),
+            type_test::double.eq(double),
+            type_test::string.eq(&string),
+            type_test::blob.eq(&blob),
+            type_test::timestamp1.eq(timestamp1),
+            type_test::time1.eq(time1),
+            type_test::date1.eq(date1),
+            type_test::timestamp2.eq(timestamp2),
+            type_test::time2.eq(time2),
+            type_test::date2.eq(date2),
+        ))
+        .execute(&mut conn)
+        .unwrap();
+
+    let result = type_test::table
+        .get_result::<(
+            Option<i16>,
+            Option<i32>,
+            Option<i64>,
+            Option<f32>,
+            Option<f64>,
+            Option<String>,
+            Option<Vec<u8>>,
+            Option<chrono::NaiveDateTime>,
+            Option<chrono::NaiveTime>,
+            Option<chrono::NaiveDate>,
+            Option<time::PrimitiveDateTime>,
+            Option<time::Time>,
+            Option<time::Date>,
+        )>(&mut conn)
+        .unwrap();
+
+    assert_eq!(small_int, result.0);
+    assert_eq!(integer, result.1);
+    assert_eq!(big_int, result.2);
+    assert_eq!(float, result.3);
+    assert_eq!(double, result.4);
+    assert_eq!(string, result.5);
+    assert_eq!(blob, result.6);
+    assert_eq!(timestamp1, result.7);
+    assert_eq!(time1, result.8);
+    assert_eq!(date1, result.9);
+    assert_eq!(timestamp2, result.10);
+    assert_eq!(time2, result.11);
+    assert_eq!(date2, result.12);
+
+    diesel::delete(type_test::table).execute(&mut conn).unwrap();
+
+    diesel::insert_into(type_test::table)
+        .values((
+            type_test::small_int.eq(None::<i16>),
+            type_test::integer.eq(None::<i32>),
+            type_test::big_int.eq(None::<i64>),
+            type_test::float.eq(None::<f32>),
+            type_test::double.eq(None::<f64>),
+            type_test::string.eq(None::<String>),
+            type_test::blob.eq(None::<Vec<u8>>),
+            type_test::timestamp1.eq(None::<chrono::NaiveDateTime>),
+            type_test::time1.eq(None::<chrono::NaiveTime>),
+            type_test::date1.eq(None::<chrono::NaiveDate>),
+            type_test::timestamp2.eq(None::<time::PrimitiveDateTime>),
+            type_test::time2.eq(None::<time::Time>),
+            type_test::date2.eq(None::<time::Date>),
+        ))
+        .execute(&mut conn)
+        .unwrap();
+    let result = type_test::table
+        .get_result::<(
+            Option<i16>,
+            Option<i32>,
+            Option<i64>,
+            Option<f32>,
+            Option<f64>,
+            Option<String>,
+            Option<Vec<u8>>,
+            Option<chrono::NaiveDateTime>,
+            Option<chrono::NaiveTime>,
+            Option<chrono::NaiveDate>,
+            Option<time::PrimitiveDateTime>,
+            Option<time::Time>,
+            Option<time::Date>,
+        )>(&mut conn)
+        .unwrap();
+    assert!(result.0.is_none());
+    assert!(result.1.is_none());
+    assert!(result.2.is_none());
+    assert!(result.3.is_none());
+    assert!(result.4.is_none());
+    assert!(result.5.is_none());
+    assert!(result.6.is_none());
+    assert!(result.7.is_none());
+    assert!(result.8.is_none());
+    assert!(result.9.is_none());
+    assert!(result.10.is_none());
+    assert!(result.11.is_none());
+    assert!(result.12.is_none());
+}
diff --git a/diesel_derives/tests/selectable.rs b/diesel_derives/tests/selectable.rs
index a4aba7b49863..af1c73e4591c 100644
--- a/diesel_derives/tests/selectable.rs
+++ b/diesel_derives/tests/selectable.rs
@@ -145,3 +145,30 @@ fn manually_specified_expression() {
     let data = my_structs::table.select(A::as_select()).get_result(conn);
     assert!(data.is_err());
 }
+
+#[allow(dead_code)] // that's essentially a compile test
+#[test]
+fn check_for_backend_with_deserialize_as() {
+    table! {
+        tests {
+            id -> Integer,
+            name -> Text,
+        }
+    }
+
+    struct MyString(String);
+
+    impl From<String> for MyString {
+        fn from(s: String) -> Self {
+            Self(s)
+        }
+    }
+
+    #[derive(Queryable, Selectable)]
+    #[diesel(check_for_backend(crate::helpers::TestBackend))]
+    struct Test {
+        id: i32,
+        #[diesel(deserialize_as = String)]
+        name: MyString,
+    }
+}
diff --git a/diesel_tests/Cargo.toml b/diesel_tests/Cargo.toml
index 27adeb6805ab..e2340c979037 100644
--- a/diesel_tests/Cargo.toml
+++ b/diesel_tests/Cargo.toml
@@ -19,7 +19,7 @@ uuid = { version = "1.0.0" }
 serde_json = { version=">=0.9, <2.0" }
 ipnet = { version = "2.5.0" }
 ipnetwork = ">=0.12.2, <0.21.0"
-bigdecimal = ">= 0.0.13, < 0.4.0"
+bigdecimal = ">= 0.0.13, < 0.5.0"
 rand = "0.8.4"
 libsqlite3-sys = { version = "0.26", optional = true }
 
diff --git a/diesel_tests/tests/distinct.rs b/diesel_tests/tests/distinct.rs
index a400b241f44c..3e4866091270 100644
--- a/diesel_tests/tests/distinct.rs
+++ b/diesel_tests/tests/distinct.rs
@@ -85,6 +85,8 @@ fn distinct_on_select_by() {
 #[cfg(feature = "postgres")]
 #[test]
 fn distinct_on_select_order_by_two_columns() {
+    use diesel::sql_types::Integer;
+
     use crate::schema::users::dsl::*;
 
     let connection = &mut connection();
@@ -136,6 +138,18 @@ fn distinct_on_select_order_by_two_columns() {
     let data: Vec<_> = source.load(connection).unwrap();
 
     assert_eq!(expected_data, data);
+
+    let source = users
+        .select((name, hair_color))
+        .order(dsl::sql::<Integer>("name DESC, hair_color"))
+        .distinct_on(dsl::sql("name"));
+    let expected_data = vec![
+        NewUser::new("Tess", Some("bronze")),
+        NewUser::new("Sean", Some("aqua")),
+    ];
+    let data: Vec<_> = source.load(connection).unwrap();
+
+    assert_eq!(expected_data, data);
 }
 
 #[cfg(feature = "postgres")]