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

Issue:
`dsl::max` doesn't work with `dsl::auto_type`
## Setup

### Versions

- **Rust:** rustc --version = rustc 1.70.0 (90c541806 2023-05-31)
- **Diesel:** master
- **Database:** _
- **Operating System** Archlinux

### Feature Flags

- **diesel:** "postgres", "r2d2", "serde_json", "chrono",

## Problem Description
FYI @Ten0 knows and says it's a blocker for the release of `auto_type` so he'll try to fix.

When  deriving on a struct `Selectable` with one field having a `select_expression` containing `dsl::max` compilation breaks with error:
```
error[E0573]: expected type, found function `diesel::dsl::max`
   --> mod.rs:463:6
    |
463 |                     diesel::dsl::max<table::column>,
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a type
    |
help: consider importing this type alias instead
    |
8   + use diesel::helper_types::max;
    |
help: if you import `max`, refer to it directly
    |
463 -                     diesel::dsl::max<table::column>,
463 +                     max<table::column>,
    |
```
### What are you trying to accomplish?
Use `Selectable` derive

### What is the expected output?
Code compiles

### What is the actual output?
Compilation fails

### Are you seeing any additional errors?
No

### Steps to reproduce
```rust
mod schema {

    diesel::table! {
	    table (id) {
		    id -> Int4,
                    other_id -> Int4,,
		    column -> Int4,
	    }
    }
    diesel::table! {
	    other_table (id) {
		    id -> Int4,
		    column -> Int4,
	    }
    }
}

#[derive(Queryable, Serialize, Selectable)]
#[diesel(table_name = table)]
pub(crate) struct Struct {
	#[diesel(
		select_expression = schema::table::table
			.filter(schema::table::other_id.eq(schema::other_table::id))
			.select(dsl::max(table::column))
			.single_value(),
	)]
	pub(crate) max_val: Option<i32>,
}

```

## 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



Discussion / hints:
@weiznich this might be an issue with the "type alias at the same path as function" approach (might require breaking change) so I think it's a blocker for the release.
I'll try to fix it this week. 🙂 
I think we can handle this without breaking change. We can just have a ”private" type dsl module in `diesel::internals::derive::auto_type` that is essentially 
```rust

pub use diesel::dsl::*;
// overwrite the type provided by diesel::dsl wit the correct one
pub use diesel::expressions::helper_types::max;
```
`#[auto_type]` can then internally use that dsl module by default. Additionally we likely should mention these exceptions somehow in the documentation of the custom dsl prelude for user defined extension.
> `#[auto_type]` can then internally use that dsl module by default

if it did then custom functions couldn't be referred directly anymore. That's notably why it uses "always same path as function" by default (otherwise I guess it could just use diesel::helper_types).

That would be a breaking change for this reason.

I suspect `max` is actually not the only such case.
So re-defining the dsl module in the exact same way as within Diesel in the local crate works. (I did not expect name resolving would work differently on external modules than on local modules...)

Alternately, explicitly specifying `helper_types::max` in addition to `helper_types::*` in the definition of `diesel::dsl` works.
However, in addition to being very verbose, that also breaks previously compiling code such as:
https://github.com/diesel-rs/diesel/blob/c99ebda62395a092e059d6ca494861afa559028b/diesel_tests/tests/select_by.rs#L161-L162
(which did need the second line to compile for the same reason)

As far as I can tell that makes the best solution to keep both local type aliases generated by `dsl::auto_type` and `diesel` types working to leave them at the ~same path, but change the name. Either prefix/suffix it with something, or put it `PascalCase`.
Unfortunately that would imply:
1. Duplicating all our type aliases for SQL functions within Diesel to preserve backwards-compatibility
2. If `PascalCase`, method names can now collide with function names. Fortunately I don't think there are such cases currently.
Wait `not` and `exists` both work. Not sure yet what the difference with `min` is.
Ok so the issue comes from the fact that `sql_function!` generates a module with the same name as the function.
When that happens, Rust gets confused with the import of the module colliding with that of the type, and eventually stops resolving anything but the function.

Re-defining in the local crate works because the module generated by `sql_function!` is `pub(crate)` so it doesn't collide anymore.
The best compromise is probably to release a new `sql_function` macro that generates the helper type at that path instead of the module.
This would also enable removing this doc:
https://github.com/diesel-rs/diesel/blob/c99ebda62395a092e059d6ca494861afa559028b/diesel_derives/src/lib.rs#L1029-L1054

Any opinion with regards to choice between:
1. `sql_function_v2!` + deprecate `sql_function!`
2. `sql_function_v2!` and don't deprecate
3. Breaking change on `sql_function!`

?

EDIT: Ah but that also breaks https://github.com/diesel-rs/diesel/blob/c99ebda62395a092e059d6ca494861afa559028b/diesel_tests/tests/select_by.rs#L161-L162
```
the name `max` is defined multiple times
```

Well alternately that leaves us with the "duplicate everything to `PascalCase`" option...
@weiznich I'd like an opinion on whether this particular breaking change could be considered a bug fix, vs the loss of ergonomics and duplicated types of pascal-casing 
> Well alternately that leaves us with the "duplicate everything to PascalCase" option...

To explore that option a bit more: We only talk about these 6 functions here, right? (If that includes `now`, `today` and `date` as well we are talking about 9 functions…)

 https://github.com/diesel-rs/diesel/blob/409585e9548f61f4e180ea5fe13ee4c3b7ab241c/diesel/src/expression/functions/helper_types.rs#L8-L23

If that only affects that number of function I think it might be the easiest solution to:

* Deprecate the old aliases
* Add new ones in in `PascalCase`
* Update the documentation of `sql_function!` to show that these type aliases should be named in `PascalCase`, although it seems like that's even already the case as shown by the link in your comment above.


> We only talk about these 6 functions here, right?

The issue is that we want the types to all be in the same case so that `dsl::auto_type` can look for the types in a consistent manner, so instead that would be updating the aliases for all the functions and deprecating all the old aliases.
Another advantage of having the types hold the same exact name as the functions is that a `use` statement will import both the function and its type, making it easy to declare query fragments/selectable using other `dsl::auto_type` factored query fragments.

> although it seems like that's even already the case as shown by the link in your comment above

Although that is the official convention proposed by this doc, it's not the current convention within Diesel itself (and the user is invited to do it themselves for the particular case of `sql_function!`), whereas we'd want the function to always generate that type.
I did not find the time today to look into this issue, hopefully I have capacity for that next week. I've started to play a bit around, but I did not come to a conclusion yet. As part of the playing around I've discovered that `QueryDsl::distinct`, `QueryDsl::distinct_on`, `QueryDsl::limit` and `QueryDsl::offset` do not work with `#[auto_type]`. For at least the later two the issue is that the corresponding types have only one generic argument instead of two. I think it would be useful to write some code that checks for all existing `QueryDsl`, `*ExpressionMethod` and other dsl functions whether or not the corresponding type defs are what `#[auto_type]` expects. 
Cool, thanks.

As far as I'm concerned I'd favor the compromise of considering the solution at https://github.com/diesel-rs/diesel/issues/3745#issuecomment-1678862830 as a bugfix of the types not being available in the `dsl` module, which is inconsistent and seems like it wasn't intended. 
I've continued to put together a list of all expressions that might occur. It still misses some items like free standing functions (which are the original problem reported by this issue.) See [here](https://gist.github.com/weiznich/e8d22e8bdc8c66051679d2b6df0af958) for the result. Every commented out expression seems to be not supported right now. I think we want to classify each of the items and see what's the problem for each one and how we might solve it. Help for this classification would be greatly appreciated. 

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eaa59a4827d1..2402a513412e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ Increasing the minimal supported Rust version will always be coupled at least wi
 ### Changed
 
 * The minimal officially supported rustc version is now 1.70.0
+* Deprecated `sql_function!` in favour of `define_sql_function!` which provides compatibility with `#[dsl::auto_type]`
 
 ## [2.1.0] 2023-05-26
 
diff --git a/diesel/src/expression/count.rs b/diesel/src/expression/count.rs
index 0a8ea4d2dbc4..e3512b22995c 100644
--- a/diesel/src/expression/count.rs
+++ b/diesel/src/expression/count.rs
@@ -1,6 +1,6 @@
 use std::marker::PhantomData;
 
-use super::functions::sql_function;
+use super::functions::define_sql_function;
 use super::{is_aggregate, AsExpression};
 use super::{Expression, ValidGrouping};
 use crate::backend::Backend;
@@ -9,7 +9,7 @@ use crate::result::QueryResult;
 use crate::sql_types::{BigInt, DieselNumericOps, SingleValue, SqlType};
 use crate::{AppearsOnTable, SelectableExpression};
 
-sql_function! {
+define_sql_function! {
     /// Creates a SQL `COUNT` expression
     ///
     /// As with most bare functions, this is not exported by default. You can import
diff --git a/diesel/src/expression/exists.rs b/diesel/src/expression/exists.rs
index 82779bfbc049..ff6a8d5d65cf 100644
--- a/diesel/src/expression/exists.rs
+++ b/diesel/src/expression/exists.rs
@@ -4,7 +4,7 @@
 use crate::backend::{sql_dialect, Backend, SqlDialect};
 use crate::expression::subselect::Subselect;
 use crate::expression::{AppearsOnTable, Expression, SelectableExpression, ValidGrouping};
-use crate::helper_types::exists;
+use crate::helper_types;
 use crate::query_builder::*;
 use crate::result::QueryResult;
 use crate::sql_types::Bool;
@@ -32,7 +32,7 @@ use crate::sql_types::Bool;
 /// assert_eq!(Ok(false), jim_exists);
 /// # }
 /// ```
-pub fn exists<T>(query: T) -> exists<T> {
+pub fn exists<T>(query: T) -> helper_types::exists<T> {
     Exists {
         subselect: Subselect::new(query),
     }
diff --git a/diesel/src/expression/functions/aggregate_folding.rs b/diesel/src/expression/functions/aggregate_folding.rs
index fb8b9b2bd464..7d0f3732072e 100644
--- a/diesel/src/expression/functions/aggregate_folding.rs
+++ b/diesel/src/expression/functions/aggregate_folding.rs
@@ -1,7 +1,7 @@
-use crate::expression::functions::sql_function;
+use crate::expression::functions::define_sql_function;
 use crate::sql_types::Foldable;
 
-sql_function! {
+define_sql_function! {
     /// Represents a SQL `SUM` function. This function can only take types which are
     /// Foldable.
     ///
@@ -21,7 +21,7 @@ sql_function! {
     fn sum<ST: Foldable>(expr: ST) -> ST::Sum;
 }
 
-sql_function! {
+define_sql_function! {
     /// Represents a SQL `AVG` function. This function can only take types which are
     /// Foldable.
     ///
diff --git a/diesel/src/expression/functions/aggregate_ordering.rs b/diesel/src/expression/functions/aggregate_ordering.rs
index aa95e1d1a64f..c36b6cc0b96b 100644
--- a/diesel/src/expression/functions/aggregate_ordering.rs
+++ b/diesel/src/expression/functions/aggregate_ordering.rs
@@ -1,7 +1,7 @@
 use self::private::SqlOrdAggregate;
-use crate::expression::functions::sql_function;
+use crate::expression::functions::define_sql_function;
 
-sql_function! {
+define_sql_function! {
     /// Represents a SQL `MAX` function. This function can only take types which are
     /// ordered.
     ///
@@ -20,7 +20,7 @@ sql_function! {
     fn max<ST: SqlOrdAggregate>(expr: ST) -> ST::Ret;
 }
 
-sql_function! {
+define_sql_function! {
     /// Represents a SQL `MIN` function. This function can only take types which are
     /// ordered.
     ///
diff --git a/diesel/src/expression/functions/date_and_time.rs b/diesel/src/expression/functions/date_and_time.rs
index 22d51a470cec..8433d5db8524 100644
--- a/diesel/src/expression/functions/date_and_time.rs
+++ b/diesel/src/expression/functions/date_and_time.rs
@@ -1,6 +1,6 @@
 use crate::backend::Backend;
 use crate::expression::coerce::Coerce;
-use crate::expression::functions::sql_function;
+use crate::expression::functions::define_sql_function;
 use crate::expression::{AsExpression, Expression, ValidGrouping};
 use crate::query_builder::*;
 use crate::result::QueryResult;
@@ -27,7 +27,7 @@ impl_selectable_expression!(now);
 
 operator_allowed!(now, Add, add);
 operator_allowed!(now, Sub, sub);
-sql_function! {
+define_sql_function! {
     /// Represents the SQL `DATE` function. The argument should be a Timestamp
     /// expression, and the return value will be an expression of type Date.
     ///
diff --git a/diesel/src/expression/functions/helper_types.rs b/diesel/src/expression/functions/helper_types.rs
index ff757c350447..4f6bd2be06ab 100644
--- a/diesel/src/expression/functions/helper_types.rs
+++ b/diesel/src/expression/functions/helper_types.rs
@@ -8,16 +8,16 @@ use crate::expression::operators;
 pub type not<Expr> = operators::Not<Grouped<Expr>>;
 
 /// The return type of [`max(expr)`](crate::dsl::max())
-pub type max<Expr> = super::aggregate_ordering::max::HelperType<SqlTypeOf<Expr>, Expr>;
+pub type max<Expr> = super::aggregate_ordering::max<SqlTypeOf<Expr>, Expr>;
 
 /// The return type of [`min(expr)`](crate::dsl::min())
-pub type min<Expr> = super::aggregate_ordering::min::HelperType<SqlTypeOf<Expr>, Expr>;
+pub type min<Expr> = super::aggregate_ordering::min<SqlTypeOf<Expr>, Expr>;
 
 /// The return type of [`sum(expr)`](crate::dsl::sum())
-pub type sum<Expr> = super::aggregate_folding::sum::HelperType<SqlTypeOf<Expr>, Expr>;
+pub type sum<Expr> = super::aggregate_folding::sum<SqlTypeOf<Expr>, Expr>;
 
 /// The return type of [`avg(expr)`](crate::dsl::avg())
-pub type avg<Expr> = super::aggregate_folding::avg::HelperType<SqlTypeOf<Expr>, Expr>;
+pub type avg<Expr> = super::aggregate_folding::avg<SqlTypeOf<Expr>, Expr>;
 
 /// The return type of [`exists(expr)`](crate::dsl::exists())
 pub type exists<Expr> = crate::expression::exists::Exists<Expr>;
diff --git a/diesel/src/expression/functions/mod.rs b/diesel/src/expression/functions/mod.rs
index f37261412495..db8f79e7a730 100644
--- a/diesel/src/expression/functions/mod.rs
+++ b/diesel/src/expression/functions/mod.rs
@@ -1,6 +1,10 @@
 //! Helper macros to define custom sql functions
 
 #[doc(inline)]
+pub use diesel_derives::define_sql_function;
+
+#[doc(inline)]
+#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
 pub use diesel_derives::sql_function_proc as sql_function;
 
 #[macro_export]
@@ -73,7 +77,7 @@ macro_rules! no_arg_sql_function_body {
 /// function.
 #[deprecated(
     since = "2.0.0",
-    note = "Use `sql_function!` instead. See `CHANGELOG.md` for migration instructions"
+    note = "Use `define_sql_function!` instead. See `CHANGELOG.md` for migration instructions"
 )]
 #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
 macro_rules! no_arg_sql_function {
diff --git a/diesel/src/expression/helper_types.rs b/diesel/src/expression/helper_types.rs
index 5104b93a0c05..4ca14ad6daa7 100644
--- a/diesel/src/expression/helper_types.rs
+++ b/diesel/src/expression/helper_types.rs
@@ -26,6 +26,9 @@ pub type Eq<Lhs, Rhs> = Grouped<super::operators::Eq<Lhs, AsExpr<Rhs, Lhs>>>;
 /// [`lhs.ne(rhs)`](crate::expression_methods::ExpressionMethods::ne())
 pub type NotEq<Lhs, Rhs> = Grouped<super::operators::NotEq<Lhs, AsExpr<Rhs, Lhs>>>;
 
+#[doc(hidden)] // required for `#[auto_type]`
+pub type Ne<Lhs, Rhs> = NotEq<Lhs, Rhs>;
+
 /// The return type of
 /// [`lhs.eq_any(rhs)`](crate::expression_methods::ExpressionMethods::eq_any())
 pub type EqAny<Lhs, Rhs> = Grouped<In<Lhs, <Rhs as AsInExpression<SqlTypeOf<Lhs>>>::InExpression>>;
@@ -35,6 +38,9 @@ pub type EqAny<Lhs, Rhs> = Grouped<In<Lhs, <Rhs as AsInExpression<SqlTypeOf<Lhs>
 pub type NeAny<Lhs, Rhs> =
     Grouped<NotIn<Lhs, <Rhs as AsInExpression<SqlTypeOf<Lhs>>>::InExpression>>;
 
+#[doc(hidden)] // required for `#[auto_type]`
+pub type NeAll<Lhs, Rhs> = NeAny<Lhs, Rhs>;
+
 /// The return type of
 /// [`expr.is_null()`](crate::expression_methods::ExpressionMethods::is_null())
 pub type IsNull<Expr> = Grouped<super::operators::IsNull<Expr>>;
@@ -51,6 +57,9 @@ pub type Gt<Lhs, Rhs> = Grouped<super::operators::Gt<Lhs, AsExpr<Rhs, Lhs>>>;
 /// [`lhs.ge(rhs)`](crate::expression_methods::ExpressionMethods::ge())
 pub type GtEq<Lhs, Rhs> = Grouped<super::operators::GtEq<Lhs, AsExpr<Rhs, Lhs>>>;
 
+#[doc(hidden)] // required for `#[auto_type]`
+pub type Ge<Lhs, Rhs> = GtEq<Lhs, Rhs>;
+
 /// The return type of
 /// [`lhs.lt(rhs)`](crate::expression_methods::ExpressionMethods::lt())
 pub type Lt<Lhs, Rhs> = Grouped<super::operators::Lt<Lhs, AsExpr<Rhs, Lhs>>>;
@@ -59,6 +68,9 @@ pub type Lt<Lhs, Rhs> = Grouped<super::operators::Lt<Lhs, AsExpr<Rhs, Lhs>>>;
 /// [`lhs.le(rhs)`](crate::expression_methods::ExpressionMethods::le())
 pub type LtEq<Lhs, Rhs> = Grouped<super::operators::LtEq<Lhs, AsExpr<Rhs, Lhs>>>;
 
+#[doc(hidden)] // required for `#[auto_type]`
+pub type Le<Lhs, Rhs> = LtEq<Lhs, Rhs>;
+
 /// The return type of
 /// [`lhs.between(lower, upper)`](crate::expression_methods::ExpressionMethods::between())
 pub type Between<Lhs, Lower, Upper> = Grouped<
@@ -122,12 +134,12 @@ pub type Like<Lhs, Rhs> = Grouped<super::operators::Like<Lhs, AsExprOf<Rhs, SqlT
 pub type NotLike<Lhs, Rhs> = Grouped<super::operators::NotLike<Lhs, AsExprOf<Rhs, SqlTypeOf<Lhs>>>>;
 
 /// The return type of [`case_when()`](expression::case_when::case_when)
-#[allow(non_camel_case_types)]
+#[allow(non_camel_case_types)] // required for `#[auto_type]`
 pub type case_when<C, T, ST = <T as Expression>::SqlType> = expression::case_when::CaseWhen<
     expression::case_when::CaseWhenConditionsLeaf<Grouped<C>, Grouped<AsExprOf<T, ST>>>,
     expression::case_when::NoElseExpression,
 >;
-/// The return type of [`case_when(...).when(...)`](expression::case_when::CaseWhen::when)
+/// The return type of [`case_when(...).when(...)`](expression::CaseWhen::when)
 pub type When<W, C, T> = expression::case_when::CaseWhen<
     expression::case_when::CaseWhenConditionsIntermediateNode<
         Grouped<C>,
diff --git a/diesel/src/expression/mod.rs b/diesel/src/expression/mod.rs
index f54d5244a470..be3c47d2a466 100644
--- a/diesel/src/expression/mod.rs
+++ b/diesel/src/expression/mod.rs
@@ -53,7 +53,7 @@ pub(crate) mod dsl {
     use crate::dsl::SqlTypeOf;
 
     #[doc(inline)]
-    pub use super::case_when::*;
+    pub use super::case_when::case_when;
     #[doc(inline)]
     pub use super::count::*;
     #[doc(inline)]
@@ -65,6 +65,8 @@ pub(crate) mod dsl {
     #[doc(inline)]
     pub use super::functions::date_and_time::*;
     #[doc(inline)]
+    pub use super::helper_types::{case_when, Otherwise, When};
+    #[doc(inline)]
     pub use super::not::not;
     #[doc(inline)]
     pub use super::sql_literal::sql;
@@ -73,7 +75,7 @@ pub(crate) mod dsl {
     pub use crate::pg::expression::dsl::*;
 
     /// The return type of [`count(expr)`](crate::dsl::count())
-    pub type count<Expr> = super::count::count::HelperType<SqlTypeOf<Expr>, Expr>;
+    pub type count<Expr> = super::count::count<SqlTypeOf<Expr>, Expr>;
 
     /// The return type of [`count_star()`](crate::dsl::count_star())
     pub type count_star = super::count::CountStar;
@@ -82,12 +84,14 @@ pub(crate) mod dsl {
     pub type count_distinct<Expr> = super::count::CountDistinct<SqlTypeOf<Expr>, Expr>;
 
     /// The return type of [`date(expr)`](crate::dsl::date())
-    pub type date<Expr> = super::functions::date_and_time::date::HelperType<Expr>;
+    pub type date<Expr> = super::functions::date_and_time::date<Expr>;
 
     #[cfg(feature = "mysql_backend")]
     pub use crate::mysql::query_builder::DuplicatedKeys;
 }
 
+#[doc(inline)]
+pub use self::case_when::CaseWhen;
 #[doc(inline)]
 pub use self::sql_literal::{SqlLiteral, UncheckedBind};
 
diff --git a/diesel/src/expression/not.rs b/diesel/src/expression/not.rs
index 70fa1bade423..8280dc218c3f 100644
--- a/diesel/src/expression/not.rs
+++ b/diesel/src/expression/not.rs
@@ -1,6 +1,6 @@
 use crate::expression::grouped::Grouped;
 use crate::expression::Expression;
-use crate::helper_types::not;
+use crate::helper_types;
 use crate::sql_types::BoolOrNullableBool;
 
 /// Creates a SQL `NOT` expression
@@ -23,7 +23,7 @@ use crate::sql_types::BoolOrNullableBool;
 /// assert_eq!(Ok(2), users_not_with_name.first(connection));
 /// # }
 /// ```
-pub fn not<T>(expr: T) -> not<T>
+pub fn not<T>(expr: T) -> helper_types::not<T>
 where
     T: Expression,
     <T as Expression>::SqlType: BoolOrNullableBool,
diff --git a/diesel/src/expression/operators.rs b/diesel/src/expression/operators.rs
index 55b1372c4e0a..718cd0d149d1 100644
--- a/diesel/src/expression/operators.rs
+++ b/diesel/src/expression/operators.rs
@@ -529,7 +529,6 @@ infix_operator!(Escape, " ESCAPE ");
 infix_operator!(Eq, " = ");
 infix_operator!(Gt, " > ");
 infix_operator!(GtEq, " >= ");
-infix_operator!(Like, " LIKE ");
 infix_operator!(Lt, " < ");
 infix_operator!(LtEq, " <= ");
 infix_operator!(NotEq, " != ");
@@ -645,3 +644,106 @@ where
         Ok(())
     }
 }
+
+// need an explicit impl here to control which types are allowed
+#[derive(
+    Debug,
+    Clone,
+    Copy,
+    crate::query_builder::QueryId,
+    crate::sql_types::DieselNumericOps,
+    crate::expression::ValidGrouping,
+)]
+#[doc(hidden)]
+pub struct Like<T, U> {
+    pub(crate) left: T,
+    pub(crate) right: U,
+}
+
+impl<T, U> Like<T, U> {
+    pub(crate) fn new(left: T, right: U) -> Self {
+        Like { left, right }
+    }
+}
+
+impl<T, U, QS> crate::expression::SelectableExpression<QS> for Like<T, U>
+where
+    Like<T, U>: crate::expression::AppearsOnTable<QS>,
+    T: crate::expression::SelectableExpression<QS>,
+    U: crate::expression::SelectableExpression<QS>,
+{
+}
+
+impl<T, U, QS> crate::expression::AppearsOnTable<QS> for Like<T, U>
+where
+    Like<T, U>: crate::expression::Expression,
+    T: crate::expression::AppearsOnTable<QS>,
+    U: crate::expression::AppearsOnTable<QS>,
+{
+}
+
+impl<T, U> crate::expression::Expression for Like<T, U>
+where
+    T: crate::expression::Expression,
+    U: crate::expression::Expression,
+    <T as crate::expression::Expression>::SqlType: crate::sql_types::SqlType,
+    <U as crate::expression::Expression>::SqlType: crate::sql_types::SqlType,
+    crate::sql_types::is_nullable::IsSqlTypeNullable<<T as crate::expression::Expression>::SqlType>:
+        crate::sql_types::OneIsNullable<
+            crate::sql_types::is_nullable::IsSqlTypeNullable<
+                <U as crate::expression::Expression>::SqlType,
+            >,
+        >,
+    crate::sql_types::is_nullable::IsOneNullable<
+        <T as crate::expression::Expression>::SqlType,
+        <U as crate::expression::Expression>::SqlType,
+    >: crate::sql_types::MaybeNullableType<crate::sql_types::Bool>,
+{
+    type SqlType = crate::sql_types::is_nullable::MaybeNullable<
+        crate::sql_types::is_nullable::IsOneNullable<
+            <T as crate::expression::Expression>::SqlType,
+            <U as crate::expression::Expression>::SqlType,
+        >,
+        crate::sql_types::Bool,
+    >;
+}
+
+impl<T, U, DB> crate::query_builder::QueryFragment<DB> for Like<T, U>
+where
+    T: crate::query_builder::QueryFragment<DB> + crate::Expression,
+    U: crate::query_builder::QueryFragment<DB>,
+    DB: crate::backend::Backend,
+    DB: LikeIsAllowedForType<T::SqlType>,
+{
+    fn walk_ast<'b>(
+        &'b self,
+        mut out: crate::query_builder::AstPass<'_, 'b, DB>,
+    ) -> crate::result::QueryResult<()> {
+        (self.left.walk_ast(out.reborrow())?);
+        (out.push_sql(" LIKE "));
+        (self.right.walk_ast(out.reborrow())?);
+        Ok(())
+    }
+}
+
+impl<S, T, U> crate::internal::operators_macro::FieldAliasMapper<S> for Like<T, U>
+where
+    S: crate::query_source::AliasSource,
+    T: crate::internal::operators_macro::FieldAliasMapper<S>,
+    U: crate::internal::operators_macro::FieldAliasMapper<S>,
+{
+    type Out = Like<
+        <T as crate::internal::operators_macro::FieldAliasMapper<S>>::Out,
+        <U as crate::internal::operators_macro::FieldAliasMapper<S>>::Out,
+    >;
+    fn map(self, alias: &crate::query_source::Alias<S>) -> Self::Out {
+        Like {
+            left: self.left.map(alias),
+            right: self.right.map(alias),
+        }
+    }
+}
+
+pub trait LikeIsAllowedForType<ST>: Backend {}
+
+impl<DB> LikeIsAllowedForType<crate::sql_types::Text> for DB where DB: Backend {}
diff --git a/diesel/src/lib.rs b/diesel/src/lib.rs
index 96e0bf2817de..2a74471dd626 100644
--- a/diesel/src/lib.rs
+++ b/diesel/src/lib.rs
@@ -57,7 +57,7 @@
 //!   They live in [the `dsl` module](dsl).
 //!   Diesel only supports a very small number of these functions.
 //!   You can declare additional functions you want to use
-//!   with [the `sql_function!` macro][`sql_function!`].
+//!   with [the `define_sql_function!` macro][`define_sql_function!`].
 //!
 //! [`std::ops`]: //doc.rust-lang.org/stable/std/ops/index.html
 //!
@@ -416,10 +416,12 @@ pub mod helper_types {
     pub type ThenOrderBy<Source, Ordering> = <Source as ThenOrderDsl<Ordering>>::Output;
 
     /// Represents the return type of [`.limit()`](crate::prelude::QueryDsl::limit)
-    pub type Limit<Source> = <Source as LimitDsl>::Output;
+    pub type Limit<Source, DummyArgForAutoType = i64> =
+        <Source as LimitDsl<DummyArgForAutoType>>::Output;
 
     /// Represents the return type of [`.offset()`](crate::prelude::QueryDsl::offset)
-    pub type Offset<Source> = <Source as OffsetDsl>::Output;
+    pub type Offset<Source, DummyArgForAutoType = i64> =
+        <Source as OffsetDsl<DummyArgForAutoType>>::Output;
 
     /// Represents the return type of [`.inner_join(rhs)`](crate::prelude::QueryDsl::inner_join)
     pub type InnerJoin<Source, Rhs> =
@@ -631,6 +633,43 @@ pub mod helper_types {
     #[deprecated(note = "Use `LoadQuery::RowIter` directly")]
     pub type LoadIter<'conn, 'query, Q, Conn, U, B = crate::connection::DefaultLoadingMode> =
         <Q as load_dsl::LoadQuery<'query, Conn, U, B>>::RowIter<'conn>;
+
+    /// Represents the return type of [`diesel::delete`]
+    #[allow(non_camel_case_types)] // required for `#[auto_type]`
+    pub type delete<T> = crate::query_builder::DeleteStatement<
+        <T as HasTable>::Table,
+        <T as IntoUpdateTarget>::WhereClause,
+    >;
+
+    /// Represents the return type of [`diesel::insert_into`]
+    #[allow(non_camel_case_types)] // required for `#[auto_type]`
+    pub type insert_into<T> = crate::query_builder::IncompleteInsertStatement<T>;
+
+    /// Represents the return type of [`diesel::insert_or_ignore_into`]
+    #[allow(non_camel_case_types)] // required for `#[auto_type]`
+    pub type insert_or_ignore_into<T> = crate::query_builder::IncompleteInsertOrIgnoreStatement<T>;
+
+    /// Represents the return type of [`diesel::replace_into`]
+    #[allow(non_camel_case_types)] // required for `#[auto_type]`
+    pub type replace_into<T> = crate::query_builder::IncompleteReplaceStatement<T>;
+
+    /// Represents the return type of
+    /// [`IncompleteInsertStatement::values()`](crate::query_builder::IncompleteInsertStatement::values)
+    pub type Values<I, U> = crate::query_builder::InsertStatement<
+        <I as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Table,
+        <U as crate::Insertable<
+            <I as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Table,
+        >>::Values,
+        <I as crate::query_builder::insert_statement::InsertAutoTypeHelper>::Op,
+    >;
+
+    /// Represents the return type of
+    /// [`UpdateStatement::set()`](crate::query_builder::UpdateStatement::set)
+    pub type Set<U, V> = crate::query_builder::UpdateStatement<
+        <U as crate::query_builder::update_statement::UpdateAutoTypeHelper>::Table,
+        <U as crate::query_builder::update_statement::UpdateAutoTypeHelper>::Where,
+        <V as crate::AsChangeset>::Changeset,
+    >;
 }
 
 pub mod prelude {
@@ -648,6 +687,8 @@ pub mod prelude {
     };
 
     #[doc(inline)]
+    pub use crate::expression::functions::define_sql_function;
+    #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
     pub use crate::expression::functions::sql_function;
 
     #[doc(inline)]
diff --git a/diesel/src/pg/backend.rs b/diesel/src/pg/backend.rs
index 7b151705ab53..db18d681c8d0 100644
--- a/diesel/src/pg/backend.rs
+++ b/diesel/src/pg/backend.rs
@@ -4,6 +4,7 @@ use super::query_builder::PgQueryBuilder;
 use super::{PgMetadataLookup, PgValue};
 use crate::backend::*;
 use crate::deserialize::Queryable;
+use crate::expression::operators::LikeIsAllowedForType;
 use crate::pg::metadata_lookup::PgMetadataCacheKey;
 use crate::query_builder::bind_collector::RawBytesBindCollector;
 use crate::sql_types::TypeMetadata;
@@ -154,3 +155,5 @@ impl sql_dialect::on_conflict_clause::PgLikeOnConflictClause for PgOnConflictCla
 
 #[derive(Debug, Copy, Clone)]
 pub struct PgStyleArrayComparison;
+
+impl LikeIsAllowedForType<crate::sql_types::Binary> for Pg {}
diff --git a/diesel/src/pg/connection/mod.rs b/diesel/src/pg/connection/mod.rs
index f7aadf954d65..3166e5f32842 100644
--- a/diesel/src/pg/connection/mod.rs
+++ b/diesel/src/pg/connection/mod.rs
@@ -584,7 +584,7 @@ mod tests {
         assert_eq!(2, connection.statement_cache.len());
     }
 
-    sql_function!(fn lower(x: VarChar) -> VarChar);
+    define_sql_function!(fn lower(x: VarChar) -> VarChar);
 
     #[test]
     fn queries_with_identical_types_and_binds_but_different_sql_are_cached_separately() {
diff --git a/diesel/src/pg/expression/expression_methods.rs b/diesel/src/pg/expression/expression_methods.rs
index bc34c468de16..d1d4ed376649 100644
--- a/diesel/src/pg/expression/expression_methods.rs
+++ b/diesel/src/pg/expression/expression_methods.rs
@@ -8,10 +8,10 @@ use super::date_and_time::{AtTimeZone, DateTimeLike};
 use super::operators::*;
 use crate::dsl;
 use crate::expression::grouped::Grouped;
-use crate::expression::operators::{Asc, Desc};
+use crate::expression::operators::{Asc, Concat, Desc, Like, NotLike};
 use crate::expression::{AsExpression, Expression, IntoSql, TypedExpressionType};
 use crate::pg::expression::expression_methods::private::BinaryOrNullableBinary;
-use crate::sql_types::{Array, Binary, Inet, Integer, Jsonb, SqlType, Text, VarChar};
+use crate::sql_types::{Array, Inet, Integer, SqlType, Text, VarChar};
 use crate::EscapeExpressionMethods;
 
 /// PostgreSQL specific methods which are present on all expressions.
@@ -253,7 +253,7 @@ pub trait PgArrayExpressionMethods: Expression + Sized {
     /// #     Ok(())
     /// # }
     /// ```
-    fn contains<T>(self, other: T) -> dsl::ArrayContains<Self, T>
+    fn contains<T>(self, other: T) -> dsl::Contains<Self, T>
     where
         Self::SqlType: SqlType,
         T: AsExpression<Self::SqlType>,
@@ -363,7 +363,7 @@ pub trait PgArrayExpressionMethods: Expression + Sized {
     /// #     Ok(())
     /// # }
     /// ```
-    fn index<T>(self, other: T) -> dsl::ArrayIndex<Self, T>
+    fn index<T>(self, other: T) -> dsl::Index<Self, T>
     where
         Self::SqlType: SqlType,
         T: AsExpression<Integer>,
@@ -409,12 +409,12 @@ pub trait PgArrayExpressionMethods: Expression + Sized {
     /// #     Ok(())
     /// # }
     ///
-    fn concat<T>(self, other: T) -> dsl::ConcatArray<Self, T>
+    fn concat<T>(self, other: T) -> dsl::Concat<Self, T>
     where
         Self::SqlType: SqlType,
         T: AsExpression<Self::SqlType>,
     {
-        Grouped(ConcatArray::new(self, other.as_expression()))
+        Grouped(Concat::new(self, other.as_expression()))
     }
 }
 
@@ -1303,11 +1303,12 @@ pub trait PgJsonbExpressionMethods: Expression + Sized {
     /// #     Ok(())
     /// # }
     /// ```
-    fn concat<T>(self, other: T) -> dsl::ConcatJsonb<Self, T>
+    fn concat<T>(self, other: T) -> dsl::Concat<Self, T>
     where
-        T: AsExpression<Jsonb>,
+        Self::SqlType: SqlType,
+        T: AsExpression<Self::SqlType>,
     {
-        Grouped(ConcatJsonb::new(self, other.as_expression()))
+        Grouped(Concat::new(self, other.as_expression()))
     }
 
     /// Creates a PostgreSQL `?` expression.
@@ -1546,11 +1547,12 @@ pub trait PgJsonbExpressionMethods: Expression + Sized {
     /// #     Ok(())
     /// # }
     /// ```
-    fn contains<T>(self, other: T) -> dsl::ContainsJsonb<Self, T>
+    fn contains<T>(self, other: T) -> dsl::Contains<Self, T>
     where
-        T: AsExpression<Jsonb>,
+        Self::SqlType: SqlType,
+        T: AsExpression<Self::SqlType>,
     {
-        Grouped(ContainsJsonb::new(self, other.as_expression()))
+        Grouped(Contains::new(self, other.as_expression()))
     }
 
     /// Creates a PostgreSQL `<@` expression.
@@ -1611,11 +1613,12 @@ pub trait PgJsonbExpressionMethods: Expression + Sized {
     /// # }
     /// ```
     #[allow(clippy::wrong_self_convention)] // This is named after the sql operator
-    fn is_contained_by<T>(self, other: T) -> dsl::IsContainedByJsonb<Self, T>
+    fn is_contained_by<T>(self, other: T) -> dsl::IsContainedBy<Self, T>
     where
-        T: AsExpression<Jsonb>,
+        Self::SqlType: SqlType,
+        T: AsExpression<Self::SqlType>,
     {
-        Grouped(IsContainedByJsonb::new(self, other.as_expression()))
+        Grouped(IsContainedBy::new(self, other.as_expression()))
     }
 
     /// Creates a PostgreSQL `-` expression.
@@ -2277,12 +2280,12 @@ pub trait PgBinaryExpressionMethods: Expression + Sized {
     /// assert_eq!(Ok(expected_names), names);
     /// # }
     /// ```
-    fn concat<T>(self, other: T) -> dsl::ConcatBinary<Self, T>
+    fn concat<T>(self, other: T) -> dsl::Concat<Self, T>
     where
         Self::SqlType: SqlType,
-        T: AsExpression<Binary>,
+        T: AsExpression<Self::SqlType>,
     {
-        Grouped(ConcatBinary::new(self, other.as_expression()))
+        Grouped(Concat::new(self, other.as_expression()))
     }
 
     /// Creates a PostgreSQL binary `LIKE` expression.
@@ -2327,12 +2330,12 @@ pub trait PgBinaryExpressionMethods: Expression + Sized {
     /// assert_eq!(Ok(vec![b"Sean".to_vec()]), starts_with_s);
     /// # }
     /// ```
-    fn like<T>(self, other: T) -> dsl::LikeBinary<Self, T>
+    fn like<T>(self, other: T) -> dsl::Like<Self, T>
     where
         Self::SqlType: SqlType,
-        T: AsExpression<Binary>,
+        T: AsExpression<Self::SqlType>,
     {
-        Grouped(LikeBinary::new(self, other.as_expression()))
+        Grouped(Like::new(self, other.as_expression()))
     }
 
     /// Creates a PostgreSQL binary `LIKE` expression.
@@ -2377,12 +2380,12 @@ pub trait PgBinaryExpressionMethods: Expression + Sized {
     /// assert_eq!(Ok(vec![b"Tess".to_vec()]), starts_with_s);
     /// # }
     /// ```
-    fn not_like<T>(self, other: T) -> dsl::NotLikeBinary<Self, T>
+    fn not_like<T>(self, other: T) -> dsl::NotLike<Self, T>
     where
         Self::SqlType: SqlType,
-        T: AsExpression<Binary>,
+        T: AsExpression<Self::SqlType>,
     {
-        Grouped(NotLikeBinary::new(self, other.as_expression()))
+        Grouped(NotLike::new(self, other.as_expression()))
     }
 }
 
@@ -2394,7 +2397,7 @@ where
 {
 }
 
-mod private {
+pub(in crate::pg) mod private {
     use crate::sql_types::{
         Array, Binary, Cidr, Inet, Integer, Json, Jsonb, Nullable, Range, SqlType, Text,
     };
diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs
index 254856268564..b1593dfe16f6 100644
--- a/diesel/src/pg/expression/functions.rs
+++ b/diesel/src/pg/expression/functions.rs
@@ -1,61 +1,61 @@
 //! PostgreSQL specific functions
 
 use super::expression_methods::InetOrCidr;
-use crate::expression::functions::sql_function;
+use crate::expression::functions::define_sql_function;
 use crate::sql_types::*;
 
-sql_function! {
+define_sql_function! {
     /// Creates an abbreviated display format as text.
     #[cfg(feature = "postgres_backend")]
     fn abbrev<T: InetOrCidr + SingleValue>(addr: T) -> Text;
 }
-sql_function! {
+define_sql_function! {
     /// Computes the broadcast address for the address's network.
     #[cfg(feature = "postgres_backend")]
     fn broadcast<T: InetOrCidr + SingleValue>(addr: T) -> Inet;
 }
-sql_function! {
+define_sql_function! {
     /// Returns the address's family: 4 for IPv4, 6 for IPv6.
     #[cfg(feature = "postgres_backend")]
     fn family<T: InetOrCidr + SingleValue>(addr: T) -> Integer;
 }
-sql_function! {
+define_sql_function! {
     /// Returns the IP address as text, ignoring the netmask.
     #[cfg(feature = "postgres_backend")]
     fn host<T: InetOrCidr + SingleValue>(addr: T) -> Text;
 }
-sql_function! {
+define_sql_function! {
     /// Computes the host mask for the address's network.
     #[cfg(feature = "postgres_backend")]
     fn hostmask<T: InetOrCidr + SingleValue>(addr: T) -> Inet;
 }
-sql_function! {
+define_sql_function! {
     /// Computes the smallest network that includes both of the given networks.
     #[cfg(feature = "postgres_backend")]
     fn inet_merge<T: InetOrCidr + SingleValue, U: InetOrCidr + SingleValue>(a: T, b: U) -> Cidr;
 }
-sql_function! {
+define_sql_function! {
     /// Tests whether the addresses belong to the same IP family.
     #[cfg(feature = "postgres_backend")]
     fn inet_same_family<T: InetOrCidr + SingleValue, U: InetOrCidr + SingleValue>(a: T, b: U) -> Bool;
 }
-sql_function! {
+define_sql_function! {
     /// Returns the netmask length in bits.
     #[cfg(feature = "postgres_backend")]
     fn masklen<T: InetOrCidr + SingleValue>(addr: T) -> Integer;
 }
-sql_function! {
+define_sql_function! {
     /// Computes the network mask for the address's network.
     #[cfg(feature = "postgres_backend")]
     fn netmask<T: InetOrCidr + SingleValue>(addr: T) -> Inet;
 }
-sql_function! {
+define_sql_function! {
     /// Returns the network part of the address, zeroing out whatever is to the right of the
     /// netmask. (This is equivalent to casting the value to cidr.)
     #[cfg(feature = "postgres_backend")]
     fn network<T: InetOrCidr + SingleValue>(addr: T) -> Cidr;
 }
-sql_function! {
+define_sql_function! {
     /// Sets the netmask length for an inet or cidr value.
     /// For inet, the address part does not changes. For cidr, address bits to the right of the new
     /// netmask are set to zero.
diff --git a/diesel/src/pg/expression/helper_types.rs b/diesel/src/pg/expression/helper_types.rs
index a8ba9570419b..c205e1d157a9 100644
--- a/diesel/src/pg/expression/helper_types.rs
+++ b/diesel/src/pg/expression/helper_types.rs
@@ -1,15 +1,21 @@
 use crate::dsl::{AsExpr, AsExprOf, SqlTypeOf};
 use crate::expression::grouped::Grouped;
+use crate::expression::Expression;
+use crate::pg::expression::expression_methods::private::{JsonIndex, JsonRemoveIndex};
 use crate::pg::types::sql_types::Array;
-use crate::sql_types::{Binary, Inet, Integer, Jsonb, VarChar};
+use crate::sql_types::{Inet, Integer, VarChar};
 
 /// The return type of [`lhs.ilike(rhs)`](super::expression_methods::PgTextExpressionMethods::ilike)
 #[cfg(feature = "postgres_backend")]
 pub type ILike<Lhs, Rhs> = Grouped<super::operators::ILike<Lhs, AsExprOf<Rhs, VarChar>>>;
+#[doc(hidden)] // required for #[auto_type]
+pub type Ilike<Lhs, Rhs> = ILike<Lhs, Rhs>;
 
 /// The return type of [`lhs.not_ilike(rhs)`](super::expression_methods::PgTextExpressionMethods::not_ilike)
 #[cfg(feature = "postgres_backend")]
 pub type NotILike<Lhs, Rhs> = Grouped<super::operators::NotILike<Lhs, AsExprOf<Rhs, VarChar>>>;
+#[doc(hidden)] // required for #[auto_type]
+pub type NotIlike<Lhs, Rhs> = NotILike<Lhs, Rhs>;
 
 /// The return type of [`lhs.similar_to(rhs)`](super::expression_methods::PgTextExpressionMethods::similar_to)
 #[cfg(feature = "postgres_backend")]
@@ -37,7 +43,11 @@ pub type OverlapsWith<Lhs, Rhs> = Grouped<super::operators::OverlapsWith<Lhs, As
 /// The return type of [`lhs.contains(rhs)`](super::expression_methods::PgArrayExpressionMethods::contains)
 /// for array expressions
 #[cfg(feature = "postgres_backend")]
-pub type ArrayContains<Lhs, Rhs> = Grouped<super::operators::Contains<Lhs, AsExpr<Rhs, Lhs>>>;
+pub type Contains<Lhs, Rhs> = Grouped<super::operators::Contains<Lhs, AsExpr<Rhs, Lhs>>>;
+
+#[doc(hidden)]
+#[deprecated(note = "Use `Contains` instead")]
+pub type ArrayContains<Lhs, Rhs> = Contains<Lhs, Rhs>;
 
 /// The return type of [`lhs.contains(rhs)`](super::expression_methods::PgRangeExpressionMethods::contains)
 /// for range expressions
@@ -76,6 +86,9 @@ pub type ContainsNet<Lhs, Rhs> = Grouped<super::operators::ContainsNet<Lhs, AsEx
 pub type ContainsNetLoose<Lhs, Rhs> =
     Grouped<super::operators::ContainsNetLoose<Lhs, AsExprOf<Rhs, Inet>>>;
 
+#[doc(hidden)] // used by `#[auto_type]`
+pub type ContainsOrEq<Lhs, Rhs> = ContainsNetLoose<Lhs, Rhs>;
+
 /// The return type of [`lhs.is_contained_by(rhs)`]((super::expression_methods::PgNetExpressionMethods::is_contained_by)
 /// for network types
 #[cfg(feature = "postgres_backend")]
@@ -87,6 +100,9 @@ pub type IsContainedByNet<Lhs, Rhs> =
 pub type IsContainedByNetLoose<Lhs, Rhs> =
     Grouped<super::operators::IsContainedByNetLoose<Lhs, AsExprOf<Rhs, Inet>>>;
 
+#[doc(hidden)] // is used by `#[auto_type]`
+pub type IsContainedByOrEq<Lhs, Rhs> = IsContainedByNetLoose<Lhs, Rhs>;
+
 /// The return type of [`lhs.overlaps_with(rhs)`](super::expression_methods::PgNetExpressionMethods::overlaps_with)
 /// for network types
 #[cfg(feature = "postgres_backend")]
@@ -106,85 +122,125 @@ pub type OrNet<Lhs, Rhs> = Grouped<super::operators::OrNet<Lhs, AsExprOf<Rhs, In
 pub type DifferenceNet<Lhs, Rhs> =
     Grouped<super::operators::DifferenceNet<Lhs, AsExprOf<Rhs, Inet>>>;
 
-/// The return type of [`lsh.concat(rhs)`](super::expression_methods::PgJsonbExpressionMethods::concat)
-#[cfg(feature = "postgres_backend")]
-pub type ConcatJsonb<Lhs, Rhs> = Grouped<super::operators::ConcatJsonb<Lhs, AsExprOf<Rhs, Jsonb>>>;
+#[doc(hidden)] // used by `#[auto_type]`
+pub type Diff<Lhs, Rhs> = DifferenceNet<Lhs, Rhs>;
+
+#[doc(hidden)]
+#[deprecated(note = "Use `dsl::Concat` instead")]
+pub type ConcatJsonb<Lhs, Rhs> = crate::dsl::Concat<Lhs, Rhs>;
 
 /// The return type of [`lsh.has_key(rhs)`](super::expression_methods::PgJsonbExpressionMethods::has_key)
 #[cfg(feature = "postgres_backend")]
 pub type HasKeyJsonb<Lhs, Rhs> =
     Grouped<super::operators::HasKeyJsonb<Lhs, AsExprOf<Rhs, VarChar>>>;
 
+#[doc(hidden)] // needed for `#[auto_type]`
+pub type HasKey<Lhs, Rhs> = HasKeyJsonb<Lhs, Rhs>;
+
 /// The return type of [`lsh.has_any_key(rhs)`](super::expression_methods::PgJsonbExpressionMethods::has_any_key)
 #[cfg(feature = "postgres_backend")]
 pub type HasAnyKeyJsonb<Lhs, Rhs> =
     Grouped<super::operators::HasAnyKeyJsonb<Lhs, AsExprOf<Rhs, Array<VarChar>>>>;
 
+#[doc(hidden)] // needed for `#[auto_type]`
+pub type HasAnyKey<Lhs, Rhs> = HasAnyKeyJsonb<Lhs, Rhs>;
+
 /// The return type of [`lsh.has_all_keys(rhs)`](super::expression_methods::PgJsonbExpressionMethods::has_all_keys)
 #[cfg(feature = "postgres_backend")]
 pub type HasAllKeysJsonb<Lhs, Rhs> =
     Grouped<super::operators::HasAllKeysJsonb<Lhs, AsExprOf<Rhs, Array<VarChar>>>>;
 
-/// The return type of [`lsh.contains(rhs)`](super::expression_methods::PgJsonbExpressionMethods::contains)
-/// for jsonb types
-#[cfg(feature = "postgres_backend")]
-pub type ContainsJsonb<Lhs, Rhs> =
-    Grouped<super::operators::ContainsJsonb<Lhs, AsExprOf<Rhs, Jsonb>>>;
+#[doc(hidden)] // needed for `#[auto_type]`
+pub type HasAllKeys<Lhs, Rhs> = HasAllKeysJsonb<Lhs, Rhs>;
 
-/// The return type of [`lsh.is_contained_by(rhs)`](super::expression_methods::PgJsonbExpressionMethods::is_contained_by)
-/// for jsonb types
-#[cfg(feature = "postgres_backend")]
-pub type IsContainedByJsonb<Lhs, Rhs> =
-    Grouped<super::operators::IsContainedByJsonb<Lhs, AsExprOf<Rhs, Jsonb>>>;
+#[doc(hidden)]
+#[deprecated(note = "Use `dsl::Contains` instead")]
+pub type ContainsJsonb<Lhs, Rhs> = Contains<Lhs, Rhs>;
+
+#[doc(hidden)]
+#[deprecated(note = "Use `dsl::IsContainedBy` instead")]
+pub type IsContainedByJsonb<Lhs, Rhs> = IsContainedBy<Lhs, Rhs>;
 
 /// The return type of [`lhs.index(rhs)`](super::expression_methods::PgArrayExpressionMethods::index)
 #[cfg(feature = "postgres_backend")]
-pub type ArrayIndex<Lhs, Rhs> = super::operators::ArrayIndex<Lhs, AsExprOf<Rhs, Integer>>;
+pub type Index<Lhs, Rhs> = super::operators::ArrayIndex<Lhs, AsExprOf<Rhs, Integer>>;
+
+#[doc(hidden)]
+#[deprecated(note = "Use `Index` instead")]
+pub type ArrayIndex<Lhs, Rhs> = Index<Lhs, Rhs>;
 
 /// The return type of [`lhs.remove(rhs)`](super::expression_methods::PgJsonbExpressionMethods::remove)
 #[cfg(feature = "postgres_backend")]
 pub type RemoveFromJsonb<Lhs, Rhs, ST> =
     Grouped<super::operators::RemoveFromJsonb<Lhs, AsExprOf<Rhs, ST>>>;
 
+#[doc(hidden)] // needed for `#[auto_type]`
+pub type Remove<Lhs, Rhs> = RemoveFromJsonb<
+    Lhs,
+    <Rhs as JsonRemoveIndex>::Expression,
+    <<Rhs as JsonRemoveIndex>::Expression as Expression>::SqlType,
+>;
+
 /// The return type of [`lhs.retrieve_as_object(rhs)`](super::expression_methods::PgAnyJsonExpressionMethods::retrieve_as_object)
 #[cfg(feature = "postgres_backend")]
 pub type RetrieveAsObjectJson<Lhs, Rhs, ST> =
     Grouped<super::operators::RetrieveAsObjectJson<Lhs, AsExprOf<Rhs, ST>>>;
 
+#[doc(hidden)] // needed for `#[auto_type]`
+pub type RetrieveAsObject<Lhs, Rhs> = RetrieveAsObjectJson<
+    Lhs,
+    <Rhs as JsonIndex>::Expression,
+    <<Rhs as JsonIndex>::Expression as Expression>::SqlType,
+>;
+
 /// The return type of [`lhs.retrieve_as_text(rhs)`](super::expression_methods::PgAnyJsonExpressionMethods::retrieve_as_text)
 #[cfg(feature = "postgres_backend")]
 pub type RetrieveAsTextJson<Lhs, Rhs, ST> =
     Grouped<super::operators::RetrieveAsTextJson<Lhs, AsExprOf<Rhs, ST>>>;
 
+#[doc(hidden)] // needed for `#[auto_type]`
+pub type RetrieveAsText<Lhs, Rhs> = RetrieveAsTextJson<
+    Lhs,
+    <Rhs as JsonIndex>::Expression,
+    <<Rhs as JsonIndex>::Expression as Expression>::SqlType,
+>;
+
 /// The return type of [`lhs.retrieve_by_path_as_object(rhs)`](super::expression_methods::PgAnyJsonExpressionMethods::retrieve_by_path_as_object)
 #[cfg(feature = "postgres_backend")]
 pub type RetrieveByPathAsObjectJson<Lhs, Rhs> =
     Grouped<super::operators::RetrieveByPathAsObjectJson<Lhs, AsExprOf<Rhs, Array<VarChar>>>>;
 
+#[doc(hidden)] // needed for `#[auto_type]`
+pub type RetrieveByPathAsObject<Lhs, Rhs> = RetrieveByPathAsObjectJson<Lhs, Rhs>;
+
 /// The return type of [`lhs.retrieve_by_path_as_text(rhs)`](super::expression_methods::PgAnyJsonExpressionMethods::retrieve_by_path_as_text)
 #[cfg(feature = "postgres_backend")]
 pub type RetrieveByPathAsTextJson<Lhs, Rhs> =
     Grouped<super::operators::RetrieveByPathAsTextJson<Lhs, AsExprOf<Rhs, Array<VarChar>>>>;
 
+#[doc(hidden)] // needed for `#[auto_type]`
+pub type RetrieveByPathAsText<Lhs, Rhs> = RetrieveByPathAsTextJson<Lhs, Rhs>;
+
 /// The return type of [`lhs.remove_by_path(rhs)`](super::expression_methods::PgJsonbExpressionMethods::remove_by_path)
 #[cfg(feature = "postgres_backend")]
 pub type RemoveByPathFromJsonb<Lhs, Rhs> =
     Grouped<super::operators::RemoveByPathFromJsonb<Lhs, AsExprOf<Rhs, Array<VarChar>>>>;
 
-/// The return type of [`lhs.remove_by_path(rhs)`](super::expression_methods::PgBinaryExpressionMethods::concat)
-#[cfg(feature = "postgres_backend")]
-pub type ConcatBinary<Lhs, Rhs> =
-    Grouped<super::operators::ConcatBinary<Lhs, AsExprOf<Rhs, Binary>>>;
+#[doc(hidden)] // needed for `#[auto_type]`
+pub type RemoveByPath<Lhs, Rhs> = RemoveByPathFromJsonb<Lhs, Rhs>;
 
-/// The return type of [`lhs.remove_by_path(rhs)`](super::expression_methods::PgBinaryExpressionMethods::like)
-#[cfg(feature = "postgres_backend")]
-pub type LikeBinary<Lhs, Rhs> = Grouped<super::operators::LikeBinary<Lhs, AsExprOf<Rhs, Binary>>>;
+#[doc(hidden)]
+#[deprecated(note = "Use `dsl::Concat` instead")]
+pub type ConcatBinary<Lhs, Rhs> = crate::dsl::Concat<Lhs, Rhs>;
 
-/// The return type of [`lhs.remove_by_path(rhs)`](super::expression_methods::PgBinaryExpressionMethods::not_like)
-#[cfg(feature = "postgres_backend")]
-pub type NotLikeBinary<Lhs, Rhs> =
-    Grouped<super::operators::NotLikeBinary<Lhs, AsExprOf<Rhs, Binary>>>;
+#[doc(hidden)]
+#[deprecated(note = "Use `dsl::Like` instead")]
+pub type LikeBinary<Lhs, Rhs> = crate::dsl::Like<Lhs, Rhs>;
 
-/// The return type of [`lhs.concat(rhs)`](super::expression_methods::PgArrayExpressionMethods::concat)
-#[cfg(feature = "postgres_backend")]
-pub type ConcatArray<Lhs, Rhs> = Grouped<super::operators::ConcatArray<Lhs, AsExpr<Rhs, Lhs>>>;
+#[doc(hidden)]
+#[deprecated(note = "Use `dsl::NotLike` instead")]
+pub type NotLikeBinary<Lhs, Rhs> = crate::dsl::NotLike<Lhs, Rhs>;
+
+#[doc(hidden)]
+#[deprecated(note = "Use `dsl::Concat` instead")]
+pub type ConcatArray<Lhs, Rhs> = crate::dsl::Concat<Lhs, Rhs>;
diff --git a/diesel/src/pg/expression/operators.rs b/diesel/src/pg/expression/operators.rs
index a7220cbfb95e..8d7b834f29e8 100644
--- a/diesel/src/pg/expression/operators.rs
+++ b/diesel/src/pg/expression/operators.rs
@@ -5,7 +5,7 @@ use crate::pg::Pg;
 use crate::query_builder::update_statement::changeset::AssignmentTarget;
 use crate::query_builder::{AstPass, QueryFragment, QueryId};
 use crate::sql_types::{
-    Array, Bigint, Binary, Bool, DieselNumericOps, Inet, Integer, Jsonb, SqlType, Text,
+    Array, Bigint, Bool, DieselNumericOps, Inet, Integer, Jsonb, SqlType, Text,
 };
 use crate::{Column, QueryResult};
 
@@ -27,12 +27,9 @@ infix_operator!(IsContainedByNetLoose, " <<= ", backend: Pg);
 infix_operator!(AndNet, " & ", Inet, backend: Pg);
 infix_operator!(OrNet, " | ", Inet, backend: Pg);
 infix_operator!(DifferenceNet, " - ", Bigint, backend: Pg);
-infix_operator!(ConcatJsonb, " || ", Jsonb, backend: Pg);
 infix_operator!(HasKeyJsonb, " ? ", backend: Pg);
 infix_operator!(HasAnyKeyJsonb, " ?| ", backend: Pg);
 infix_operator!(HasAllKeysJsonb, " ?& ", backend: Pg);
-infix_operator!(ContainsJsonb, " @> ", backend: Pg);
-infix_operator!(IsContainedByJsonb, " <@ ", backend: Pg);
 infix_operator!(RemoveFromJsonb, " - ", Jsonb, backend: Pg);
 __diesel_infix_operator!(
     RetrieveAsObjectJson,
@@ -49,15 +46,6 @@ __diesel_infix_operator!(
 );
 infix_operator!(RetrieveByPathAsTextJson, " #>> ", Text, backend: Pg);
 infix_operator!(RemoveByPathFromJsonb, " #-", Jsonb, backend: Pg);
-infix_operator!(ConcatBinary, " || ", Binary, backend: Pg);
-infix_operator!(LikeBinary, " LIKE ", backend: Pg);
-infix_operator!(NotLikeBinary, " NOT LIKE ", backend: Pg);
-__diesel_infix_operator!(
-    ConcatArray,
-    " || ",
-    __diesel_internal_SameResultAsInput,
-    backend: Pg
-);
 
 #[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)]
 #[doc(hidden)]
diff --git a/diesel/src/pg/metadata_lookup.rs b/diesel/src/pg/metadata_lookup.rs
index bf08a9d2706e..7e09c643fd5d 100644
--- a/diesel/src/pg/metadata_lookup.rs
+++ b/diesel/src/pg/metadata_lookup.rs
@@ -214,4 +214,4 @@ table! {
 joinable!(pg_type -> pg_namespace(typnamespace));
 allow_tables_to_appear_in_same_query!(pg_type, pg_namespace);
 
-sql_function! { fn pg_my_temp_schema() -> Oid; }
+define_sql_function! { fn pg_my_temp_schema() -> Oid; }
diff --git a/diesel/src/query_builder/ast_pass.rs b/diesel/src/query_builder/ast_pass.rs
index 9e4de8fc37ed..f1bf8c2a1d45 100644
--- a/diesel/src/query_builder/ast_pass.rs
+++ b/diesel/src/query_builder/ast_pass.rs
@@ -256,7 +256,7 @@ where
     #[cfg_attr(
         not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
         doc(hidden)
-    )] // This is used by the `sql_function` macro
+    )] // This is used by the `define_sql_function` macro
     #[cfg_attr(
         doc_cfg,
         doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
diff --git a/diesel/src/query_builder/insert_statement/mod.rs b/diesel/src/query_builder/insert_statement/mod.rs
index dc7c2667d393..fd992de0c27b 100644
--- a/diesel/src/query_builder/insert_statement/mod.rs
+++ b/diesel/src/query_builder/insert_statement/mod.rs
@@ -18,6 +18,8 @@ use crate::query_source::{Column, Table};
 use crate::{insertable::*, QuerySource};
 use std::marker::PhantomData;
 
+pub(crate) use self::private::InsertAutoTypeHelper;
+
 #[cfg(feature = "sqlite")]
 mod insert_with_default_for_sqlite;
 
@@ -537,4 +539,16 @@ mod private {
             Ok(())
         }
     }
+
+    // otherwise rustc complains at a different location that this trait is more private than the other item that uses it
+    #[allow(unreachable_pub)]
+    pub trait InsertAutoTypeHelper {
+        type Table;
+        type Op;
+    }
+
+    impl<T, Op> InsertAutoTypeHelper for crate::query_builder::IncompleteInsertStatement<T, Op> {
+        type Table = T;
+        type Op = Op;
+    }
 }
diff --git a/diesel/src/query_builder/update_statement/mod.rs b/diesel/src/query_builder/update_statement/mod.rs
index d3df633abf2e..b234d35cc03a 100644
--- a/diesel/src/query_builder/update_statement/mod.rs
+++ b/diesel/src/query_builder/update_statement/mod.rs
@@ -14,6 +14,8 @@ use crate::query_source::Table;
 use crate::result::Error::QueryBuilderError;
 use crate::{query_builder::*, QuerySource};
 
+pub(crate) use self::private::UpdateAutoTypeHelper;
+
 impl<T: QuerySource, U> UpdateStatement<T, U, SetNotCalled> {
     pub(crate) fn new(target: UpdateTarget<T, U>) -> Self {
         UpdateStatement {
@@ -287,3 +289,20 @@ impl<T: QuerySource, U, V> UpdateStatement<T, U, V, NoReturningClause> {
 /// Indicates that you have not yet called `.set` on an update statement
 #[derive(Debug, Clone, Copy)]
 pub struct SetNotCalled;
+
+mod private {
+    // otherwise rustc complains at a different location that this trait is more private than the other item that uses it
+    #[allow(unreachable_pub)]
+    pub trait UpdateAutoTypeHelper {
+        type Table;
+        type Where;
+    }
+
+    impl<T, W> UpdateAutoTypeHelper for crate::query_builder::UpdateStatement<T, W>
+    where
+        T: crate::QuerySource,
+    {
+        type Table = T;
+        type Where = W;
+    }
+}
diff --git a/diesel/src/query_dsl/limit_dsl.rs b/diesel/src/query_dsl/limit_dsl.rs
index 4f8b823c39b7..3aa73662fe47 100644
--- a/diesel/src/query_dsl/limit_dsl.rs
+++ b/diesel/src/query_dsl/limit_dsl.rs
@@ -7,7 +7,7 @@ use crate::query_source::Table;
 /// to call `limit` from generic code.
 ///
 /// [`QueryDsl`]: crate::QueryDsl
-pub trait LimitDsl {
+pub trait LimitDsl<DummyArgForAutoType = i64> {
     /// The type returned by `.limit`
     type Output;
 
diff --git a/diesel/src/query_dsl/offset_dsl.rs b/diesel/src/query_dsl/offset_dsl.rs
index 0d783edfa478..fa625546357a 100644
--- a/diesel/src/query_dsl/offset_dsl.rs
+++ b/diesel/src/query_dsl/offset_dsl.rs
@@ -7,7 +7,7 @@ use crate::query_source::Table;
 /// to call `offset` from generic code.
 ///
 /// [`QueryDsl`]: crate::QueryDsl
-pub trait OffsetDsl {
+pub trait OffsetDsl<DummyArgForAutoType = i64> {
     /// The type returned by `.offset`.
     type Output;
 
diff --git a/diesel/src/sqlite/connection/mod.rs b/diesel/src/sqlite/connection/mod.rs
index 05f7cc1f1c4d..8151ee0d12cb 100644
--- a/diesel/src/sqlite/connection/mod.rs
+++ b/diesel/src/sqlite/connection/mod.rs
@@ -20,6 +20,7 @@ use self::raw::RawConnection;
 use self::statement_iterator::*;
 use self::stmt::{Statement, StatementUse};
 use super::SqliteAggregateFunction;
+use crate::connection::instrumentation::InstrumentationEvent;
 use crate::connection::instrumentation::StrQueryHelper;
 use crate::connection::statement_cache::StatementCache;
 use crate::connection::*;
@@ -633,12 +634,12 @@ mod tests {
     }
 
     use crate::sql_types::Text;
-    sql_function!(fn fun_case(x: Text) -> Text);
+    define_sql_function!(fn fun_case(x: Text) -> Text);
 
     #[test]
     fn register_custom_function() {
         let connection = &mut SqliteConnection::establish(":memory:").unwrap();
-        fun_case::register_impl(connection, |x: String| {
+        fun_case_utils::register_impl(connection, |x: String| {
             x.chars()
                 .enumerate()
                 .map(|(i, c)| {
@@ -658,23 +659,23 @@ mod tests {
         assert_eq!("fOoBaR", mapped_string);
     }
 
-    sql_function!(fn my_add(x: Integer, y: Integer) -> Integer);
+    define_sql_function!(fn my_add(x: Integer, y: Integer) -> Integer);
 
     #[test]
     fn register_multiarg_function() {
         let connection = &mut SqliteConnection::establish(":memory:").unwrap();
-        my_add::register_impl(connection, |x: i32, y: i32| x + y).unwrap();
+        my_add_utils::register_impl(connection, |x: i32, y: i32| x + y).unwrap();
 
         let added = crate::select(my_add(1, 2)).get_result::<i32>(connection);
         assert_eq!(Ok(3), added);
     }
 
-    sql_function!(fn answer() -> Integer);
+    define_sql_function!(fn answer() -> Integer);
 
     #[test]
     fn register_noarg_function() {
         let connection = &mut SqliteConnection::establish(":memory:").unwrap();
-        answer::register_impl(connection, || 42).unwrap();
+        answer_utils::register_impl(connection, || 42).unwrap();
 
         let answer = crate::select(answer()).get_result::<i32>(connection);
         assert_eq!(Ok(42), answer);
@@ -683,19 +684,19 @@ mod tests {
     #[test]
     fn register_nondeterministic_noarg_function() {
         let connection = &mut SqliteConnection::establish(":memory:").unwrap();
-        answer::register_nondeterministic_impl(connection, || 42).unwrap();
+        answer_utils::register_nondeterministic_impl(connection, || 42).unwrap();
 
         let answer = crate::select(answer()).get_result::<i32>(connection);
         assert_eq!(Ok(42), answer);
     }
 
-    sql_function!(fn add_counter(x: Integer) -> Integer);
+    define_sql_function!(fn add_counter(x: Integer) -> Integer);
 
     #[test]
     fn register_nondeterministic_function() {
         let connection = &mut SqliteConnection::establish(":memory:").unwrap();
         let mut y = 0;
-        add_counter::register_nondeterministic_impl(connection, move |x: i32| {
+        add_counter_utils::register_nondeterministic_impl(connection, move |x: i32| {
             y += 1;
             x + y
         })
@@ -706,7 +707,7 @@ mod tests {
         assert_eq!(Ok((2, 3, 4)), added);
     }
 
-    sql_function! {
+    define_sql_function! {
         #[aggregate]
         fn my_sum(expr: Integer) -> Integer;
     }
@@ -749,7 +750,7 @@ mod tests {
             .execute(connection)
             .unwrap();
 
-        my_sum::register_impl::<MySum, _>(connection).unwrap();
+        my_sum_utils::register_impl::<MySum, _>(connection).unwrap();
 
         let result = my_sum_example
             .select(my_sum(value))
@@ -768,7 +769,7 @@ mod tests {
         .execute(connection)
         .unwrap();
 
-        my_sum::register_impl::<MySum, _>(connection).unwrap();
+        my_sum_utils::register_impl::<MySum, _>(connection).unwrap();
 
         let result = my_sum_example
             .select(my_sum(value))
@@ -776,7 +777,7 @@ mod tests {
         assert_eq!(Ok(0), result);
     }
 
-    sql_function! {
+    define_sql_function! {
         #[aggregate]
         fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable<Integer>;
     }
@@ -840,7 +841,7 @@ mod tests {
         .execute(connection)
         .unwrap();
 
-        range_max::register_impl::<RangeMax<i32>, _, _, _>(connection).unwrap();
+        range_max_utils::register_impl::<RangeMax<i32>, _, _, _>(connection).unwrap();
         let result = range_max_example
             .select(range_max(value1, value2, value3))
             .get_result::<Option<i32>>(connection)
diff --git a/diesel/src/sqlite/mod.rs b/diesel/src/sqlite/mod.rs
index 6a9fbbe9fc58..45cabe6f86c4 100644
--- a/diesel/src/sqlite/mod.rs
+++ b/diesel/src/sqlite/mod.rs
@@ -21,9 +21,9 @@ pub use self::query_builder::SqliteQueryBuilder;
 
 /// Trait for the implementation of a SQLite aggregate function
 ///
-/// This trait is to be used in conjunction with the `sql_function!`
+/// This trait is to be used in conjunction with the `define_sql_function!`
 /// macro for defining a custom SQLite aggregate function. See
-/// the documentation [there](super::prelude::sql_function!) for details.
+/// the documentation [there](super::prelude::define_sql_function!) for details.
 pub trait SqliteAggregateFunction<Args>: Default {
     /// The result type of the SQLite aggregate function
     type Output;
diff --git a/diesel/src/sqlite/types/date_and_time/chrono.rs b/diesel/src/sqlite/types/date_and_time/chrono.rs
index d6aa1bcad41a..a8df1a0099e8 100644
--- a/diesel/src/sqlite/types/date_and_time/chrono.rs
+++ b/diesel/src/sqlite/types/date_and_time/chrono.rs
@@ -250,9 +250,9 @@ mod tests {
     use crate::sql_types::{Text, Time, Timestamp, TimestamptzSqlite};
     use crate::test_helpers::connection;
 
-    sql_function!(fn datetime(x: Text) -> Timestamp);
-    sql_function!(fn time(x: Text) -> Time);
-    sql_function!(fn date(x: Text) -> Date);
+    define_sql_function!(fn datetime(x: Text) -> Timestamp);
+    define_sql_function!(fn time(x: Text) -> Time);
+    define_sql_function!(fn date(x: Text) -> Date);
 
     #[test]
     fn unix_epoch_encodes_correctly() {
diff --git a/diesel/src/sqlite/types/date_and_time/time.rs b/diesel/src/sqlite/types/date_and_time/time.rs
index 3995f39c459e..ae2475f65bcc 100644
--- a/diesel/src/sqlite/types/date_and_time/time.rs
+++ b/diesel/src/sqlite/types/date_and_time/time.rs
@@ -275,9 +275,9 @@ mod tests {
     use crate::sql_types::{Text, Time, Timestamp, TimestamptzSqlite};
     use crate::test_helpers::connection;
 
-    sql_function!(fn datetime(x: Text) -> Timestamp);
-    sql_function!(fn time(x: Text) -> Time);
-    sql_function!(fn date(x: Text) -> Date);
+    define_sql_function!(fn datetime(x: Text) -> Timestamp);
+    define_sql_function!(fn time(x: Text) -> Time);
+    define_sql_function!(fn date(x: Text) -> Date);
 
     #[test]
     fn unix_epoch_encodes_correctly() {
diff --git a/diesel_cli/src/infer_schema_internals/information_schema.rs b/diesel_cli/src/infer_schema_internals/information_schema.rs
index 6c762de91db7..44d8775ac900 100644
--- a/diesel_cli/src/infer_schema_internals/information_schema.rs
+++ b/diesel_cli/src/infer_schema_internals/information_schema.rs
@@ -31,7 +31,7 @@ impl DefaultSchema for Pg {
 }
 
 #[cfg(feature = "mysql")]
-sql_function!(fn database() -> VarChar);
+define_sql_function!(fn database() -> VarChar);
 
 #[cfg(feature = "mysql")]
 impl DefaultSchema for Mysql {
diff --git a/diesel_cli/src/infer_schema_internals/mysql.rs b/diesel_cli/src/infer_schema_internals/mysql.rs
index f549c66657b9..0a46e460aad5 100644
--- a/diesel_cli/src/infer_schema_internals/mysql.rs
+++ b/diesel_cli/src/infer_schema_internals/mysql.rs
@@ -10,7 +10,7 @@ use super::information_schema::DefaultSchema;
 use super::table_data::TableName;
 use crate::print_schema::ColumnSorting;
 
-diesel::sql_function! {
+diesel::define_sql_function! {
     #[sql_name = "NULLIF"]
     fn null_if_text(lhs: sql_types::Text, rhs: sql_types::Text) -> sql_types::Nullable<sql_types::Text>
 }
diff --git a/diesel_cli/src/infer_schema_internals/pg.rs b/diesel_cli/src/infer_schema_internals/pg.rs
index 779fa12c8c3d..e991129aa592 100644
--- a/diesel_cli/src/infer_schema_internals/pg.rs
+++ b/diesel_cli/src/infer_schema_internals/pg.rs
@@ -70,7 +70,7 @@ fn regclass(table: &TableName) -> Regclass<AsExprOf<String, sql_types::Text>> {
     ))
 }
 
-diesel::sql_function!(fn col_description(table: sql_types::Oid, column_number: sql_types::BigInt) -> sql_types::Nullable<sql_types::Text>);
+diesel::define_sql_function!(fn col_description(table: sql_types::Oid, column_number: sql_types::BigInt) -> sql_types::Nullable<sql_types::Text>);
 
 pub fn get_table_data(
     conn: &mut PgConnection,
@@ -139,7 +139,7 @@ where
     }
 }
 
-sql_function!(fn obj_description(oid: sql_types::Oid, catalog: sql_types::Text) -> Nullable<Text>);
+define_sql_function!(fn obj_description(oid: sql_types::Oid, catalog: sql_types::Text) -> Nullable<Text>);
 
 pub fn get_table_comment(
     conn: &mut PgConnection,
@@ -166,7 +166,7 @@ mod information_schema {
     }
 }
 
-sql_function! {
+define_sql_function! {
     #[aggregate]
     fn array_agg(input: diesel::sql_types::Text) -> diesel::sql_types::Array<diesel::sql_types::Text>;
 }
diff --git a/diesel_derives/src/lib.rs b/diesel_derives/src/lib.rs
index 7503c2d49f43..df8f04d3bb17 100644
--- a/diesel_derives/src/lib.rs
+++ b/diesel_derives/src/lib.rs
@@ -1013,46 +1013,17 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// function. For example, this invocation:
 ///
 /// ```ignore
-/// sql_function!(fn lower(x: Text) -> Text);
+/// define_sql_function!(fn lower(x: Text) -> Text);
 /// ```
 ///
 /// will generate this code:
 ///
 /// ```ignore
-/// pub fn lower<X>(x: X) -> lower::HelperType<X> {
+/// pub fn lower<X>(x: X) -> lower<X> {
 ///     ...
 /// }
 ///
-/// pub(crate) mod lower {
-///     pub type HelperType<X> = ...;
-/// }
-/// ```
-///
-/// If you are using this macro for part of a library, where the function is
-/// part of your public API, it is highly recommended that you re-export this
-/// helper type with the same name as your function. This is the standard
-/// structure:
-///
-/// ```ignore
-/// pub mod functions {
-///     use super::types::*;
-///     use diesel::sql_types::*;
-///
-///     sql_function! {
-///         /// Represents the Pg `LENGTH` function used with `tsvector`s.
-///         fn length(x: TsVector) -> Integer;
-///     }
-/// }
-///
-/// pub mod helper_types {
-///     /// The return type of `length(expr)`
-///     pub type Length<Expr> = functions::length::HelperType<Expr>;
-/// }
-///
-/// pub mod dsl {
-///     pub use functions::*;
-///     pub use helper_types::*;
-/// }
+/// pub type lower<X> = ...;
 /// ```
 ///
 /// Most attributes given to this macro will be put on the generated function
@@ -1068,7 +1039,7 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// #
 /// use diesel::sql_types::Text;
 ///
-/// sql_function! {
+/// define_sql_function! {
 ///     /// Represents the `canon_crate_name` SQL function, created in
 ///     /// migration ....
 ///     fn canon_crate_name(a: Text) -> Text;
@@ -1106,7 +1077,7 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// #
 /// use diesel::sql_types::Foldable;
 ///
-/// sql_function! {
+/// define_sql_function! {
 ///     #[aggregate]
 ///     #[sql_name = "SUM"]
 ///     fn sum<ST: Foldable>(expr: ST) -> ST::Sum;
@@ -1121,7 +1092,7 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// # SQL Functions without Arguments
 ///
 /// A common example is ordering a query using the `RANDOM()` sql function,
-/// which can be implemented using `sql_function!` like this:
+/// which can be implemented using `define_sql_function!` like this:
 ///
 /// ```rust
 /// # extern crate diesel;
@@ -1129,7 +1100,7 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// #
 /// # table! { crates { id -> Integer, name -> VarChar, } }
 /// #
-/// sql_function!(fn random() -> Text);
+/// define_sql_function!(fn random() -> Text);
 ///
 /// # fn main() {
 /// # use self::crates::dsl::*;
@@ -1142,8 +1113,8 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// On most backends, the implementation of the function is defined in a
 /// migration using `CREATE FUNCTION`. On SQLite, the function is implemented in
 /// Rust instead. You must call `register_impl` or
-/// `register_nondeterministic_impl` with every connection before you can use
-/// the function.
+/// `register_nondeterministic_impl` (in the generated function's `_internals`
+/// module) with every connection before you can use the function.
 ///
 /// These functions will only be generated if the `sqlite` feature is enabled,
 /// and the function is not generic.
@@ -1163,13 +1134,13 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// # }
 /// #
 /// use diesel::sql_types::{Integer, Double};
-/// sql_function!(fn add_mul(x: Integer, y: Integer, z: Double) -> Double);
+/// define_sql_function!(fn add_mul(x: Integer, y: Integer, z: Double) -> Double);
 ///
 /// # #[cfg(feature = "sqlite")]
 /// # fn run_test() -> Result<(), Box<dyn std::error::Error>> {
 /// let connection = &mut SqliteConnection::establish(":memory:")?;
 ///
-/// add_mul::register_impl(connection, |x: i32, y: i32, z: f64| {
+/// add_mul_utils::register_impl(connection, |x: i32, y: i32, z: f64| {
 ///     (x + y) as f64 * z
 /// })?;
 ///
@@ -1191,8 +1162,8 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// ## Custom Aggregate Functions
 ///
 /// Custom aggregate functions can be created in SQLite by adding an `#[aggregate]`
-/// attribute inside `sql_function`. `register_impl` needs to be called on
-/// the generated function with a type implementing the
+/// attribute inside `define_sql_function`. `register_impl` (in the generated function's `_utils`
+/// module) needs to be called with a type implementing the
 /// [SqliteAggregateFunction](../diesel/sqlite/trait.SqliteAggregateFunction.html)
 /// trait as a type parameter as shown in the examples below.
 ///
@@ -1212,7 +1183,7 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// # #[cfg(feature = "sqlite")]
 /// use diesel::sqlite::SqliteAggregateFunction;
 ///
-/// sql_function! {
+/// define_sql_function! {
 ///     #[aggregate]
 ///     fn my_sum(x: Integer) -> Integer;
 /// }
@@ -1250,7 +1221,7 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// #        .execute(connection)
 /// #        .unwrap();
 ///
-///     my_sum::register_impl::<MySum, _>(connection)?;
+///     my_sum_utils::register_impl::<MySum, _>(connection)?;
 ///
 ///     let total_score = players.select(my_sum(score))
 ///         .get_result::<i32>(connection)?;
@@ -1280,7 +1251,7 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// # #[cfg(feature = "sqlite")]
 /// use diesel::sqlite::SqliteAggregateFunction;
 ///
-/// sql_function! {
+/// define_sql_function! {
 ///     #[aggregate]
 ///     fn range_max(x0: Float, x1: Float) -> Nullable<Float>;
 /// }
@@ -1330,7 +1301,7 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// #        .execute(connection)
 /// #        .unwrap();
 ///
-///     range_max::register_impl::<RangeMax<f32>, _, _>(connection)?;
+///     range_max_utils::register_impl::<RangeMax<f32>, _, _>(connection)?;
 ///
 ///     let result = student_avgs.select(range_max(s1_avg, s2_avg))
 ///         .get_result::<Option<f32>>(connection)?;
@@ -1344,8 +1315,41 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream {
 /// }
 /// ```
 #[proc_macro]
+pub fn define_sql_function(input: TokenStream) -> TokenStream {
+    sql_function::expand(parse_macro_input!(input), false).into()
+}
+
+/// A legacy version of [`define_sql_function!`].
+///
+/// The difference is that it makes the helper type available in a module named the exact same as
+/// the function:
+///
+/// ```ignore
+/// sql_function!(fn lower(x: Text) -> Text);
+/// ```
+///
+/// will generate this code:
+///
+/// ```ignore
+/// pub fn lower<X>(x: X) -> lower::HelperType<X> {
+///     ...
+/// }
+///
+/// pub(crate) mod lower {
+///     pub type HelperType<X> = ...;
+/// }
+/// ```
+///
+/// This turned out to be an issue for the support of the `auto_type` feature, which is why
+/// [`define_sql_function!`] was introduced (and why this is deprecated).
+///
+/// SQL functions declared with this version of the macro will not be usable with `#[auto_type]`
+/// or `Selectable` `select_expression` type inference.
+#[deprecated(since = "2.2.0", note = "Use [`define_sql_function`] instead")]
+#[proc_macro]
+#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
 pub fn sql_function_proc(input: TokenStream) -> TokenStream {
-    sql_function::expand(parse_macro_input!(input)).into()
+    sql_function::expand(parse_macro_input!(input), true).into()
 }
 
 /// This is an internal diesel macro that
@@ -1545,7 +1549,7 @@ pub fn table_proc(input: TokenStream) -> TokenStream {
     }
 }
 
-/// This derives implements [`diesel::Connection`] and related traits for an enum of
+/// This derives implements `diesel::Connection` and related traits for an enum of
 /// connections to different databases.
 ///
 /// By applying this derive to such an enum, you can use the enum as a connection type in
@@ -1554,12 +1558,12 @@ pub fn table_proc(input: TokenStream) -> TokenStream {
 /// `diesel::Connection` and a number of related traits. Connection types form Diesel itself
 /// as well as third party connection types are supported by this derive.
 ///
-/// The implementation of [`diesel::Connection::establish`] tries to establish
+/// The implementation of `diesel::Connection::establish` tries to establish
 /// a new connection with the given connection string in the order the connections
 /// are specified in the enum. If one connection fails, it tries the next one and so on.
 /// That means that as soon as more than one connection type accepts a certain connection
 /// string the first matching type in your enum will always establish the connection. This
-/// is especially important if one of the connection types is [`diesel::SqliteConnection`]
+/// is especially important if one of the connection types is `diesel::SqliteConnection`
 /// as this connection type accepts arbitrary paths. It should normally place as last entry
 /// in your enum. If you want control of which connection type is created, just construct the
 /// corresponding enum manually by first establishing the connection via the inner type and then
@@ -1770,6 +1774,20 @@ pub fn derive_multiconnection(input: TokenStream) -> TokenStream {
 /// #     Ok(())
 /// # }
 /// ```
+/// # Limitations
+///
+/// While this attribute tries to support as much of diesels built-in DSL as possible it's unfortunally not
+/// possible to support everything. Notable unsupported types are:
+///
+/// * Update statements
+/// * Insert from select statements
+/// * Select statements without from clause
+/// * Query constructed by `diesel::sql_query`
+/// * Expressions using `diesel::dsl::sql`
+///
+/// For this cases a manual type annotation is required. See the "Annotating Types" section below
+/// for details.
+///
 ///
 /// # Advanced usage
 ///
diff --git a/diesel_derives/src/sql_function.rs b/diesel_derives/src/sql_function.rs
index 9a9ed90fe59b..1f76d9d9adfd 100644
--- a/diesel_derives/src/sql_function.rs
+++ b/diesel_derives/src/sql_function.rs
@@ -8,7 +8,7 @@ use syn::{
     PathArguments, Token, Type,
 };
 
-pub(crate) fn expand(input: SqlFunctionDecl) -> TokenStream {
+pub(crate) fn expand(input: SqlFunctionDecl, legacy_helper_type_and_module: bool) -> TokenStream {
     let SqlFunctionDecl {
         mut attributes,
         fn_token,
@@ -94,6 +94,8 @@ pub(crate) fn expand(input: SqlFunctionDecl) -> TokenStream {
         numeric_derive = Some(quote!(#[derive(diesel::sql_types::DieselNumericOps)]));
     }
 
+    let helper_type_doc = format!("The return type of [`{fn_name}()`](super::fn_name)");
+
     let args_iter = args.iter();
     let mut tokens = quote! {
         use diesel::{self, QueryResult};
@@ -109,6 +111,7 @@ pub(crate) fn expand(input: SqlFunctionDecl) -> TokenStream {
             #(pub(in super) #type_args: ::std::marker::PhantomData<#type_args>,)*
         }
 
+        #[doc = #helper_type_doc]
         pub type HelperType #ty_generics = #fn_name <
             #(#type_args,)*
             #(<#arg_name as AsExpression<#arg_type>>::Expression,)*
@@ -391,23 +394,44 @@ pub(crate) fn expand(input: SqlFunctionDecl) -> TokenStream {
 
     let args_iter = args.iter();
 
+    let (outside_of_module_helper_type, return_type_path, internals_module_name) =
+        if legacy_helper_type_and_module {
+            (None, quote! { #fn_name::HelperType }, fn_name.clone())
+        } else {
+            let internals_module_name = Ident::new(&format!("{fn_name}_utils"), fn_name.span());
+            (
+                Some(quote! {
+                    #[allow(non_camel_case_types, non_snake_case)]
+                    #[doc = #helper_type_doc]
+                    pub type #fn_name #ty_generics = #internals_module_name::#fn_name <
+                        #(#type_args,)*
+                        #(<#arg_name as ::diesel::expression::AsExpression<#arg_type>>::Expression,)*
+                    >;
+                }),
+                quote! { #fn_name },
+                internals_module_name,
+            )
+        };
+
     quote! {
         #(#attributes)*
         #[allow(non_camel_case_types)]
         pub #fn_token #fn_name #impl_generics (#(#args_iter,)*)
-            -> #fn_name::HelperType #ty_generics
+            -> #return_type_path #ty_generics
         #where_clause
             #(#arg_name: ::diesel::expression::AsExpression<#arg_type>,)*
         {
-            #fn_name::#fn_name {
+            #internals_module_name::#fn_name {
                 #(#arg_struct_assign,)*
                 #(#type_args: ::std::marker::PhantomData,)*
             }
         }
 
+        #outside_of_module_helper_type
+
         #[doc(hidden)]
         #[allow(non_camel_case_types, non_snake_case, unused_imports)]
-        pub(crate) mod #fn_name {
+        pub(crate) mod #internals_module_name {
             #tokens
         }
     }
diff --git a/examples/postgres/composite_types/README.md b/examples/postgres/composite_types/README.md
index 52e6d3d1a8f1..8057440c477e 100644
--- a/examples/postgres/composite_types/README.md
+++ b/examples/postgres/composite_types/README.md
@@ -32,10 +32,10 @@ composite_type_db=# SELECT * FROM coordinates;
 The typical working flow when using Diesel is to automatically generate [schema.rs](./src/schema.rs) which
 provides us with the type information of the columns which are present in
 our coordinates table. Also, an SQL function, [distance_from_origin()](./migrations/2023-10-23-111951_composite2rust_coordinates/up.sql),
-is defined. We need to explain this to the Rust compiler using the [sql_function!](https://docs.rs/diesel/latest/diesel/expression/functions/macro.sql_function.html)
+is defined. We need to explain this to the Rust compiler using the [define_sql_function!](https://docs.rs/diesel/latest/diesel/expression/functions/macro.define_sql_function.html)
 macro like this:
 ```rust
-sql_function!(fn distance_from_origin(re: Integer,im: Integer) -> Float);
+define_sql_function!(fn distance_from_origin(re: Integer,im: Integer) -> Float);
 ```
 Keep in mind that we specify [only postgreSQL types](https://docs.rs/diesel/latest/diesel/sql_types/index.html)
 as the input parameters and return value(s) of this function. If the columns
@@ -48,7 +48,7 @@ let results: Vec<(i32, f32)> = coordinates
 ```
 So we expect a vector of a 2-tuple, or ordered pair of the Rust types ```i32```
 and ```f32```. Mind that the float type is not present in the table but is
-specified in by the SQL function in the database and also in the macro `sql_function!`
+specified in by the SQL function in the database and also in the macro `define_sql_function!`
 definition above. Of course we can expand this to very long tuples, but that
 will become error prone as we have to specify the sequence of type correctly
 every function call. Try out the [first example](./examples/composite2rust_coordinates) with:
@@ -126,8 +126,8 @@ cargo run --example composite2rust_colors
 On the Rust side, we define the interpretation of both functions differently, the first one using a tuple similar to the coordinates example, the second one using a _locally_ defined Rust type for interpreting a tuple: notice the **Pg**-prefix of `PgGrayType`.
 
 ```rust
-sql_function!(fn color2grey(r: Integer, g: Integer,b: Integer) -> Record<(Float,Text)>);
-sql_function!(fn color2gray(r: Integer, g: Integer,b: Integer) -> PgGrayType);
+define_sql_function!(fn color2grey(r: Integer, g: Integer,b: Integer) -> Record<(Float,Text)>);
+define_sql_function!(fn color2gray(r: Integer, g: Integer,b: Integer) -> PgGrayType);
 ```
 As this only creates a type with anonymous fields, which can be addressed by their field number **object.0**, **object.1** etc., it would be more convenient to attach names to the fields. Therefore we need to define a type with our intended field names, which we can use _globally_ (or at least outside the database related code space):
 ```rust
diff --git a/examples/postgres/composite_types/examples/composite2rust_colors.rs b/examples/postgres/composite_types/examples/composite2rust_colors.rs
index 1c87b368966b..9be65ec7522e 100644
--- a/examples/postgres/composite_types/examples/composite2rust_colors.rs
+++ b/examples/postgres/composite_types/examples/composite2rust_colors.rs
@@ -7,12 +7,12 @@ use diesel_postgres_composite_type::schema::colors::{
 };
 
 // Define the signature of the SQL function we want to call:
+use diesel::define_sql_function;
 use diesel::pg::Pg;
 use diesel::pg::PgValue;
-use diesel::sql_function;
 use diesel::sql_types::{Float, Integer, Record, Text};
-sql_function!(fn color2grey(r: Integer, g: Integer,b: Integer) -> Record<(Float,Text)>);
-sql_function!(fn color2gray(r: Integer, g: Integer,b: Integer) -> PgGrayType);
+define_sql_function!(fn color2grey(r: Integer, g: Integer,b: Integer) -> Record<(Float,Text)>);
+define_sql_function!(fn color2gray(r: Integer, g: Integer,b: Integer) -> PgGrayType);
 
 // Needed to select, construct the query and submit it.
 use diesel::deserialize::{self, FromSql, FromSqlRow};
@@ -66,7 +66,7 @@ fn main() {
         );
     }
     // Experiment 3: Similar, using the type also in the above listed
-    // sql_function!(...) definition.
+    // define_sql_function!(...) definition.
     let results: Vec<(i32, GrayType)> = colors
         .select((color_id, color2gray(red, green, blue)))
         .load(connection)
diff --git a/examples/postgres/composite_types/examples/composite2rust_coordinates.rs b/examples/postgres/composite_types/examples/composite2rust_coordinates.rs
index a785bdd6ed29..18e5ebc7a014 100644
--- a/examples/postgres/composite_types/examples/composite2rust_coordinates.rs
+++ b/examples/postgres/composite_types/examples/composite2rust_coordinates.rs
@@ -7,11 +7,11 @@ use diesel_postgres_composite_type::schema::coordinates::{
 };
 
 // Define the signature of the SQL function we want to call:
-use diesel::sql_function;
+use diesel::define_sql_function;
 use diesel::sql_types::Integer;
-sql_function!(fn distance_from_origin(re: Integer,im: Integer) -> Float);
-sql_function!(fn shortest_distance() -> Record<(Integer,Float)>);
-sql_function!(fn longest_distance() -> Record<(Integer,Float)>);
+define_sql_function!(fn distance_from_origin(re: Integer,im: Integer) -> Float);
+define_sql_function!(fn shortest_distance() -> Record<(Integer,Float)>);
+define_sql_function!(fn longest_distance() -> Record<(Integer,Float)>);
 
 // Needed to select, construct the query and submit it.
 use diesel::select;
diff --git a/guide_drafts/migration_guide.md b/guide_drafts/migration_guide.md
index 591c0354777d..2134ac41faa4 100644
--- a/guide_drafts/migration_guide.md
+++ b/guide_drafts/migration_guide.md
@@ -32,7 +32,7 @@ by the following changes:
 * [Changed `FromSql` implementations](#2-0-0-from-sql)
 
 `no_arg_sql_function!` macro is now pending deprecation.
-Users of the macro are advised to consider `sql_function!` macro.
+Users of the macro are advised to consider `define_sql_function!` macro.
 
 * [Deprecated usage of `no_arg_sql_function!` macro](#2-0-0-no_arg_sql_function)
 
@@ -188,9 +188,9 @@ impl<DB: Backend> FromSql<YourSqlType, DB> for YourType {
 
 ## `no_arg_sql_function`<a name="2-0-0-no_arg_sql_function"></a>
 
-The `no_arg_sql_function` was deprecated without direct replacement. At the same time the
-`sql_function!` macro gained support for sql functions without argument. This support generates slightly 
-different code. Instead of representing the sql function as zero sized struct, `sql_function!` will generate an ordinary function call without arguments. This requires changing any usage of the generated dsl. This change 
+The `no_arg_sql_function` was deprecated without direct replacement. However the
+`define_sql_function!` macro gained support for sql functions without argument. This support generates slightly 
+different code. Instead of representing the sql function as zero sized struct, `define_sql_function!` will generate an ordinary function call without arguments. This requires changing any usage of the generated dsl. This change 
 affects all of the usages of the `no_arg_sql_function!` in third party crates.
 
 ```diff
@@ -198,7 +198,7 @@ affects all of the usages of the `no_arg_sql_function!` in third party crates.
 - 
 - diesel::select(now)
  
-+ sql_function!{
++ define_sql_function!{
 +     /// Represents the SQL NOW() function
 +     fn now() -> sql_types::Timestamp;
 + }


Test patch:
diff --git a/diesel_compile_tests/tests/fail/aggregate_expression_requires_column_from_same_table.stderr b/diesel_compile_tests/tests/fail/aggregate_expression_requires_column_from_same_table.stderr
index 1d0a7f863b7e..5a4d9ceff83c 100644
--- a/diesel_compile_tests/tests/fail/aggregate_expression_requires_column_from_same_table.stderr
+++ b/diesel_compile_tests/tests/fail/aggregate_expression_requires_column_from_same_table.stderr
@@ -13,8 +13,8 @@ error[E0277]: Cannot select `posts::columns::id` from `users::table`
              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
              <posts::columns::id as SelectableExpression<Only<posts::table>>>
              <posts::columns::id as SelectableExpression<Tablesample<posts::table, TSM>>>
-   = note: required for `diesel::expression::functions::aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_folding::sum_utils::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::sum_utils::sum<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0271]: type mismatch resolving `<table as AppearsInFromClause<table>>::Count == Once`
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:20:31
@@ -28,9 +28,9 @@ note: required for `posts::columns::id` to implement `AppearsOnTable<users::tabl
 14 |         id -> Integer,
    |         ^^
    = note: 1 redundant requirement hidden
-   = note: required for `diesel::expression::functions::aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
-   = note: required for `diesel::expression::functions::aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_folding::sum_utils::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::functions::aggregate_folding::sum_utils::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::sum_utils::sum<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0277]: the trait bound `users::table: TableNotEqual<posts::table>` is not satisfied
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:20:31
@@ -55,9 +55,9 @@ note: required for `posts::columns::id` to implement `AppearsOnTable<users::tabl
 14 |         id -> Integer,
    |         ^^
    = note: 1 redundant requirement hidden
-   = note: required for `diesel::expression::functions::aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
-   = note: required for `diesel::expression::functions::aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::sum::sum<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_folding::sum_utils::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::functions::aggregate_folding::sum_utils::sum<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::sum_utils::sum<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0277]: Cannot select `posts::columns::id` from `users::table`
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:21:31
@@ -74,8 +74,8 @@ error[E0277]: Cannot select `posts::columns::id` from `users::table`
              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
              <posts::columns::id as SelectableExpression<Only<posts::table>>>
              <posts::columns::id as SelectableExpression<Tablesample<posts::table, TSM>>>
-   = note: required for `diesel::expression::functions::aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_folding::avg_utils::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::avg_utils::avg<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0271]: type mismatch resolving `<table as AppearsInFromClause<table>>::Count == Once`
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:21:31
@@ -89,9 +89,9 @@ note: required for `posts::columns::id` to implement `AppearsOnTable<users::tabl
 14 |         id -> Integer,
    |         ^^
    = note: 1 redundant requirement hidden
-   = note: required for `diesel::expression::functions::aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
-   = note: required for `diesel::expression::functions::aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_folding::avg_utils::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::functions::aggregate_folding::avg_utils::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::avg_utils::avg<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0277]: the trait bound `users::table: TableNotEqual<posts::table>` is not satisfied
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:21:31
@@ -116,9 +116,9 @@ note: required for `posts::columns::id` to implement `AppearsOnTable<users::tabl
 14 |         id -> Integer,
    |         ^^
    = note: 1 redundant requirement hidden
-   = note: required for `diesel::expression::functions::aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
-   = note: required for `diesel::expression::functions::aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::avg::avg<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_folding::avg_utils::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::functions::aggregate_folding::avg_utils::avg<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_folding::avg_utils::avg<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0277]: Cannot select `posts::columns::id` from `users::table`
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:22:31
@@ -135,8 +135,8 @@ error[E0277]: Cannot select `posts::columns::id` from `users::table`
              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
              <posts::columns::id as SelectableExpression<Only<posts::table>>>
              <posts::columns::id as SelectableExpression<Tablesample<posts::table, TSM>>>
-   = note: required for `diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0271]: type mismatch resolving `<table as AppearsInFromClause<table>>::Count == Once`
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:22:31
@@ -150,9 +150,9 @@ note: required for `posts::columns::id` to implement `AppearsOnTable<users::tabl
 14 |         id -> Integer,
    |         ^^
    = note: 1 redundant requirement hidden
-   = note: required for `diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
-   = note: required for `diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0277]: the trait bound `users::table: TableNotEqual<posts::table>` is not satisfied
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:22:31
@@ -177,9 +177,9 @@ note: required for `posts::columns::id` to implement `AppearsOnTable<users::tabl
 14 |         id -> Integer,
    |         ^^
    = note: 1 redundant requirement hidden
-   = note: required for `diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
-   = note: required for `diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0277]: Cannot select `posts::columns::id` from `users::table`
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:23:31
@@ -196,8 +196,8 @@ error[E0277]: Cannot select `posts::columns::id` from `users::table`
              <posts::columns::id as SelectableExpression<JoinOn<Join, On>>>
              <posts::columns::id as SelectableExpression<Only<posts::table>>>
              <posts::columns::id as SelectableExpression<Tablesample<posts::table, TSM>>>
-   = note: required for `diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0271]: type mismatch resolving `<table as AppearsInFromClause<table>>::Count == Once`
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:23:31
@@ -211,9 +211,9 @@ note: required for `posts::columns::id` to implement `AppearsOnTable<users::tabl
 14 |         id -> Integer,
    |         ^^
    = note: 1 redundant requirement hidden
-   = note: required for `diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
-   = note: required for `diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Integer, posts::columns::id>>`
 
 error[E0277]: the trait bound `users::table: TableNotEqual<posts::table>` is not satisfied
   --> tests/fail/aggregate_expression_requires_column_from_same_table.rs:23:31
@@ -238,6 +238,6 @@ note: required for `posts::columns::id` to implement `AppearsOnTable<users::tabl
 14 |         id -> Integer,
    |         ^^
    = note: 1 redundant requirement hidden
-   = note: required for `diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
-   = note: required for `diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Integer, posts::columns::id>>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Integer, posts::columns::id>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Integer, posts::columns::id>` to implement `SelectableExpression<users::table>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Integer, posts::columns::id>>`
diff --git a/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs b/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs
index d44fe0069691..60f7ac63fd9b 100644
--- a/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs
+++ b/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs
@@ -11,7 +11,7 @@ table! {
     }
 }
 
-sql_function!(fn f(x: Nullable<Integer>, y: Nullable<Integer>) -> Nullable<Integer>);
+define_sql_function!(fn f(x: Nullable<Integer>, y: Nullable<Integer>) -> Nullable<Integer>);
 
 fn main() {
     use self::users::dsl::*;
diff --git a/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.stderr b/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.stderr
index dc7b430a10bd..d2851e2b80a2 100644
--- a/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.stderr
+++ b/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.stderr
@@ -20,7 +20,7 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg
              <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::No>>
              <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::Never>>
    = note: required for `Add<nullable_int_col, max<Nullable<Integer>, nullable_int_col>>` to implement `ValidGrouping<()>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::ops::numeric::Add<columns::nullable_int_col, diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Nullable<diesel::sql_types::Integer>, columns::nullable_int_col>>>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<diesel::expression::ops::numeric::Add<columns::nullable_int_col, diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Nullable<diesel::sql_types::Integer>, columns::nullable_int_col>>>`
 
 error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggregates<diesel::expression::is_aggregate::Yes>` is not satisfied
   --> tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs:24:24
@@ -34,9 +34,9 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg
 note: required for `__Derived<nullable_int_col, max<Nullable<Integer>, nullable_int_col>>` to implement `ValidGrouping<()>`
   --> tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs:14:1
    |
-14 | sql_function!(fn f(x: Nullable<Integer>, y: Nullable<Integer>) -> Nullable<Integer>);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
+14 | define_sql_function!(fn f(x: Nullable<Integer>, y: Nullable<Integer>) -> Nullable<Integer>);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
    = note: 1 redundant requirement hidden
    = note: required for `f<nullable_int_col, max<Nullable<Integer>, nullable_int_col>>` to implement `ValidGrouping<()>`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<f::f<columns::nullable_int_col, diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Nullable<diesel::sql_types::Integer>, columns::nullable_int_col>>>`
-   = note: this error originates in the macro `sql_function` which comes from the expansion of the derive macro `ValidGrouping` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `SelectDsl<f_utils::f<columns::nullable_int_col, diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Nullable<diesel::sql_types::Integer>, columns::nullable_int_col>>>`
+   = note: this error originates in the macro `define_sql_function` which comes from the expansion of the derive macro `ValidGrouping` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/diesel_compile_tests/tests/fail/cannot_pass_aggregate_to_where.stderr b/diesel_compile_tests/tests/fail/cannot_pass_aggregate_to_where.stderr
index a239411f82b3..a40da00906b3 100644
--- a/diesel_compile_tests/tests/fail/cannot_pass_aggregate_to_where.stderr
+++ b/diesel_compile_tests/tests/fail/cannot_pass_aggregate_to_where.stderr
@@ -8,4 +8,4 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::Yes: MixedAggre
              <diesel::expression::is_aggregate::Yes as MixedAggregates<diesel::expression::is_aggregate::Yes>>
              <diesel::expression::is_aggregate::Yes as MixedAggregates<diesel::expression::is_aggregate::Never>>
    = note: required for `Grouped<Gt<count<Integer, id>, Bound<BigInt, i64>>>` to implement `NonAggregate`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `FilterDsl<diesel::expression::grouped::Grouped<diesel::expression::operators::Gt<diesel::expression::count::count::count<diesel::sql_types::Integer, columns::id>, diesel::expression::bound::Bound<BigInt, i64>>>>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `FilterDsl<diesel::expression::grouped::Grouped<diesel::expression::operators::Gt<diesel::expression::count::count_utils::count<diesel::sql_types::Integer, columns::id>, diesel::expression::bound::Bound<BigInt, i64>>>>`
diff --git a/diesel_compile_tests/tests/fail/custom_returning_requires_nonaggregate.stderr b/diesel_compile_tests/tests/fail/custom_returning_requires_nonaggregate.stderr
index 11d060ce99f8..28a3c85e0224 100644
--- a/diesel_compile_tests/tests/fail/custom_returning_requires_nonaggregate.stderr
+++ b/diesel_compile_tests/tests/fail/custom_returning_requires_nonaggregate.stderr
@@ -30,8 +30,8 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg
    = help: the following other types implement trait `MixedAggregates<Other>`:
              <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::No>>
              <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::Never>>
-   = note: required for `(columns::name, diesel::expression::count::count::count<diesel::sql_types::Text, columns::name>)` to implement `ValidGrouping<()>`
-   = note: required for `(columns::name, diesel::expression::count::count::count<diesel::sql_types::Text, columns::name>)` to implement `NonAggregate`
+   = note: required for `(columns::name, diesel::expression::count::count_utils::count<diesel::sql_types::Text, columns::name>)` to implement `ValidGrouping<()>`
+   = note: required for `(columns::name, diesel::expression::count::count_utils::count<diesel::sql_types::Text, columns::name>)` to implement `NonAggregate`
    = note: required for `InsertStatement<table, ValuesClause<(DefaultableColumnInsertValue<...>,), ...>, ..., ...>` to implement `Query`
 note: required by a bound in `InsertStatement::<T, U, Op>::returning`
   --> $DIESEL/src/query_builder/insert_statement/mod.rs
diff --git a/diesel_compile_tests/tests/fail/filter_requires_bool_nonaggregate_expression.stderr b/diesel_compile_tests/tests/fail/filter_requires_bool_nonaggregate_expression.stderr
index 9d994f20ddfb..9eea82b6da9b 100644
--- a/diesel_compile_tests/tests/fail/filter_requires_bool_nonaggregate_expression.stderr
+++ b/diesel_compile_tests/tests/fail/filter_requires_bool_nonaggregate_expression.stderr
@@ -31,4 +31,4 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::Yes: MixedAggre
              <diesel::expression::is_aggregate::Yes as MixedAggregates<diesel::expression::is_aggregate::Yes>>
              <diesel::expression::is_aggregate::Yes as MixedAggregates<diesel::expression::is_aggregate::Never>>
    = note: required for `Grouped<Eq<sum<Integer, id>, Bound<Nullable<BigInt>, i64>>>` to implement `NonAggregate`
-   = note: required for `SelectStatement<FromClause<users::table>>` to implement `FilterDsl<diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<diesel::expression::functions::aggregate_folding::sum::sum<diesel::sql_types::Integer, columns::id>, diesel::expression::bound::Bound<Nullable<BigInt>, i64>>>>`
+   = note: required for `SelectStatement<FromClause<users::table>>` to implement `FilterDsl<diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<diesel::expression::functions::aggregate_folding::sum_utils::sum<diesel::sql_types::Integer, columns::id>, diesel::expression::bound::Bound<Nullable<BigInt>, i64>>>>`
diff --git a/diesel_compile_tests/tests/fail/ordering_functions_require_ord.stderr b/diesel_compile_tests/tests/fail/ordering_functions_require_ord.stderr
index a99306aa0e7f..1739baf8ff3f 100644
--- a/diesel_compile_tests/tests/fail/ordering_functions_require_ord.stderr
+++ b/diesel_compile_tests/tests/fail/ordering_functions_require_ord.stderr
@@ -38,8 +38,8 @@ error[E0277]: the trait bound `diesel::sql_types::Bool: SqlOrd` is not satisfied
              Interval
            and $N others
    = note: required for `diesel::sql_types::Bool` to implement `diesel::expression::functions::aggregate_ordering::private::SqlOrdAggregate`
-   = note: required for `diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Bool, columns::b>` to implement `diesel::Expression`
-   = note: required for `stuff::table` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::max::max<diesel::sql_types::Bool, columns::b>>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Bool, columns::b>` to implement `diesel::Expression`
+   = note: required for `stuff::table` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::max_utils::max<diesel::sql_types::Bool, columns::b>>`
 
 error[E0277]: the trait bound `diesel::sql_types::Bool: SqlOrd` is not satisfied
   --> tests/fail/ordering_functions_require_ord.rs:14:38
@@ -81,5 +81,5 @@ error[E0277]: the trait bound `diesel::sql_types::Bool: SqlOrd` is not satisfied
              Interval
            and $N others
    = note: required for `diesel::sql_types::Bool` to implement `diesel::expression::functions::aggregate_ordering::private::SqlOrdAggregate`
-   = note: required for `diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Bool, columns::b>` to implement `diesel::Expression`
-   = note: required for `stuff::table` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::min::min<diesel::sql_types::Bool, columns::b>>`
+   = note: required for `diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Bool, columns::b>` to implement `diesel::Expression`
+   = note: required for `stuff::table` to implement `SelectDsl<diesel::expression::functions::aggregate_ordering::min_utils::min<diesel::sql_types::Bool, columns::b>>`
diff --git a/diesel_compile_tests/tests/fail/pg_on_conflict_requires_valid_conflict_target.rs b/diesel_compile_tests/tests/fail/pg_on_conflict_requires_valid_conflict_target.rs
index 6f4816a2695b..ae7635184543 100644
--- a/diesel_compile_tests/tests/fail/pg_on_conflict_requires_valid_conflict_target.rs
+++ b/diesel_compile_tests/tests/fail/pg_on_conflict_requires_valid_conflict_target.rs
@@ -20,7 +20,7 @@ table! {
 #[diesel(table_name = users)]
 pub struct NewUser(#[diesel(column_name = name)] &'static str);
 
-sql_function!(fn lower(x: diesel::sql_types::Text) -> diesel::sql_types::Text);
+define_sql_function!(fn lower(x: diesel::sql_types::Text) -> diesel::sql_types::Text);
 
 fn main() {
     use self::users::dsl::*;
diff --git a/diesel_compile_tests/tests/fail/pg_on_conflict_requires_valid_conflict_target.stderr b/diesel_compile_tests/tests/fail/pg_on_conflict_requires_valid_conflict_target.stderr
index 154aa6f65b76..5f69a0a33324 100644
--- a/diesel_compile_tests/tests/fail/pg_on_conflict_requires_valid_conflict_target.stderr
+++ b/diesel_compile_tests/tests/fail/pg_on_conflict_requires_valid_conflict_target.stderr
@@ -42,11 +42,11 @@ note: required by a bound in `upsert::on_conflict_extension::<impl InsertStateme
    |                                 ^^^^^^^^^^^^^^^^^^^ required by this bound in `upsert::on_conflict_extension::<impl InsertStatement<T, U, Op, Ret>>::on_conflict`
    = note: this error originates in the macro `table` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0277]: the trait bound `lower::lower<posts::columns::title>: Column` is not satisfied
+error[E0277]: the trait bound `lower_utils::lower<posts::columns::title>: Column` is not satisfied
   --> tests/fail/pg_on_conflict_requires_valid_conflict_target.rs:42:22
    |
 42 |         .on_conflict(lower(posts::title));
-   |          ----------- ^^^^^^^^^^^^^^^^^^^ the trait `Column` is not implemented for `lower::lower<posts::columns::title>`
+   |          ----------- ^^^^^^^^^^^^^^^^^^^ the trait `Column` is not implemented for `lower_utils::lower<posts::columns::title>`
    |          |
    |          required by a bound introduced by this call
    |
@@ -60,7 +60,7 @@ error[E0277]: the trait bound `lower::lower<posts::columns::title>: Column` is n
              pg::metadata_lookup::pg_type::columns::typnamespace
              pg::metadata_lookup::pg_type::columns::typarray
            and $N others
-   = note: required for `diesel::query_builder::upsert::on_conflict_target::ConflictTarget<lower::lower<posts::columns::title>>` to implement `diesel::query_builder::upsert::on_conflict_target::OnConflictTarget<users::table>`
+   = note: required for `diesel::query_builder::upsert::on_conflict_target::ConflictTarget<lower_utils::lower<posts::columns::title>>` to implement `diesel::query_builder::upsert::on_conflict_target::OnConflictTarget<users::table>`
 note: required by a bound in `upsert::on_conflict_extension::<impl InsertStatement<T, U, Op, Ret>>::on_conflict`
   --> $DIESEL/src/upsert/on_conflict_extension.rs
    |
diff --git a/diesel_compile_tests/tests/fail/pg_specific_binary_expressions_only_usable_with_pg.stderr b/diesel_compile_tests/tests/fail/pg_specific_binary_expressions_only_usable_with_pg.stderr
index 0feaa5ff3a08..5732b585944d 100644
--- a/diesel_compile_tests/tests/fail/pg_specific_binary_expressions_only_usable_with_pg.stderr
+++ b/diesel_compile_tests/tests/fail/pg_specific_binary_expressions_only_usable_with_pg.stderr
@@ -1,16 +1,16 @@
-error[E0277]: the trait bound `diesel::pg::expression::operators::ConcatBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>: QueryFragment<Sqlite>` is not satisfied
+error[E0277]: the trait bound `Sqlite: diesel::expression::operators::LikeIsAllowedForType<diesel::sql_types::Binary>` is not satisfied
   --> tests/fail/pg_specific_binary_expressions_only_usable_with_pg.rs:21:32
    |
 21 |         .get_result::<Vec<u8>>(&mut connection).unwrap();
-   |          ----------            ^^^^^^^^^^^^^^^ the trait `QueryFragment<Sqlite>` is not implemented for `diesel::pg::expression::operators::ConcatBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>`
+   |          ----------            ^^^^^^^^^^^^^^^ the trait `diesel::expression::operators::LikeIsAllowedForType<diesel::sql_types::Binary>` is not implemented for `Sqlite`
    |          |
    |          required by a bound introduced by this call
    |
-   = help: the trait `QueryFragment<Pg>` is implemented for `diesel::pg::expression::operators::ConcatBinary<T, U>`
-   = note: required for `Grouped<ConcatBinary<name, Bound<Binary, Vec<u8>>>>` to implement `QueryFragment<Sqlite>`
-   = note: 3 redundant requirements hidden
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `QueryFragment<Sqlite>`
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `LoadQuery<'_, diesel::SqliteConnection, Vec<u8>>`
+   = help: the trait `diesel::expression::operators::LikeIsAllowedForType<diesel::sql_types::Binary>` is implemented for `Pg`
+   = note: required for `diesel::expression::operators::Like<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>` to implement `QueryFragment<Sqlite>`
+   = note: 6 redundant requirements hidden
+   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<Concat<name, Bound<Binary, Vec<u8>>>>>, ..., ...>` to implement `QueryFragment<Sqlite>`
+   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<Concat<name, Bound<Binary, Vec<u8>>>>>, ..., ...>` to implement `LoadQuery<'_, diesel::SqliteConnection, Vec<u8>>`
 note: required by a bound in `get_result`
   --> $DIESEL/src/query_dsl/mod.rs
    |
@@ -20,107 +20,19 @@ note: required by a bound in `get_result`
    |         Self: LoadQuery<'query, Conn, U>,
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::get_result`
 
-error[E0277]: the trait bound `diesel::pg::expression::operators::LikeBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>: QueryFragment<Sqlite>` is not satisfied
-  --> tests/fail/pg_specific_binary_expressions_only_usable_with_pg.rs:21:32
-   |
-21 |         .get_result::<Vec<u8>>(&mut connection).unwrap();
-   |          ----------            ^^^^^^^^^^^^^^^ the trait `QueryFragment<Sqlite>` is not implemented for `diesel::pg::expression::operators::LikeBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>`
-   |          |
-   |          required by a bound introduced by this call
-   |
-   = help: the trait `QueryFragment<Pg>` is implemented for `diesel::pg::expression::operators::LikeBinary<T, U>`
-   = note: required for `Grouped<LikeBinary<name, Bound<Binary, Vec<u8>>>>` to implement `QueryFragment<Sqlite>`
-   = note: 5 redundant requirements hidden
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `QueryFragment<Sqlite>`
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `LoadQuery<'_, diesel::SqliteConnection, Vec<u8>>`
-note: required by a bound in `get_result`
-  --> $DIESEL/src/query_dsl/mod.rs
-   |
-   |     fn get_result<'query, U>(self, conn: &mut Conn) -> QueryResult<U>
-   |        ---------- required by a bound in this associated function
-   |     where
-   |         Self: LoadQuery<'query, Conn, U>,
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::get_result`
-
-error[E0277]: the trait bound `diesel::pg::expression::operators::NotLikeBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>: QueryFragment<Sqlite>` is not satisfied
-  --> tests/fail/pg_specific_binary_expressions_only_usable_with_pg.rs:21:32
-   |
-21 |         .get_result::<Vec<u8>>(&mut connection).unwrap();
-   |          ----------            ^^^^^^^^^^^^^^^ the trait `QueryFragment<Sqlite>` is not implemented for `diesel::pg::expression::operators::NotLikeBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>`
-   |          |
-   |          required by a bound introduced by this call
-   |
-   = help: the trait `QueryFragment<Pg>` is implemented for `diesel::pg::expression::operators::NotLikeBinary<T, U>`
-   = note: required for `Grouped<NotLikeBinary<name, Bound<Binary, Vec<u8>>>>` to implement `QueryFragment<Sqlite>`
-   = note: 5 redundant requirements hidden
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `QueryFragment<Sqlite>`
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `LoadQuery<'_, diesel::SqliteConnection, Vec<u8>>`
-note: required by a bound in `get_result`
-  --> $DIESEL/src/query_dsl/mod.rs
-   |
-   |     fn get_result<'query, U>(self, conn: &mut Conn) -> QueryResult<U>
-   |        ---------- required by a bound in this associated function
-   |     where
-   |         Self: LoadQuery<'query, Conn, U>,
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::get_result`
-
-error[E0277]: the trait bound `diesel::pg::expression::operators::ConcatBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>: QueryFragment<Mysql>` is not satisfied
-  --> tests/fail/pg_specific_binary_expressions_only_usable_with_pg.rs:29:32
-   |
-29 |         .get_result::<Vec<u8>>(&mut connection).unwrap();
-   |          ----------            ^^^^^^^^^^^^^^^ the trait `QueryFragment<Mysql>` is not implemented for `diesel::pg::expression::operators::ConcatBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>`
-   |          |
-   |          required by a bound introduced by this call
-   |
-   = help: the trait `QueryFragment<Pg>` is implemented for `diesel::pg::expression::operators::ConcatBinary<T, U>`
-   = note: required for `Grouped<ConcatBinary<name, Bound<Binary, Vec<u8>>>>` to implement `QueryFragment<Mysql>`
-   = note: 3 redundant requirements hidden
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `QueryFragment<Mysql>`
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `LoadQuery<'_, diesel::MysqlConnection, Vec<u8>>`
-note: required by a bound in `get_result`
-  --> $DIESEL/src/query_dsl/mod.rs
-   |
-   |     fn get_result<'query, U>(self, conn: &mut Conn) -> QueryResult<U>
-   |        ---------- required by a bound in this associated function
-   |     where
-   |         Self: LoadQuery<'query, Conn, U>,
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::get_result`
-
-error[E0277]: the trait bound `diesel::pg::expression::operators::LikeBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>: QueryFragment<Mysql>` is not satisfied
-  --> tests/fail/pg_specific_binary_expressions_only_usable_with_pg.rs:29:32
-   |
-29 |         .get_result::<Vec<u8>>(&mut connection).unwrap();
-   |          ----------            ^^^^^^^^^^^^^^^ the trait `QueryFragment<Mysql>` is not implemented for `diesel::pg::expression::operators::LikeBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>`
-   |          |
-   |          required by a bound introduced by this call
-   |
-   = help: the trait `QueryFragment<Pg>` is implemented for `diesel::pg::expression::operators::LikeBinary<T, U>`
-   = note: required for `Grouped<LikeBinary<name, Bound<Binary, Vec<u8>>>>` to implement `QueryFragment<Mysql>`
-   = note: 5 redundant requirements hidden
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `QueryFragment<Mysql>`
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `LoadQuery<'_, diesel::MysqlConnection, Vec<u8>>`
-note: required by a bound in `get_result`
-  --> $DIESEL/src/query_dsl/mod.rs
-   |
-   |     fn get_result<'query, U>(self, conn: &mut Conn) -> QueryResult<U>
-   |        ---------- required by a bound in this associated function
-   |     where
-   |         Self: LoadQuery<'query, Conn, U>,
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::get_result`
-
-error[E0277]: the trait bound `diesel::pg::expression::operators::NotLikeBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>: QueryFragment<Mysql>` is not satisfied
+error[E0277]: the trait bound `Mysql: diesel::expression::operators::LikeIsAllowedForType<diesel::sql_types::Binary>` is not satisfied
   --> tests/fail/pg_specific_binary_expressions_only_usable_with_pg.rs:29:32
    |
 29 |         .get_result::<Vec<u8>>(&mut connection).unwrap();
-   |          ----------            ^^^^^^^^^^^^^^^ the trait `QueryFragment<Mysql>` is not implemented for `diesel::pg::expression::operators::NotLikeBinary<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>`
+   |          ----------            ^^^^^^^^^^^^^^^ the trait `diesel::expression::operators::LikeIsAllowedForType<diesel::sql_types::Binary>` is not implemented for `Mysql`
    |          |
    |          required by a bound introduced by this call
    |
-   = help: the trait `QueryFragment<Pg>` is implemented for `diesel::pg::expression::operators::NotLikeBinary<T, U>`
-   = note: required for `Grouped<NotLikeBinary<name, Bound<Binary, Vec<u8>>>>` to implement `QueryFragment<Mysql>`
-   = note: 5 redundant requirements hidden
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `QueryFragment<Mysql>`
-   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<ConcatBinary<name, Bound<Binary, ...>>>>, ..., ...>` to implement `LoadQuery<'_, diesel::MysqlConnection, Vec<u8>>`
+   = help: the trait `diesel::expression::operators::LikeIsAllowedForType<diesel::sql_types::Binary>` is implemented for `Pg`
+   = note: required for `diesel::expression::operators::Like<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Binary, Vec<u8>>>` to implement `QueryFragment<Mysql>`
+   = note: 6 redundant requirements hidden
+   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<Concat<name, Bound<Binary, Vec<u8>>>>>, ..., ...>` to implement `QueryFragment<Mysql>`
+   = note: required for `SelectStatement<FromClause<table>, SelectClause<Grouped<Concat<name, Bound<Binary, Vec<u8>>>>>, ..., ...>` to implement `LoadQuery<'_, diesel::MysqlConnection, Vec<u8>>`
 note: required by a bound in `get_result`
   --> $DIESEL/src/query_dsl/mod.rs
    |
diff --git a/diesel_compile_tests/tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs b/diesel_compile_tests/tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs
index 1d7945e845f9..b1f107af0597 100644
--- a/diesel_compile_tests/tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs
+++ b/diesel_compile_tests/tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs
@@ -12,7 +12,7 @@ table! {
     }
 }
 
-sql_function!(fn lower(x: VarChar) -> VarChar);
+define_sql_function!(fn lower(x: VarChar) -> VarChar);
 
 #[derive(Insertable)]
 #[diesel(table_name = users)]
@@ -23,13 +23,20 @@ fn main() {
     use self::users::dsl::*;
     let mut connection = SqliteConnection::establish(":memory:").unwrap();
 
-    users.select(id).filter(name.eq(any(Vec::<String>::new())))
+    users
+        .select(id)
+        .filter(name.eq(any(Vec::<String>::new())))
         .load::<i32>(&mut connection);
-    users.select(id).filter(name.is_not_distinct_from("Sean"))
+    users
+        .select(id)
+        .filter(name.is_not_distinct_from("Sean"))
         .load::<i32>(&mut connection);
-    users.select(id).filter(now.eq(now.at_time_zone("UTC")))
+    users
+        .select(id)
+        .filter(now.eq(now.at_time_zone("UTC")))
         .load::<i32>(&mut connection);
-    insert_into(users).values(&NewUser("Sean"))
+    insert_into(users)
+        .values(&NewUser("Sean"))
         .on_conflict(on_constraint("name"))
         .execute(&mut connection);
 }
diff --git a/diesel_compile_tests/tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.stderr b/diesel_compile_tests/tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.stderr
index 67d4a81ca7e6..39d3375ab318 100644
--- a/diesel_compile_tests/tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.stderr
+++ b/diesel_compile_tests/tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.stderr
@@ -1,15 +1,15 @@
 warning: use of deprecated function `diesel::dsl::any`: Use `ExpressionMethods::eq_any` instead
-  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:26:37
+  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:28:25
    |
-26 |     users.select(id).filter(name.eq(any(Vec::<String>::new())))
-   |                                     ^^^
+28 |         .filter(name.eq(any(Vec::<String>::new())))
+   |                         ^^^
    |
    = note: `#[warn(deprecated)]` on by default
 
 error[E0277]: the trait bound `diesel::pg::expression::array_comparison::Any<diesel::expression::bound::Bound<Array<diesel::sql_types::Text>, Vec<std::string::String>>>: QueryFragment<Sqlite>` is not satisfied
-  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:27:22
+  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:29:22
    |
-27 |         .load::<i32>(&mut connection);
+29 |         .load::<i32>(&mut connection);
    |          ----        ^^^^^^^^^^^^^^^ the trait `QueryFragment<Sqlite>` is not implemented for `diesel::pg::expression::array_comparison::Any<diesel::expression::bound::Bound<Array<diesel::sql_types::Text>, Vec<std::string::String>>>`
    |          |
    |          required by a bound introduced by this call
@@ -29,9 +29,9 @@ note: required by a bound in `diesel::RunQueryDsl::load`
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::load`
 
 error[E0277]: the trait bound `diesel::pg::expression::operators::IsNotDistinctFrom<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>: QueryFragment<Sqlite>` is not satisfied
-  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:29:22
+  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:33:22
    |
-29 |         .load::<i32>(&mut connection);
+33 |         .load::<i32>(&mut connection);
    |          ----        ^^^^^^^^^^^^^^^ the trait `QueryFragment<Sqlite>` is not implemented for `diesel::pg::expression::operators::IsNotDistinctFrom<columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>`
    |          |
    |          required by a bound introduced by this call
@@ -51,9 +51,9 @@ note: required by a bound in `diesel::RunQueryDsl::load`
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::load`
 
 error[E0277]: the trait bound `diesel::pg::expression::date_and_time::AtTimeZone<diesel::dsl::now, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>: QueryFragment<Sqlite>` is not satisfied
-  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:31:22
+  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:37:22
    |
-31 |         .load::<i32>(&mut connection);
+37 |         .load::<i32>(&mut connection);
    |          ----        ^^^^^^^^^^^^^^^ the trait `QueryFragment<Sqlite>` is not implemented for `diesel::pg::expression::date_and_time::AtTimeZone<diesel::dsl::now, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>`
    |          |
    |          required by a bound introduced by this call
@@ -73,11 +73,12 @@ note: required by a bound in `diesel::RunQueryDsl::load`
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::load`
 
 error[E0599]: the method `execute` exists for struct `IncompleteOnConflict<InsertStatement<table, ValuesClause<(DefaultableColumnInsertValue<...>,), ...>>, ...>`, but its trait bounds were not satisfied
-  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:34:10
+  --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:41:10
    |
-32 | /     insert_into(users).values(&NewUser("Sean"))
-33 | |         .on_conflict(on_constraint("name"))
-34 | |         .execute(&mut connection);
+38 | /     insert_into(users)
+39 | |         .values(&NewUser("Sean"))
+40 | |         .on_conflict(on_constraint("name"))
+41 | |         .execute(&mut connection);
    | |         -^^^^^^^ method cannot be called due to unsatisfied trait bounds
    | |_________|
    |
diff --git a/diesel_compile_tests/tests/fail/right_side_of_left_join_requires_nullable.rs b/diesel_compile_tests/tests/fail/right_side_of_left_join_requires_nullable.rs
index c62f19081a47..e6759ecd079a 100644
--- a/diesel_compile_tests/tests/fail/right_side_of_left_join_requires_nullable.rs
+++ b/diesel_compile_tests/tests/fail/right_side_of_left_join_requires_nullable.rs
@@ -30,7 +30,7 @@ table! {
 joinable!(posts -> users (user_id));
 joinable!(pets -> users (user_id));
 allow_tables_to_appear_in_same_query!(posts, users, pets);
-sql_function!(fn lower(x: Text) -> Text);
+define_sql_function!(fn lower(x: Text) -> Text);
 
 fn main() {}
 
diff --git a/diesel_compile_tests/tests/fail/right_side_of_left_join_requires_nullable.stderr b/diesel_compile_tests/tests/fail/right_side_of_left_join_requires_nullable.stderr
index 1e47a5e0021d..0d8941f56475 100644
--- a/diesel_compile_tests/tests/fail/right_side_of_left_join_requires_nullable.stderr
+++ b/diesel_compile_tests/tests/fail/right_side_of_left_join_requires_nullable.stderr
@@ -49,8 +49,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17 |         title -> Text,
    |         ^^^^^
    = note: 2 redundant requirements hidden
-   = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-   = note: required for `SelectStatement<FromClause<JoinOn<Join<table, table, LeftOuter>, Grouped<Eq<Nullable<user_id>, ...>>>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+   = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+   = note: required for `SelectStatement<FromClause<JoinOn<Join<table, table, LeftOuter>, Grouped<Eq<Nullable<user_id>, ...>>>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0277]: Cannot select `posts::columns::title` from `users::table`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:47:18
@@ -73,8 +73,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17 |         title -> Text,
    |         ^^^^^
    = note: 2 redundant requirements hidden
-   = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-   = note: required for `SelectStatement<FromClause<JoinOn<Join<table, table, LeftOuter>, Grouped<Eq<Nullable<user_id>, ...>>>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+   = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+   = note: required for `SelectStatement<FromClause<JoinOn<Join<table, table, LeftOuter>, Grouped<Eq<Nullable<user_id>, ...>>>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:49:31
@@ -90,9 +90,9 @@ error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType
 note: required by a bound in `lower`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1
    |
-33 | sql_function!(fn lower(x: Text) -> Text);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
-   = note: this error originates in the macro `sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
+33 | define_sql_function!(fn lower(x: Text) -> Text);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
+   = note: this error originates in the macro `define_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:49:25
@@ -155,8 +155,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17 |         title -> Text,
    |         ^^^^^
    = note: 4 redundant requirements hidden
-   = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-   = note: required for `SelectStatement<FromClause<JoinOn<Join<JoinOn<Join<table, table, LeftOuter>, Grouped<...>>, ..., ...>, ...>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+   = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+   = note: required for `SelectStatement<FromClause<JoinOn<Join<JoinOn<Join<table, table, LeftOuter>, Grouped<...>>, ..., ...>, ...>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0277]: Cannot select `posts::columns::title` from `users::table`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:64:18
@@ -179,8 +179,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17 |         title -> Text,
    |         ^^^^^
    = note: 4 redundant requirements hidden
-   = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-   = note: required for `SelectStatement<FromClause<JoinOn<Join<JoinOn<Join<table, table, LeftOuter>, Grouped<...>>, ..., ...>, ...>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+   = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+   = note: required for `SelectStatement<FromClause<JoinOn<Join<JoinOn<Join<table, table, LeftOuter>, Grouped<...>>, ..., ...>, ...>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:66:31
@@ -196,9 +196,9 @@ error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType
 note: required by a bound in `lower`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1
    |
-33 | sql_function!(fn lower(x: Text) -> Text);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
-   = note: this error originates in the macro `sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
+33 | define_sql_function!(fn lower(x: Text) -> Text);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
+   = note: this error originates in the macro `define_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:66:25
@@ -261,8 +261,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17 |         title -> Text,
    |         ^^^^^
    = note: 4 redundant requirements hidden
-   = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>, pets::table, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-   = note: required for `SelectStatement<FromClause<JoinOn<Join<JoinOn<Join<table, table, LeftOuter>, Grouped<...>>, ..., ...>, ...>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+   = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>, pets::table, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+   = note: required for `SelectStatement<FromClause<JoinOn<Join<JoinOn<Join<table, table, LeftOuter>, Grouped<...>>, ..., ...>, ...>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0277]: Cannot select `posts::columns::title` from `users::table`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:81:18
@@ -285,8 +285,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17 |         title -> Text,
    |         ^^^^^
    = note: 4 redundant requirements hidden
-   = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>, pets::table, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-   = note: required for `SelectStatement<FromClause<JoinOn<Join<JoinOn<Join<table, table, LeftOuter>, Grouped<...>>, ..., ...>, ...>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+   = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>, pets::table, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+   = note: required for `SelectStatement<FromClause<JoinOn<Join<JoinOn<Join<table, table, LeftOuter>, Grouped<...>>, ..., ...>, ...>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:83:31
@@ -302,9 +302,9 @@ error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType
 note: required by a bound in `lower`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1
    |
-33 | sql_function!(fn lower(x: Text) -> Text);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
-   = note: this error originates in the macro `sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
+33 | define_sql_function!(fn lower(x: Text) -> Text);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
+   = note: this error originates in the macro `define_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:83:25
@@ -367,8 +367,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17 |         title -> Text,
    |         ^^^^^
    = note: 2 redundant requirements hidden
-   = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<pets::table, SelectStatement<FromClause<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>>, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-   = note: required for `SelectStatement<FromClause<JoinOn<Join<table, SelectStatement<FromClause<JoinOn<..., ...>>>, ...>, ...>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+   = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<pets::table, SelectStatement<FromClause<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>>, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+   = note: required for `SelectStatement<FromClause<JoinOn<Join<table, SelectStatement<FromClause<JoinOn<..., ...>>>, ...>, ...>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0277]: Cannot select `posts::columns::title` from `pets::table`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:96:18
@@ -391,8 +391,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17 |         title -> Text,
    |         ^^^^^
    = note: 2 redundant requirements hidden
-   = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<pets::table, SelectStatement<FromClause<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>>, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-   = note: required for `SelectStatement<FromClause<JoinOn<Join<table, SelectStatement<FromClause<JoinOn<..., ...>>>, ...>, ...>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+   = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<pets::table, SelectStatement<FromClause<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>>, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+   = note: required for `SelectStatement<FromClause<JoinOn<Join<table, SelectStatement<FromClause<JoinOn<..., ...>>>, ...>, ...>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:98:31
@@ -408,9 +408,9 @@ error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType
 note: required by a bound in `lower`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1
    |
-33 | sql_function!(fn lower(x: Text) -> Text);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
-   = note: this error originates in the macro `sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
+33 | define_sql_function!(fn lower(x: Text) -> Text);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
+   = note: this error originates in the macro `define_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
   --> tests/fail/right_side_of_left_join_requires_nullable.rs:98:25
@@ -473,8 +473,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17  |         title -> Text,
     |         ^^^^^
     = note: 5 redundant requirements hidden
-    = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<pets::table, SelectStatement<FromClause<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>>, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-    = note: required for `SelectStatement<FromClause<JoinOn<Join<table, SelectStatement<FromClause<JoinOn<..., ...>>>, ...>, ...>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+    = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<pets::table, SelectStatement<FromClause<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>>, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+    = note: required for `SelectStatement<FromClause<JoinOn<Join<table, SelectStatement<FromClause<JoinOn<..., ...>>>, ...>, ...>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0277]: Cannot select `posts::columns::title` from `users::table`
    --> tests/fail/right_side_of_left_join_requires_nullable.rs:111:18
@@ -497,8 +497,8 @@ note: required for `posts::columns::title` to implement `SelectableExpression<qu
 17  |         title -> Text,
     |         ^^^^^
     = note: 5 redundant requirements hidden
-    = note: required for `lower::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<pets::table, SelectStatement<FromClause<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>>, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
-    = note: required for `SelectStatement<FromClause<JoinOn<Join<table, SelectStatement<FromClause<JoinOn<..., ...>>>, ...>, ...>>>` to implement `SelectDsl<lower::lower<posts::columns::title>>`
+    = note: required for `lower_utils::lower<posts::columns::title>` to implement `SelectableExpression<JoinOn<query_source::joins::Join<pets::table, SelectStatement<FromClause<JoinOn<query_source::joins::Join<users::table, posts::table, LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<posts::columns::user_id>, NullableExpression<users::columns::id>>>>>>, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<NullableExpression<pets::columns::user_id>, NullableExpression<users::columns::id>>>>>`
+    = note: required for `SelectStatement<FromClause<JoinOn<Join<table, SelectStatement<FromClause<JoinOn<..., ...>>>, ...>, ...>>>` to implement `SelectDsl<lower_utils::lower<posts::columns::title>>`
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
    --> tests/fail/right_side_of_left_join_requires_nullable.rs:113:31
@@ -514,9 +514,9 @@ error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType
 note: required by a bound in `lower`
    --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1
     |
-33  | sql_function!(fn lower(x: Text) -> Text);
-    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
-    = note: this error originates in the macro `sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
+33  | define_sql_function!(fn lower(x: Text) -> Text);
+    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower`
+    = note: this error originates in the macro `define_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0271]: type mismatch resolving `<Nullable<title> as Expression>::SqlType == Text`
    --> tests/fail/right_side_of_left_join_requires_nullable.rs:113:25
diff --git a/diesel_compile_tests/tests/fail/selectable.stderr b/diesel_compile_tests/tests/fail/selectable.stderr
index 1bbbbcc69c47..db8bcef3d6c8 100644
--- a/diesel_compile_tests/tests/fail/selectable.stderr
+++ b/diesel_compile_tests/tests/fail/selectable.stderr
@@ -234,7 +234,7 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg
     = help: the following other types implement trait `MixedAggregates<Other>`:
               <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::No>>
               <diesel::expression::is_aggregate::No as MixedAggregates<diesel::expression::is_aggregate::Never>>
-    = note: required for `(users::columns::name, diesel::expression::count::count::count<diesel::sql_types::Integer, posts::columns::id>)` to implement `ValidGrouping<()>`
+    = note: required for `(users::columns::name, diesel::expression::count::count_utils::count<diesel::sql_types::Integer, posts::columns::id>)` to implement `ValidGrouping<()>`
     = note: 2 redundant requirements hidden
     = note: required for `diesel::expression::select_by::SelectBy<UserWithPostCount, Pg>` to implement `ValidGrouping<()>`
     = note: required for `SelectStatement<FromClause<JoinOn<Join<table, table, Inner>, Grouped<Eq<Nullable<user_id>, Nullable<id>>>>>>` to implement `SelectDsl<diesel::expression::select_by::SelectBy<UserWithPostCount, Pg>>`
diff --git a/diesel_compile_tests/tests/fail/user_defined_functions_follow_same_selection_rules.rs b/diesel_compile_tests/tests/fail/user_defined_functions_follow_same_selection_rules.rs
index 88a40720aee3..430854976cf1 100644
--- a/diesel_compile_tests/tests/fail/user_defined_functions_follow_same_selection_rules.rs
+++ b/diesel_compile_tests/tests/fail/user_defined_functions_follow_same_selection_rules.rs
@@ -1,7 +1,7 @@
 extern crate diesel;
 
-use diesel::*;
 use diesel::sql_types::*;
+use diesel::*;
 
 table! {
     users {
@@ -23,17 +23,18 @@ struct User {
     name: String,
 }
 
-sql_function!(fn foo(x: Integer) -> Integer);
-sql_function!(fn bar(x: VarChar) -> VarChar);
+define_sql_function!(fn foo(x: Integer) -> Integer);
+define_sql_function!(fn bar(x: VarChar) -> VarChar);
 
 fn main() {
-    use self::users::name;
     use self::posts::title;
+    use self::users::name;
 
     let mut conn = PgConnection::establish("").unwrap();
 
     let _ = users::table.filter(name.eq(foo(1)));
 
-    let _ = users::table.filter(name.eq(bar(title)))
+    let _ = users::table
+        .filter(name.eq(bar(title)))
         .load::<User>(&mut conn);
 }
diff --git a/diesel_compile_tests/tests/fail/user_defined_functions_follow_same_selection_rules.stderr b/diesel_compile_tests/tests/fail/user_defined_functions_follow_same_selection_rules.stderr
index 3b50e58d9fbc..eca81fbfdb82 100644
--- a/diesel_compile_tests/tests/fail/user_defined_functions_follow_same_selection_rules.stderr
+++ b/diesel_compile_tests/tests/fail/user_defined_functions_follow_same_selection_rules.stderr
@@ -4,12 +4,12 @@ error[E0271]: type mismatch resolving `<foo<Bound<Integer, i32>> as Expression>:
 35 |     let _ = users::table.filter(name.eq(foo(1)));
    |                                      ^^ expected `Integer`, found `Text`
    |
-   = note: required for `foo::foo<diesel::expression::bound::Bound<diesel::sql_types::Integer, i32>>` to implement `AsExpression<diesel::sql_types::Text>`
+   = note: required for `foo_utils::foo<diesel::expression::bound::Bound<diesel::sql_types::Integer, i32>>` to implement `AsExpression<diesel::sql_types::Text>`
 
 error[E0271]: type mismatch resolving `<table as AppearsInFromClause<table>>::Count == Once`
-  --> tests/fail/user_defined_functions_follow_same_selection_rules.rs:38:23
+  --> tests/fail/user_defined_functions_follow_same_selection_rules.rs:39:23
    |
-38 |         .load::<User>(&mut conn);
+39 |         .load::<User>(&mut conn);
    |          ----         ^^^^^^^^^ expected `Once`, found `Never`
    |          |
    |          required by a bound introduced by this call
@@ -20,7 +20,7 @@ note: required for `posts::columns::title` to implement `AppearsOnTable<users::t
 16 |         title -> VarChar,
    |         ^^^^^
    = note: 3 redundant requirements hidden
-   = note: required for `diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<users::columns::name, bar::bar<posts::columns::title>>>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<users::columns::name, bar_utils::bar<posts::columns::title>>>` to implement `AppearsOnTable<users::table>`
    = note: required for `WhereClause<Grouped<Eq<name, bar<title>>>>` to implement `diesel::query_builder::where_clause::ValidWhereClause<FromClause<users::table>>`
    = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, WhereClause<...>>` to implement `Query`
    = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, WhereClause<...>>` to implement `LoadQuery<'_, _, User>`
@@ -34,9 +34,9 @@ note: required by a bound in `diesel::RunQueryDsl::load`
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::load`
 
 error[E0277]: the trait bound `users::table: TableNotEqual<posts::table>` is not satisfied
-  --> tests/fail/user_defined_functions_follow_same_selection_rules.rs:38:23
+  --> tests/fail/user_defined_functions_follow_same_selection_rules.rs:39:23
    |
-38 |         .load::<User>(&mut conn);
+39 |         .load::<User>(&mut conn);
    |          ----         ^^^^^^^^^ the trait `TableNotEqual<posts::table>` is not implemented for `users::table`
    |          |
    |          required by a bound introduced by this call
@@ -58,7 +58,7 @@ note: required for `posts::columns::title` to implement `AppearsOnTable<users::t
 16 |         title -> VarChar,
    |         ^^^^^
    = note: 3 redundant requirements hidden
-   = note: required for `diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<users::columns::name, bar::bar<posts::columns::title>>>` to implement `AppearsOnTable<users::table>`
+   = note: required for `diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<users::columns::name, bar_utils::bar<posts::columns::title>>>` to implement `AppearsOnTable<users::table>`
    = note: required for `WhereClause<Grouped<Eq<name, bar<title>>>>` to implement `diesel::query_builder::where_clause::ValidWhereClause<FromClause<users::table>>`
    = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, WhereClause<...>>` to implement `Query`
    = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, WhereClause<...>>` to implement `LoadQuery<'_, _, User>`
diff --git a/diesel_derives/tests/auto_type.rs b/diesel_derives/tests/auto_type.rs
new file mode 100644
index 000000000000..edaa2d3ab294
--- /dev/null
+++ b/diesel_derives/tests/auto_type.rs
@@ -0,0 +1,366 @@
+#![allow(dead_code)] // this is a compile pass test
+use diesel::dsl::*;
+use diesel::helper_types::*;
+use diesel::prelude::*;
+
+table! {
+    users {
+        id -> Integer,
+        name -> Text,
+        time -> Timestamp,
+    }
+}
+
+table! {
+    posts {
+        id -> Integer,
+        user_id -> Integer,
+    }
+}
+
+table! {
+    posts2 {
+        id -> Integer,
+        user_id -> Integer,
+    }
+}
+
+table! {
+    posts3 {
+        id -> Integer,
+        user_id -> Integer,
+    }
+}
+
+#[cfg(feature = "postgres")]
+table! {
+    pg_extras(id) {
+        id -> Integer,
+        json -> Json,
+        jsonb -> Jsonb,
+        net -> Inet,
+        array -> Array<Integer>,
+        blob -> Binary,
+        timestamp -> Timestamp,
+        range -> Range<Integer>,
+    }
+}
+
+joinable!(posts -> users(user_id));
+joinable!(posts2 -> users(user_id));
+joinable!(posts3 -> users(user_id));
+allow_tables_to_appear_in_same_query!(users, posts, posts2, posts3);
+
+#[auto_type]
+fn test_all_query_dsl() -> _ {
+    users::table
+        .distinct()
+        .filter(users::id.eq(42_i32))
+        .find(42_i32)
+        .or_filter(users::id.eq(42_i32))
+        .limit(23_i64)
+        .offset(12_i64)
+        .order(users::id)
+        .order_by(users::id)
+        .then_order_by(users::id)
+        .select(users::id)
+        .group_by(users::id)
+        .having(users::id.eq(32_i32))
+        .inner_join(posts::table)
+        .left_join(posts2::table)
+        .inner_join(posts3::table.on(users::id.eq(posts3::user_id)))
+    //.into_boxed()
+}
+
+#[auto_type]
+fn single_value() -> _ {
+    users::table.select(users::id).find(42_i32).single_value()
+}
+
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_distinct_on() -> _ {
+    users::table.distinct_on(users::id)
+}
+
+#[auto_type]
+fn test_lock_dsl1() -> _ {
+    users::table.for_key_share().no_wait().skip_locked()
+}
+
+#[auto_type]
+fn test_lock_dsl2() -> _ {
+    users::table.for_no_key_update()
+}
+
+#[auto_type]
+fn test_lock_dsl3() -> _ {
+    users::table.for_share()
+}
+
+#[auto_type]
+fn test_lock_dsl4() -> _ {
+    users::table.for_update()
+}
+
+// #[auto_type]
+// fn test_count_query() -> _ {
+//     users::table.find(1_i32).count()
+// }
+
+#[auto_type]
+fn test_expression_methods() -> _ {
+    let v = 42_i32;
+    let v2: &'static [i32] = &[42];
+    users::id
+        .eq(v)
+        .and(users::id.ne(v))
+        .and(users::id.eq_any(v2))
+        .and(users::id.ne_all(v2))
+        .and(users::id.gt(v))
+        .and(users::id.lt(v))
+        .and(users::id.is_not_null())
+        .and(users::id.is_null())
+        .and(users::id.le(v))
+        .and(users::id.ge(v))
+        .and(users::id.between(v, v))
+        .and(users::id.not_between(v, v))
+}
+
+#[auto_type]
+fn test_boolean_expression_methods() -> _ {
+    let v = 42_i32;
+    users::id.eq(v).and(users::id.eq(v)).or(users::id.eq(v))
+}
+
+#[auto_type]
+fn test_nullable_expression_methods() -> _ {
+    users::id.nullable().assume_not_null()
+}
+
+#[auto_type]
+fn test_text_expression_methods() -> _ {
+    let a: &'static str = "foo";
+    users::name
+        .like(a)
+        .and(users::name.not_like(a))
+        .and(users::name.concat(a).eq(a))
+}
+
+#[auto_type]
+fn test_delete() -> _ {
+    delete(users::table)
+}
+
+#[auto_type]
+fn test_delete_2() -> _ {
+    delete(users::table.find(1_i32))
+}
+
+#[auto_type]
+fn test_delete_3() -> _ {
+    delete(users::table).filter(users::id.eq(1_i32))
+}
+
+// #[auto_type]
+// fn test_update() -> _ {
+//     update(users::table).set(users::id.eq(42_i32))
+// }
+
+#[auto_type]
+fn test_insert1() -> _ {
+    insert_into(users::table).values(users::id.eq(42_i32))
+}
+
+/*
+#[auto_type]
+fn test_insert2() -> _ {
+    users::table.insert_into(users::table).into_columns(users::all_columns)
+}*/
+
+#[auto_type]
+fn test_insert_or_ignore() -> _ {
+    insert_or_ignore_into(users::table).values(users::id.eq(42_i32))
+}
+
+#[auto_type]
+fn test_insert_or_replace() -> _ {
+    replace_into(users::table).values(users::id.eq(42_i32))
+}
+
+/*
+#[auto_type]
+fn test_bare_select() -> _ {
+    select(1_i32.into_sql::<Integer>())
+}*/
+
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_pg_expression_methods() -> _ {
+    let v = 42_i32;
+    users::id
+        .is_not_distinct_from(v)
+        .and(users::id.is_distinct_from(v))
+}
+
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_pg_text_expression_methods() -> _ {
+    let a: &'static str = "foo";
+    users::name
+        .ilike(a)
+        .and(users::name.not_ilike(a))
+        .and(users::name.similar_to(a))
+        .and(users::name.not_similar_to(a))
+}
+
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_pg_net_expression_methods() -> _ {
+    // cannot be supported on diesel 2.x as the contains operator for net
+    // is different than the "normal" contains operator
+    // We could probably rename this function to `contains_net` to make it work
+    //pg_extras::net.contains(pg_extras::net)
+    pg_extras::net
+        .contains_or_eq(pg_extras::net)
+        // cannot be supported on diesel 2.x due to similar reasons
+        // as `contains`
+        //.and(pg_extras::net.is_contained_by(pg_extras::net))
+        .and(pg_extras::net.is_contained_by_or_eq(pg_extras::net))
+        .and(pg_extras::net.overlaps_with(pg_extras::net))
+        // `.and()` and `or()` for inet cannot be supported as that name collides
+        // with `BoolExpressionMethods`
+        //.and(pg_extras::net.and(pg_extras::net).contains_or_eq(pg_extras::net))
+        //.and(pg_extras::net.or(pg_extras::net).contains(pg_extras::net))
+        .and(pg_extras::net.diff(pg_extras::net).eq(42_i64))
+}
+
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_pg_array_expression_methods() -> _ {
+    let v = 42_i32;
+    pg_extras::array
+        .overlaps_with(pg_extras::array)
+        .and(pg_extras::array.contains(pg_extras::array))
+        .and(pg_extras::array.is_contained_by(pg_extras::array))
+        .and(pg_extras::array.index(v).eq(v))
+        .and(
+            pg_extras::array
+                .concat(pg_extras::array)
+                .eq(pg_extras::array),
+        )
+}
+
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_pg_jsonb_expression_methods() -> _ {
+    let s: &'static str = "";
+    let v: &'static [&'static str] = &[];
+
+    pg_extras::jsonb
+        .concat(pg_extras::jsonb)
+        .eq(pg_extras::jsonb)
+        .and(pg_extras::jsonb.has_any_key(v))
+        .and(pg_extras::jsonb.has_all_keys(v))
+        .and(pg_extras::jsonb.has_key(s))
+        .and(pg_extras::jsonb.contains(pg_extras::jsonb))
+        .and(pg_extras::jsonb.remove(1_i32).eq(pg_extras::jsonb))
+        .and(pg_extras::jsonb.remove_by_path(v).eq(pg_extras::jsonb))
+        .and(pg_extras::jsonb.is_contained_by(pg_extras::jsonb))
+}
+
+// `.contains()` cannot be supported here as
+// the type level constraints are slightly different
+// for `Range<>` than for the other types that provide a `contains()`
+// function. We could likely support it by
+// renaming the function to `.range_contains()` (or something similar)
+/*
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_pg_range_expression_methods() -> _ {
+    pg_extras::range.contains(42_i32)
+}*/
+
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_pg_binary_expression_methods() -> _ {
+    let b: &'static [u8] = &[];
+    pg_extras::blob
+        .concat(pg_extras::blob)
+        .like(pg_extras::blob)
+        .and(pg_extras::blob.not_like(b))
+}
+
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_pg_any_json_expression_methods() -> _ {
+    let s: &'static str = "";
+    let s2: &'static [&'static str] = &[];
+
+    pg_extras::jsonb
+        .retrieve_as_object(s)
+        .retrieve_as_text(s)
+        .eq(s)
+        .and(
+            pg_extras::jsonb
+                .retrieve_by_path_as_object(s2)
+                .retrieve_by_path_as_text(s2)
+                .eq(s),
+        )
+}
+
+#[cfg(feature = "postgres")]
+#[auto_type]
+fn test_pg_timestamp_expression_methods() -> _ {
+    let s: &'static str = "";
+    pg_extras::timestamp.at_time_zone(s)
+}
+
+#[cfg(feature = "sqlite")]
+#[auto_type]
+fn test_sqlite_expression_methods() -> _ {
+    users::id.is(42_i32).or(users::id.is_not(42_i32))
+}
+
+#[auto_type]
+fn test_aggregate_functions() -> _ {
+    users::table.select((
+        avg(users::id),
+        count(users::id),
+        count_distinct(users::id),
+        count_star(),
+        max(users::id),
+        min(users::id),
+        sum(users::id),
+    ))
+}
+
+#[auto_type]
+fn test_normal_functions() -> _ {
+    users::table.select((
+        date(users::time),
+        exists(posts::table.select(posts::id)),
+        not(users::id.eq(1_i32)),
+        case_when(users::id.eq(1_i32), users::id),
+        case_when(users::id.eq(1_i32), users::id).when(users::id.eq(42_i32), users::id),
+        case_when(users::id.eq(1_i32), users::id)
+            .when(users::id.eq(42_i32), users::id)
+            .otherwise(users::id),
+        case_when(users::id.eq(1_i32), users::id).otherwise(users::id),
+    ))
+}
+
+// #[auto_type]
+// fn test_sql_fragment() -> _ {
+//     sql("foo")
+// }
+
+// #[auto_type]
+// fn test_sql_query_1() -> _ {
+//     sql_query("bar")
+// }
+
+// #[auto_type]
+// fn test_sql_query_2() -> _ {
+//     sql_query("bar").bind::<Integer, _>(1)
+// }
diff --git a/diesel_derives/tests/tests.rs b/diesel_derives/tests/tests.rs
index ab3afd70f351..a1ffbdc8a80c 100644
--- a/diesel_derives/tests/tests.rs
+++ b/diesel_derives/tests/tests.rs
@@ -9,6 +9,7 @@ mod schema;
 mod as_changeset;
 mod as_expression;
 mod associations;
+mod auto_type;
 mod identifiable;
 mod insertable;
 mod multiconnection;
diff --git a/diesel_tests/tests/annotations.rs b/diesel_tests/tests/annotations.rs
index 7eb2caebc67b..841b4761f4da 100644
--- a/diesel_tests/tests/annotations.rs
+++ b/diesel_tests/tests/annotations.rs
@@ -286,7 +286,7 @@ fn derive_insertable_with_option_for_not_null_field_with_default() {
     assert_eq!(Some(&User::new(123, "Bob")), bob);
 }
 
-sql_function!(fn nextval(a: Text) -> Integer);
+define_sql_function!(fn nextval(a: Text) -> Integer);
 
 #[test]
 #[cfg(feature = "postgres")]
@@ -294,7 +294,7 @@ fn derive_insertable_with_field_that_cannot_convert_expression_to_nullable() {
     #[derive(Insertable)]
     #[diesel(table_name = users)]
     struct NewUser {
-        id: nextval::HelperType<&'static str>,
+        id: nextval<&'static str>,
         name: &'static str,
     }
 
diff --git a/diesel_tests/tests/expressions/mod.rs b/diesel_tests/tests/expressions/mod.rs
index eabcb47795e9..3f074c123e2d 100644
--- a/diesel_tests/tests/expressions/mod.rs
+++ b/diesel_tests/tests/expressions/mod.rs
@@ -219,7 +219,7 @@ fn test_min() {
     assert_eq!(Ok(None::<i32>), source.first(connection));
 }
 
-sql_function!(fn coalesce(x: sql_types::Nullable<sql_types::VarChar>, y: sql_types::VarChar) -> sql_types::VarChar);
+define_sql_function!(fn coalesce(x: sql_types::Nullable<sql_types::VarChar>, y: sql_types::VarChar) -> sql_types::VarChar);
 
 #[test]
 fn function_with_multiple_arguments() {
@@ -445,7 +445,7 @@ fn test_arrays_a() {
 #[cfg(feature = "postgres")]
 use diesel::sql_types::{Array, Int4};
 #[cfg(feature = "postgres")]
-sql_function!(fn unnest(a: Array<Int4>) -> Int4);
+define_sql_function!(fn unnest(a: Array<Int4>) -> Int4);
 
 #[test]
 #[cfg(feature = "postgres")]
diff --git a/diesel_tests/tests/filter.rs b/diesel_tests/tests/filter.rs
index 3d99733ebb86..a962b143a45f 100644
--- a/diesel_tests/tests/filter.rs
+++ b/diesel_tests/tests/filter.rs
@@ -412,7 +412,7 @@ fn not_affects_arguments_passed_when_they_contain_higher_operator_precedence() {
 }
 
 use diesel::sql_types::VarChar;
-sql_function!(fn lower(x: VarChar) -> VarChar);
+define_sql_function!(fn lower(x: VarChar) -> VarChar);
 
 #[test]
 fn filter_by_boxed_predicate() {
diff --git a/diesel_tests/tests/joins.rs b/diesel_tests/tests/joins.rs
index 24b85bab8b72..47bf6d38551c 100644
--- a/diesel_tests/tests/joins.rs
+++ b/diesel_tests/tests/joins.rs
@@ -358,7 +358,7 @@ fn select_then_join() {
 }
 
 use diesel::sql_types::Text;
-sql_function!(fn lower(x: Text) -> Text);
+define_sql_function!(fn lower(x: Text) -> Text);
 
 #[test]
 fn selecting_complex_expression_from_right_side_of_left_join() {
diff --git a/diesel_tests/tests/macros.rs b/diesel_tests/tests/macros.rs
index e5727b8d47aa..7a7a21563078 100644
--- a/diesel_tests/tests/macros.rs
+++ b/diesel_tests/tests/macros.rs
@@ -7,7 +7,7 @@ use crate::schema::*;
 use diesel::sql_types::{BigInt, VarChar};
 use diesel::*;
 
-sql_function!(fn my_lower(x: VarChar) -> VarChar);
+define_sql_function!(fn my_lower(x: VarChar) -> VarChar);
 
 #[test]
 fn test_sql_function() {
@@ -40,8 +40,8 @@ fn test_sql_function() {
     );
 }
 
-sql_function!(fn setval(x: VarChar, y: BigInt));
-sql_function!(fn currval(x: VarChar) -> BigInt);
+define_sql_function!(fn setval(x: VarChar, y: BigInt));
+define_sql_function!(fn currval(x: VarChar) -> BigInt);
 
 #[test]
 fn sql_function_without_return_type() {
diff --git a/diesel_tests/tests/schema/mod.rs b/diesel_tests/tests/schema/mod.rs
index 7aad34912c19..8584e1b67971 100644
--- a/diesel_tests/tests/schema/mod.rs
+++ b/diesel_tests/tests/schema/mod.rs
@@ -318,7 +318,7 @@ pub fn drop_table_cascade(connection: &mut TestConnection, table: &str) {
         .unwrap();
 }
 
-sql_function!(fn nextval(a: sql_types::VarChar) -> sql_types::BigInt);
+define_sql_function!(fn nextval(a: sql_types::VarChar) -> sql_types::BigInt);
 
 pub fn connection_with_sean_and_tess_in_users_table() -> TestConnection {
     let mut connection = connection();