diff --git a/CHANGELOG.md b/CHANGELOG.md index a410e44d83c0..0dc2c46ce84b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,13 +16,24 @@ Increasing the minimal supported Rust version will always be coupled at least wi * Fixed `#[derive(Identifiable)]` ignoring attribute `#[diesel(serialize_as)]` on primary keys * Added embedded struct support for `AsChangeset` via `#[diesel(embed)]` * Support for libsqlite3-sys 0.30.0 -* Add support for built-in PostgreSQL range operators and functions +* Added support for built-in PostgreSQL range operators and functions +* Added support for various built-in PostgreSQL array functions * Support for postgres multirange type * Added `diesel::r2d2::TestCustomizer`, which allows users to customize their `diesel::r2d2::Pool`s in a way that makes the pools suitable for use in parallel tests. * Added `Json` and `Jsonb` support for the SQLite backend. +* Added a `#[diesel::declare_sql_function]` attribute macro to easily define support for + multiple sql functions at once via an `extern "SQL"` block + +### Fixed + * Fixed diesel thinking `a.eq_any(b)` was non-nullable even if `a` and `b` were nullable. +### Deprecated + +* The `diesel::define_sql_function!` is now deprecated in favour of the `#[diesel::declare_sql_function]` + attribute macro. + ## [2.2.2] 2024-07-19 ### Fixed diff --git a/diesel/src/connection/statement_cache/strategy.rs b/diesel/src/connection/statement_cache/strategy.rs index 7c7919038827..2e3cf269f18c 100644 --- a/diesel/src/connection/statement_cache/strategy.rs +++ b/diesel/src/connection/statement_cache/strategy.rs @@ -148,7 +148,6 @@ mod testing_utils { mod tests_pg { use crate::connection::CacheSize; use crate::dsl::sql; - use crate::expression::functions::define_sql_function; use crate::insertable::Insertable; use crate::pg::Pg; use crate::sql_types::{Integer, VarChar}; @@ -158,6 +157,11 @@ mod tests_pg { use super::testing_utils::{count_cache_calls, RecordCacheEvents}; + #[crate::declare_sql_function] + extern "SQL" { + fn lower(x: VarChar) -> VarChar; + } + table! { users { id -> Integer, @@ -209,8 +213,6 @@ mod tests_pg { assert_eq!(2, count_cache_calls(connection)); } - define_sql_function!(fn lower(x: VarChar) -> VarChar); - #[test] fn queries_with_identical_types_and_binds_but_different_sql_are_cached_separately() { let connection = &mut connection(); diff --git a/diesel/src/expression/count.rs b/diesel/src/expression/count.rs index e3512b22995c..be3dc6875a36 100644 --- a/diesel/src/expression/count.rs +++ b/diesel/src/expression/count.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use super::functions::define_sql_function; +use super::functions::declare_sql_function; use super::{is_aggregate, AsExpression}; use super::{Expression, ValidGrouping}; use crate::backend::Backend; @@ -9,7 +9,8 @@ use crate::result::QueryResult; use crate::sql_types::{BigInt, DieselNumericOps, SingleValue, SqlType}; use crate::{AppearsOnTable, SelectableExpression}; -define_sql_function! { +#[declare_sql_function] +extern "SQL" { /// 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/functions/aggregate_folding.rs b/diesel/src/expression/functions/aggregate_folding.rs index 25fcc8da051f..081cbfed8605 100644 --- a/diesel/src/expression/functions/aggregate_folding.rs +++ b/diesel/src/expression/functions/aggregate_folding.rs @@ -1,7 +1,8 @@ -use crate::expression::functions::define_sql_function; +use crate::expression::functions::declare_sql_function; use crate::sql_types::Foldable; -define_sql_function! { +#[declare_sql_function] +extern "SQL" { /// Represents a SQL `SUM` function. This function can only take types which are /// Foldable. /// @@ -19,9 +20,7 @@ define_sql_function! { /// ``` #[aggregate] fn sum(expr: ST) -> ST::Sum; -} -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 c36b6cc0b96b..b50de3d4831e 100644 --- a/diesel/src/expression/functions/aggregate_ordering.rs +++ b/diesel/src/expression/functions/aggregate_ordering.rs @@ -1,7 +1,8 @@ use self::private::SqlOrdAggregate; -use crate::expression::functions::define_sql_function; +use crate::expression::functions::declare_sql_function; -define_sql_function! { +#[declare_sql_function] +extern "SQL" { /// Represents a SQL `MAX` function. This function can only take types which are /// ordered. /// @@ -18,9 +19,7 @@ define_sql_function! { /// # } #[aggregate] fn max(expr: ST) -> ST::Ret; -} -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 8433d5db8524..eadf37fd56db 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::define_sql_function; +use crate::expression::functions::declare_sql_function; use crate::expression::{AsExpression, Expression, ValidGrouping}; use crate::query_builder::*; use crate::result::QueryResult; @@ -27,7 +27,9 @@ impl_selectable_expression!(now); operator_allowed!(now, Add, add); operator_allowed!(now, Sub, sub); -define_sql_function! { + +#[declare_sql_function] +extern "SQL" { /// 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/mod.rs b/diesel/src/expression/functions/mod.rs index db8f79e7a730..62406f4da2a4 100644 --- a/diesel/src/expression/functions/mod.rs +++ b/diesel/src/expression/functions/mod.rs @@ -1,5 +1,9 @@ //! Helper macros to define custom sql functions +#[doc(inline)] +pub use diesel_derives::declare_sql_function; + +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] #[doc(inline)] pub use diesel_derives::define_sql_function; diff --git a/diesel/src/lib.rs b/diesel/src/lib.rs index 3b558054da77..2faa9b911a95 100644 --- a/diesel/src/lib.rs +++ b/diesel/src/lib.rs @@ -725,6 +725,8 @@ pub mod prelude { pub use crate::expression::IntoSql as _; #[doc(inline)] + pub use crate::expression::functions::declare_sql_function; + #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] pub use crate::expression::functions::define_sql_function; #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] pub use crate::expression::functions::sql_function; diff --git a/diesel/src/pg/expression/functions.rs b/diesel/src/pg/expression/functions.rs index c8a22d2c15e1..a4f6aaad30d6 100644 --- a/diesel/src/pg/expression/functions.rs +++ b/diesel/src/pg/expression/functions.rs @@ -1,7 +1,7 @@ //! PostgreSQL specific functions use super::expression_methods::InetOrCidr; -use crate::expression::functions::define_sql_function; +use crate::expression::functions::declare_sql_function; use crate::pg::expression::expression_methods::ArrayOrNullableArray; use crate::pg::expression::expression_methods::CombinedAllNullableValue; use crate::pg::expression::expression_methods::CombinedNullableValue; @@ -15,66 +15,58 @@ use crate::pg::expression::expression_methods::RecordOrNullableRecord; use crate::pg::expression::expression_methods::TextArrayOrNullableTextArray; use crate::sql_types::*; -define_sql_function! { +#[declare_sql_function] +extern "SQL" { /// Creates an abbreviated display format as text. #[cfg(feature = "postgres_backend")] fn abbrev(addr: T) -> Text; -} -define_sql_function! { + /// Computes the broadcast address for the address's network. #[cfg(feature = "postgres_backend")] fn broadcast(addr: T) -> Inet; -} -define_sql_function! { + /// Returns the address's family: 4 for IPv4, 6 for IPv6. #[cfg(feature = "postgres_backend")] fn family(addr: T) -> Integer; -} -define_sql_function! { + /// Returns the IP address as text, ignoring the netmask. #[cfg(feature = "postgres_backend")] fn host(addr: T) -> Text; -} -define_sql_function! { + /// Computes the host mask for the address's network. #[cfg(feature = "postgres_backend")] fn hostmask(addr: T) -> Inet; -} -define_sql_function! { + /// Computes the smallest network that includes both of the given networks. #[cfg(feature = "postgres_backend")] fn inet_merge(a: T, b: U) -> Cidr; -} -define_sql_function! { + /// Tests whether the addresses belong to the same IP family. #[cfg(feature = "postgres_backend")] - fn inet_same_family(a: T, b: U) -> Bool; -} -define_sql_function! { + fn inet_same_family( + a: T, + b: U, + ) -> Bool; + /// Returns the netmask length in bits. #[cfg(feature = "postgres_backend")] fn masklen(addr: T) -> Integer; -} -define_sql_function! { + /// Computes the network mask for the address's network. #[cfg(feature = "postgres_backend")] fn netmask(addr: T) -> Inet; -} -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(addr: T) -> Cidr; -} -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. #[cfg(feature = "postgres_backend")] fn set_masklen(addr: T, len: Integer) -> T; -} -define_sql_function! { /// Returns the lower bound of the range /// /// If the range is empty or has no lower bound, it returns NULL. @@ -110,9 +102,7 @@ define_sql_function! { /// ``` #[cfg(feature = "postgres_backend")] fn lower(range: R) -> Nullable; -} -define_sql_function! { /// Returns the upper bound of the range /// /// If the range is empty or has no upper bound, it returns NULL. @@ -148,9 +138,7 @@ define_sql_function! { /// ``` #[cfg(feature = "postgres_backend")] fn upper(range: R) -> Nullable; -} -define_sql_function! { /// Returns true if the range is empty /// /// # Example @@ -183,10 +171,10 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn isempty>(range: R) -> R::Out; -} + fn isempty>( + range: R, + ) -> R::Out; -define_sql_function! { /// Returns true if the range's lower bound is inclusive /// /// # Example @@ -219,10 +207,10 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn lower_inc>(range: R) -> R::Out; -} + fn lower_inc>( + range: R, + ) -> R::Out; -define_sql_function! { /// Returns true if the range's upper bound is inclusive /// /// # Example @@ -252,10 +240,10 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn upper_inc>(range: R) -> R::Out; -} + fn upper_inc>( + range: R, + ) -> R::Out; -define_sql_function! { /// Returns true if the range's lower bound is unbounded /// /// # Example @@ -288,10 +276,10 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn lower_inf>(range: R) -> R::Out; -} + fn lower_inf>( + range: R, + ) -> R::Out; -define_sql_function! { /// Returns true if the range's upper bound is unbounded /// /// # Example @@ -324,10 +312,10 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn upper_inf>(range: R) -> R::Out; -} + fn upper_inf>( + range: R, + ) -> R::Out; -define_sql_function! { /// Returns the smallest range which includes both of the given ranges /// /// # Example @@ -360,10 +348,16 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn range_merge + SingleValue + CombinedNullableValue>>(lhs: R1, rhs: R2) -> R2::Out; -} + fn range_merge< + R1: RangeOrNullableRange + SingleValue, + R2: RangeOrNullableRange + + SingleValue + + CombinedNullableValue>, + >( + lhs: R1, + rhs: R2, + ) -> R2::Out; -define_sql_function! { /// Returns the smallest range which includes all ranges in the multirange /// /// # Example @@ -391,10 +385,9 @@ define_sql_function! { /// ``` #[cfg(feature = "postgres_backend")] #[sql_name = "range_merge"] - fn multirange_merge(multirange: R) -> R::Range; -} + fn multirange_merge(multirange: R) + -> R::Range; -define_sql_function! { /// Returns range of integer /// /// # Example @@ -439,10 +432,12 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn int4range(lower: Nullable, upper: Nullable, bound: RangeBoundEnum) -> Int4range; -} + fn int4range( + lower: Nullable, + upper: Nullable, + bound: RangeBoundEnum, + ) -> Int4range; -define_sql_function! { /// Returns range of big ints /// /// # Example @@ -487,10 +482,12 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn int8range(lower: Nullable, upper: Nullable, bound: RangeBoundEnum) -> Int8range; -} + fn int8range( + lower: Nullable, + upper: Nullable, + bound: RangeBoundEnum, + ) -> Int8range; -define_sql_function! { /// Returns range of numeric values /// /// # Example @@ -538,10 +535,12 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn numrange(lower: Nullable, upper: Nullable, bound: RangeBoundEnum) -> Numrange; -} + fn numrange( + lower: Nullable, + upper: Nullable, + bound: RangeBoundEnum, + ) -> Numrange; -define_sql_function! { /// Returns range of timestamps without timezone /// /// # Example @@ -589,10 +588,12 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn tsrange(lower: Nullable, upper: Nullable, bound: RangeBoundEnum) -> Tsrange; -} + fn tsrange( + lower: Nullable, + upper: Nullable, + bound: RangeBoundEnum, + ) -> Tsrange; -define_sql_function! { /// Returns range of timestamps with timezone /// /// # Example @@ -640,10 +641,12 @@ define_sql_function! { /// # } /// ``` #[cfg(feature = "postgres_backend")] - fn tstzrange(lower: Nullable, upper: Nullable, bound: RangeBoundEnum) -> Tstzrange; -} + fn tstzrange( + lower: Nullable, + upper: Nullable, + bound: RangeBoundEnum, + ) -> Tstzrange; -define_sql_function! { /// Returns range of dates /// /// # Example @@ -692,10 +695,7 @@ define_sql_function! { /// ``` #[cfg(feature = "postgres_backend")] fn daterange(lower: Nullable, upper: Nullable, bound: RangeBoundEnum) -> Daterange; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Append an element to the end of an array /// /// # Example @@ -729,11 +729,13 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_append + SingleValue, T: SingleValue>(a: Arr, e: T) -> Array; -} + /// + #[cfg(feature = "postgres_backend")] + fn array_append + SingleValue, T: SingleValue>( + a: Arr, + e: T, + ) -> Array; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Replace all occurrences of an element in an array with a given element /// /// # Example @@ -766,11 +768,13 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_replace + SingleValue, T: SingleValue>(a: Arr, e: T, r: T) -> Arr; -} + #[cfg(feature = "postgres_backend")] + fn array_replace + SingleValue, T: SingleValue>( + a: Arr, + e: T, + r: T, + ) -> Arr; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns a text representation of the array's dimensions /// /// # Example @@ -800,11 +804,9 @@ define_sql_function! { /// # Ok(()) /// # } /// - fn array_dims + SingleValue>(arr:Arr) -> Text; -} + #[cfg(feature = "postgres_backend")] + fn array_dims(arr: Arr) -> Text; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Prepends an element to the beginning of an array /// /// # Example @@ -838,11 +840,12 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_prepend + SingleValue>(e: T, a: Arr) -> Array; -} + #[cfg(feature = "postgres_backend")] + fn array_prepend + SingleValue>( + e: T, + a: Arr, + ) -> Array; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Removes all elements equal to the given value from the array /// /// # Example @@ -872,11 +875,12 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_remove + SingleValue, T: SingleValue>(a: Arr, e: T) -> Arr; -} + #[cfg(feature = "postgres_backend")] + fn array_remove + SingleValue, T: SingleValue>( + a: Arr, + e: T, + ) -> Arr; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Converts each array element to its text representation and concatenates those elements /// separated by the delimiter string. If `null_string` is provided and is not `NULL`, then `NULL` /// array entries are represented by that string; otherwise, they are omitted. @@ -916,14 +920,14 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] #[sql_name = "array_to_string"] fn array_to_string_with_null_string( - array: Arr, del: Text, null: Text + array: Arr, + del: Text, + null: Text, ) -> Text; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Converts each array element to its text representation and concatenates those elements /// separated by the delimiter string. `NULL` entries are omitted in this variant. /// See [array_to_string_with_null_string] for a variant with that argument. @@ -963,13 +967,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_to_string( - array: Arr, del: Text - ) -> Text; -} + #[cfg(feature = "postgres_backend")] + fn array_to_string(array: Arr, del: Text) -> Text; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the total number of elements in the array, or 0 if the array is empty. /// /// # Example @@ -1003,11 +1003,11 @@ define_sql_function! { /// # Ok(()) /// # } /// - fn cardinality>(a: Arr) -> Arr::Out; -} + #[cfg(feature = "postgres_backend")] + fn cardinality>( + a: Arr, + ) -> Arr::Out; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Trims an array by removing the last n elements. If the array is multidimensional, only the first dimension is trimmed. /// /// # Example @@ -1045,11 +1045,9 @@ define_sql_function! { /// # Ok(()) /// # } /// - fn trim_array(a: Arr, n: Integer) -> Arr; -} + #[cfg(feature = "postgres_backend")] + fn trim_array(a: Arr, n: Integer) -> Arr; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Concatenates two arrays /// /// # Example @@ -1077,11 +1075,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn array_cat(a: Arr, b: Arr) -> Arr; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the length of the requested array /// /// # Example @@ -1111,11 +1107,12 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_length(array: Arr, dimension: Integer) -> Nullable; -} + #[cfg(feature = "postgres_backend")] + fn array_length( + array: Arr, + dimension: Integer, + ) -> Nullable; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns an array initialized with supplied value and dimensions, /// optionally with lower bounds other than 1. This function omits the optional /// lower bound argument. See [array_fill_with_lower_bound] for that. @@ -1151,11 +1148,9 @@ define_sql_function! { /// # Ok(()) /// # } /// - fn array_fill(value: E, dim: Array) -> Array; -} + #[cfg(feature = "postgres_backend")] + fn array_fill(value: E, dim: Array) -> Array; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns an array initialized with supplied value and dimensions, /// with lower bounds other than 1 /// @@ -1191,11 +1186,13 @@ define_sql_function! { /// # } /// #[sql_name = "array_fill"] - fn array_fill_with_lower_bound(value: E, dim: Array, lower_bound: Array) -> Array; -} + #[cfg(feature = "postgres_backend")] + fn array_fill_with_lower_bound( + value: E, + dim: Array, + lower_bound: Array, + ) -> Array; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the lower bound of the requested array /// /// This function returns null for dimensions that do not exist @@ -1224,11 +1221,12 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_lower(array: Arr, dimension: Integer) -> Nullable; -} + #[cfg(feature = "postgres_backend")] + fn array_lower( + array: Arr, + dimension: Integer, + ) -> Nullable; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the subscript of the first occurrence of the second argument in the array, or NULL if it's not present. /// If the third argument is given, the search begins at that subscript. This function omits the third argument. /// See [array_position_with_subscript]. @@ -1274,14 +1272,12 @@ define_sql_function! { /// # Ok(()) /// # } /// + #[cfg(feature = "postgres_backend")] fn array_position + SingleValue, E: SingleValue>( a: Arr, elem: E, ) -> Nullable; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the subscript of the first occurrence of the second argument in the array, /// or NULL if it's not present, beginning at the subscript given as the third argument. /// @@ -1324,6 +1320,7 @@ define_sql_function! { /// # } /// #[sql_name = "array_position"] + #[cfg(feature = "postgres_backend")] fn array_position_with_subscript< Arr: ArrayOrNullableArray + SingleValue, E: SingleValue, @@ -1332,10 +1329,7 @@ define_sql_function! { elem: E, subscript: Integer, ) -> Nullable; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns an array of the subscripts of all occurrences of the second argument in the /// array given as first argument. /// @@ -1376,14 +1370,15 @@ define_sql_function! { /// # Ok(()) /// # } /// - fn array_positions + SingleValue + MaybeNullableValue>, E: SingleValue>( + #[cfg(feature = "postgres_backend")] + fn array_positions< + Arr: ArrayOrNullableArray + SingleValue + MaybeNullableValue>, + E: SingleValue, + >( a: Arr, elem: E, ) -> Arr::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the number of dimensions of the array /// /// # Example @@ -1412,11 +1407,11 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_ndims>(arr: Arr) -> Arr::Out; -} + #[cfg(feature = "postgres_backend")] + fn array_ndims>( + arr: Arr, + ) -> Arr::Out; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the upper bound of the requested array /// /// This function returns null for dimensions that do not exist @@ -1445,11 +1440,12 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_upper(array: Arr, dimension: Integer) -> Nullable; -} + #[cfg(feature = "postgres_backend")] + fn array_upper( + array: Arr, + dimension: Integer, + ) -> Nullable; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Randomly shuffles the first dimension of the array. /// /// # Example @@ -1475,11 +1471,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn array_shuffle(array: Arr) -> Arr; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns an array of n items randomly selected from array. /// n may not exceed the length of the array. /// @@ -1517,11 +1511,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn array_sample(array: Arr, n: Integer) -> Arr; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Converts any Array to json. /// /// # Example @@ -1558,13 +1550,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn array_to_json>( - array: Arr, - ) -> Arr::Out; -} + #[cfg(feature = "postgres_backend")] + fn array_to_json>(array: Arr) -> Arr::Out; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Converts any SQL value to json /// /// # Example @@ -1606,11 +1594,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn to_json>(e: E) -> E::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Converts any SQL value to jsonb /// /// # Example @@ -1652,11 +1638,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn to_jsonb>(e: E) -> E::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Builds a JSON object out of a text array. The array must have an even number of members, /// in which case they are taken as alternating key/value pairs /// @@ -1698,13 +1682,11 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn json_object>( text_array: Arr, ) -> Arr::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// This form of json_object takes keys and values pairwise from two separate arrays. /// In all other respects it is identical to the one-argument form. /// @@ -1745,6 +1727,7 @@ define_sql_function! { /// # } /// ``` #[sql_name = "json_object"] + #[cfg(feature = "postgres_backend")] fn json_object_with_keys_and_values< Arr1: TextArrayOrNullableTextArray + SingleValue, Arr2: TextArrayOrNullableTextArray + CombinedNullableValue, @@ -1752,10 +1735,7 @@ define_sql_function! { keys: Arr1, values: Arr2, ) -> Arr2::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the type of the top-level json value as a text-string /// /// # Example @@ -1811,11 +1791,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn json_typeof>(e: E) -> E::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the type of the top-level jsonb value as a text-string /// /// # Example @@ -1871,11 +1849,11 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn jsonb_typeof>(e: E) -> E::Out; -} + #[cfg(feature = "postgres_backend")] + fn jsonb_typeof>( + e: E, + ) -> E::Out; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Converts the given json value to pretty-printed, indented text /// /// # Example @@ -1948,10 +1926,11 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn jsonb_pretty>(e: E) -> E::Out; -} + #[cfg(feature = "postgres_backend")] + fn jsonb_pretty>( + e: E, + ) -> E::Out; -define_sql_function! { /// Deletes all object fields that have null values from the given JSON value, recursively. /// /// # Example @@ -1994,10 +1973,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn json_strip_nulls(json: E) -> E; -} -define_sql_function! { /// Deletes all object fields that have null values from the given JSON value, recursively. /// /// # Example @@ -2041,11 +2019,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn jsonb_strip_nulls(jsonb: E) -> E; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the number of elements in the top-level JSON array /// /// @@ -2083,12 +2059,9 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - + #[cfg(feature = "postgres_backend")] fn json_array_length>(json: E) -> E::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns the number of elements in the top-level JSON array /// /// @@ -2126,12 +2099,11 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] + fn jsonb_array_length>( + jsonb: E, + ) -> E::Out; - fn jsonb_array_length>(jsonb: E) -> E::Out; -} - -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Builds a JSON object out of a text array. The array must have an even number of members, /// in which case they are taken as alternating key/value pairs. This function also has a form that /// that takes keys and values as separate text array arguments. @@ -2181,13 +2153,11 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn jsonb_object>( text_array: Arr, ) -> Arr::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// This form of jsonb_object takes keys and values pairwise from two separate arrays. /// In all other respects it is identical to the one-argument form. /// @@ -2228,17 +2198,15 @@ define_sql_function! { /// # } /// ``` #[sql_name = "jsonb_object"] + #[cfg(feature = "postgres_backend")] fn jsonb_object_with_keys_and_values< Arr1: TextArrayOrNullableTextArray + SingleValue, - Arr2: TextArrayOrNullableTextArray + CombinedNullableValue + Arr2: TextArrayOrNullableTextArray + CombinedNullableValue, >( keys: Arr1, - values: Arr2 + values: Arr2, ) -> Arr2::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// This function `row_to_json` takes a Record type as an input and converts it to JSON. /// /// # Example @@ -2278,11 +2246,9 @@ define_sql_function! { /// # } /// ``` #[sql_name = "row_to_json"] + #[cfg(feature = "postgres_backend")] fn row_to_json>(record: R) -> R::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// This function `json_populate_record` takes a Record base and Json as an input and converts it to top-level /// JSON object to a row having the composite type of the base argument. /// @@ -2334,14 +2300,15 @@ define_sql_function! { /// # } /// ``` #[sql_name = "json_populate_record"] + #[cfg(feature = "postgres_backend")] fn json_populate_record< B: RecordOrNullableRecord + SingleValue, - J: JsonOrNullableJson + CombinedAllNullableValue - >(base: B, from_json: J) -> J::Out; -} + J: JsonOrNullableJson + CombinedAllNullableValue, + >( + base: B, + from_json: J, + ) -> J::Out; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// This function `jsonb_populate_record` takes a Record base and Jsonb as an input and converts it to top-level /// JSON object to a row having the composite type of the base argument. /// @@ -2393,14 +2360,15 @@ define_sql_function! { /// # } /// ``` #[sql_name = "jsonb_populate_record"] + #[cfg(feature = "postgres_backend")] fn jsonb_populate_record< B: RecordOrNullableRecord + SingleValue, - J: JsonbOrNullableJsonb + CombinedAllNullableValue - >(base: B, from_json: J) -> J::Out; -} + J: JsonbOrNullableJsonb + CombinedAllNullableValue, + >( + base: B, + from_json: J, + ) -> J::Out; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns target with the item designated by path replaced by new_value, /// or with new_value added if create_if_missing is true (which is the default) /// and the item designated by path does not exist. @@ -2476,8 +2444,13 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` + #[cfg(feature = "postgres_backend")] fn jsonb_set< E: JsonbOrNullableJsonb + SingleValue, - Arr: TextArrayOrNullableTextArray + CombinedNullableValue - >(base: E, path: Arr, new_value: E) -> Arr::Out; + Arr: TextArrayOrNullableTextArray + CombinedNullableValue, + >( + base: E, + path: Arr, + new_value: E, + ) -> Arr::Out; } diff --git a/diesel/src/pg/metadata_lookup.rs b/diesel/src/pg/metadata_lookup.rs index 7e09c643fd5d..b352ab95678d 100644 --- a/diesel/src/pg/metadata_lookup.rs +++ b/diesel/src/pg/metadata_lookup.rs @@ -214,4 +214,7 @@ table! { joinable!(pg_type -> pg_namespace(typnamespace)); allow_tables_to_appear_in_same_query!(pg_type, pg_namespace); -define_sql_function! { fn pg_my_temp_schema() -> Oid; } +#[declare_sql_function] +extern "SQL" { + fn pg_my_temp_schema() -> Oid; +} diff --git a/diesel/src/sqlite/connection/mod.rs b/diesel/src/sqlite/connection/mod.rs index 454f00f7b4a3..db727dbe0afe 100644 --- a/diesel/src/sqlite/connection/mod.rs +++ b/diesel/src/sqlite/connection/mod.rs @@ -563,12 +563,25 @@ mod tests { use super::*; use crate::dsl::sql; use crate::prelude::*; - use crate::sql_types::Integer; + use crate::sql_types::{Integer, Text}; fn connection() -> SqliteConnection { SqliteConnection::establish(":memory:").unwrap() } + #[declare_sql_function] + extern "SQL" { + fn fun_case(x: Text) -> Text; + fn my_add(x: Integer, y: Integer) -> Integer; + fn answer() -> Integer; + fn add_counter(x: Integer) -> Integer; + + #[aggregate] + fn my_sum(expr: Integer) -> Integer; + #[aggregate] + fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable; + } + #[test] fn database_serializes_and_deserializes_successfully() { let expected_users = vec![ @@ -604,9 +617,6 @@ mod tests { assert_eq!(expected_users, actual_users); } - use crate::sql_types::Text; - define_sql_function!(fn fun_case(x: Text) -> Text); - #[test] fn register_custom_function() { let connection = &mut connection(); @@ -630,8 +640,6 @@ mod tests { assert_eq!("fOoBaR", mapped_string); } - define_sql_function!(fn my_add(x: Integer, y: Integer) -> Integer); - #[test] fn register_multiarg_function() { let connection = &mut connection(); @@ -641,8 +649,6 @@ mod tests { assert_eq!(Ok(3), added); } - define_sql_function!(fn answer() -> Integer); - #[test] fn register_noarg_function() { let connection = &mut connection(); @@ -661,8 +667,6 @@ mod tests { assert_eq!(Ok(42), answer); } - define_sql_function!(fn add_counter(x: Integer) -> Integer); - #[test] fn register_nondeterministic_function() { let connection = &mut connection(); @@ -678,11 +682,6 @@ mod tests { assert_eq!(Ok((2, 3, 4)), added); } - define_sql_function! { - #[aggregate] - fn my_sum(expr: Integer) -> Integer; - } - #[derive(Default)] struct MySum { sum: i32, @@ -748,11 +747,6 @@ mod tests { assert_eq!(Ok(0), result); } - define_sql_function! { - #[aggregate] - fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable; - } - #[derive(Default)] struct RangeMax { max_value: Option, diff --git a/diesel/src/sqlite/connection/row.rs b/diesel/src/sqlite/connection/row.rs index 44162a310516..55cb59141804 100644 --- a/diesel/src/sqlite/connection/row.rs +++ b/diesel/src/sqlite/connection/row.rs @@ -343,7 +343,10 @@ mod tests { } #[cfg(feature = "returning_clauses_for_sqlite_3_35")] - crate::define_sql_function! {fn sleep(a: diesel::sql_types::Integer) -> diesel::sql_types::Integer} + #[crate::declare_sql_function] + extern "SQL" { + fn sleep(a: diesel::sql_types::Integer) -> diesel::sql_types::Integer; + } #[test] #[cfg(feature = "returning_clauses_for_sqlite_3_35")] diff --git a/diesel/src/sqlite/types/date_and_time/chrono.rs b/diesel/src/sqlite/types/date_and_time/chrono.rs index 5e600e76ecbf..8d80ba77769b 100644 --- a/diesel/src/sqlite/types/date_and_time/chrono.rs +++ b/diesel/src/sqlite/types/date_and_time/chrono.rs @@ -253,9 +253,12 @@ mod tests { use crate::sql_types::{Text, Time, Timestamp, TimestamptzSqlite}; use crate::test_helpers::connection; - define_sql_function!(fn datetime(x: Text) -> Timestamp); - define_sql_function!(fn time(x: Text) -> Time); - define_sql_function!(fn date(x: Text) -> Date); + #[declare_sql_function] + extern "SQL" { + fn datetime(x: Text) -> Timestamp; + fn time(x: Text) -> Time; + 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 5d88ad93688c..d520d452f8a7 100644 --- a/diesel/src/sqlite/types/date_and_time/time.rs +++ b/diesel/src/sqlite/types/date_and_time/time.rs @@ -303,9 +303,12 @@ mod tests { use crate::sql_types::{Text, Time, Timestamp, TimestamptzSqlite}; use crate::test_helpers::connection; - define_sql_function!(fn datetime(x: Text) -> Timestamp); - define_sql_function!(fn time(x: Text) -> Time); - define_sql_function!(fn date(x: Text) -> Date); + #[declare_sql_function] + extern "SQL" { + fn datetime(x: Text) -> Timestamp; + fn time(x: Text) -> Time; + 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 44d8775ac900..12b38bb16a97 100644 --- a/diesel_cli/src/infer_schema_internals/information_schema.rs +++ b/diesel_cli/src/infer_schema_internals/information_schema.rs @@ -31,7 +31,10 @@ impl DefaultSchema for Pg { } #[cfg(feature = "mysql")] -define_sql_function!(fn database() -> VarChar); +#[diesel::declare_sql_function] +extern "SQL" { + 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 0a46e460aad5..8b280f0894e8 100644 --- a/diesel_cli/src/infer_schema_internals/mysql.rs +++ b/diesel_cli/src/infer_schema_internals/mysql.rs @@ -10,9 +10,13 @@ use super::information_schema::DefaultSchema; use super::table_data::TableName; use crate::print_schema::ColumnSorting; -diesel::define_sql_function! { +#[diesel::declare_sql_function] +extern "SQL" { #[sql_name = "NULLIF"] - fn null_if_text(lhs: sql_types::Text, rhs: sql_types::Text) -> sql_types::Nullable + fn null_if_text( + lhs: sql_types::Text, + rhs: sql_types::Text, + ) -> sql_types::Nullable; } pub fn get_table_data( diff --git a/diesel_cli/src/infer_schema_internals/pg.rs b/diesel_cli/src/infer_schema_internals/pg.rs index e991129aa592..2de1d639bd5b 100644 --- a/diesel_cli/src/infer_schema_internals/pg.rs +++ b/diesel_cli/src/infer_schema_internals/pg.rs @@ -8,10 +8,26 @@ use diesel::dsl::AsExprOf; use diesel::expression::AsExpression; use diesel::pg::Pg; use diesel::prelude::*; -use diesel::sql_types::{self, Array, Text}; +use diesel::sql_types; use heck::ToUpperCamelCase; use std::borrow::Cow; +#[diesel::declare_sql_function] +extern "SQL" { + #[aggregate] + fn array_agg(input: sql_types::Text) -> sql_types::Array; + + fn col_description( + table: sql_types::Oid, + column_number: sql_types::BigInt, + ) -> sql_types::Nullable; + + fn obj_description( + oid: sql_types::Oid, + catalog: sql_types::Text, + ) -> sql_types::Nullable; +} + #[tracing::instrument] pub fn determine_column_type( attr: &ColumnInformation, @@ -70,8 +86,6 @@ fn regclass(table: &TableName) -> Regclass> { )) } -diesel::define_sql_function!(fn col_description(table: sql_types::Oid, column_number: sql_types::BigInt) -> sql_types::Nullable); - pub fn get_table_data( conn: &mut PgConnection, table: &TableName, @@ -139,8 +153,6 @@ where } } -define_sql_function!(fn obj_description(oid: sql_types::Oid, catalog: sql_types::Text) -> Nullable); - pub fn get_table_comment( conn: &mut PgConnection, table: &TableName, @@ -166,11 +178,6 @@ mod information_schema { } } -define_sql_function! { - #[aggregate] - fn array_agg(input: diesel::sql_types::Text) -> diesel::sql_types::Array; -} - #[allow(clippy::similar_names)] pub fn load_foreign_key_constraints( connection: &mut PgConnection, @@ -178,17 +185,17 @@ pub fn load_foreign_key_constraints( ) -> QueryResult> { #[derive(QueryableByName)] struct ForeignKeyList { - #[diesel(sql_type = Text)] + #[diesel(sql_type = sql_types::Text)] self_schema: String, - #[diesel(sql_type = Text)] + #[diesel(sql_type = sql_types::Text)] self_table: String, - #[diesel(sql_type = Array)] + #[diesel(sql_type = sql_types::Array)] self_columns: Vec, - #[diesel(sql_type = Text)] + #[diesel(sql_type = sql_types::Text)] foreign_schema: String, - #[diesel(sql_type = Text)] + #[diesel(sql_type = sql_types::Text)] foreign_table: String, - #[diesel(sql_type = Array)] + #[diesel(sql_type = sql_types::Array)] foreign_columns: Vec, } @@ -196,7 +203,7 @@ pub fn load_foreign_key_constraints( let schema_name = schema_name.unwrap_or(&default_schema); diesel::sql_query(include_str!("load_foreign_keys.sql")) - .bind::(schema_name) + .bind::(schema_name) .load_iter::(connection)? .map(|f| { let f = f?; 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 60f7ac63fd9b..a0e0111cb0a5 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,10 @@ table! { } } -define_sql_function!(fn f(x: Nullable, y: Nullable) -> Nullable); +#[declare_sql_function] +extern "SQL" { + fn f(x: Nullable, y: Nullable) -> Nullable; +} 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 ecbb2acdfc34..fad8b9d9aa9a 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 @@ -1,7 +1,7 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggregates` is not satisfied - --> tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs:20:24 + --> tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs:23:24 | -20 | let source = users.select((id, count_star())); +23 | let source = users.select((id, count_star())); | ^^^^^^ the trait `MixedAggregates` is not implemented for `diesel::expression::is_aggregate::No`, which is required by `SelectStatement>: SelectDsl<_>` | = help: the following other types implement trait `MixedAggregates`: @@ -11,9 +11,9 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg = note: required for `SelectStatement>` to implement `SelectDsl<(columns::id, CountStar)>` error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggregates` is not satisfied - --> tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs:22:24 + --> tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs:25:24 | -22 | let source = users.select(nullable_int_col + max(nullable_int_col)); +25 | let source = users.select(nullable_int_col + max(nullable_int_col)); | ^^^^^^ the trait `MixedAggregates` is not implemented for `diesel::expression::is_aggregate::No`, which is required by `SelectStatement>: SelectDsl<_>` | = help: the following other types implement trait `MixedAggregates`: @@ -23,9 +23,9 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg = note: required for `SelectStatement>` to implement `SelectDsl, columns::nullable_int_col>>>` error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggregates` is not satisfied - --> tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs:24:24 + --> tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs:27:24 | -24 | let source = users.select(f(nullable_int_col, max(nullable_int_col))); +27 | let source = users.select(f(nullable_int_col, max(nullable_int_col))); | ^^^^^^ the trait `MixedAggregates` is not implemented for `diesel::expression::is_aggregate::No`, which is required by `SelectStatement>: SelectDsl<_>` | = help: the following other types implement trait `MixedAggregates`: @@ -34,9 +34,9 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg note: required for `__Derived, columns::nullable_int_col>>` to implement `ValidGrouping<()>` --> tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.rs:14:1 | -14 | define_sql_function!(fn f(x: Nullable, y: Nullable) -> Nullable); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro +14 | #[declare_sql_function] + | ^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro = note: 1 redundant requirement hidden = note: required for `f_utils::f, columns::nullable_int_col>>` to implement `ValidGrouping<()>` = note: required for `SelectStatement>` to implement `SelectDsl, columns::nullable_int_col>>>` - = note: this error originates in the derive macro `ValidGrouping` which comes from the expansion of the macro `define_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the derive macro `ValidGrouping` which comes from the expansion of the attribute macro `declare_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr b/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr index cb1cb547e6b2..53acf55fb277 100644 --- a/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr +++ b/diesel_compile_tests/tests/fail/numeric_ops_require_numeric_column.stderr @@ -13,3 +13,6 @@ note: an implementation of `std::ops::Add` might be missing for `columns::name` | ^^^^ must implement `std::ops::Add` note: the trait `std::ops::Add` must be implemented --> $RUST/core/src/ops/arith.rs + | + | pub trait Add { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ 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 ae7635184543..8ece381133c1 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,10 @@ table! { #[diesel(table_name = users)] pub struct NewUser(#[diesel(column_name = name)] &'static str); -define_sql_function!(fn lower(x: diesel::sql_types::Text) -> diesel::sql_types::Text); +#[declare_sql_function] +extern "SQL" { + 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 4a9b53401f22..7fa3b049207c 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 @@ -1,7 +1,7 @@ error[E0271]: type mismatch resolving `::Table == table` - --> tests/fail/pg_on_conflict_requires_valid_conflict_target.rs:38:22 + --> tests/fail/pg_on_conflict_requires_valid_conflict_target.rs:41:22 | -38 | .on_conflict(posts::id); +41 | .on_conflict(posts::id); | ----------- ^^^^^^^^^ type mismatch resolving `::Table == table` | | | required by a bound introduced by this call @@ -43,9 +43,9 @@ note: required by a bound in `upsert::on_conflict_extension::: Column` is not satisfied - --> tests/fail/pg_on_conflict_requires_valid_conflict_target.rs:42:22 + --> tests/fail/pg_on_conflict_requires_valid_conflict_target.rs:45:22 | -42 | .on_conflict(lower(posts::title)); +45 | .on_conflict(lower(posts::title)); | ----------- ^^^^^^^^^^^^^^^^^^^ the trait `Column` is not implemented for `lower_utils::lower`, which is required by `diesel::query_builder::upsert::on_conflict_target::ConflictTarget>: diesel::query_builder::upsert::on_conflict_target::OnConflictTarget` | | | required by a bound introduced by this call @@ -71,9 +71,9 @@ note: required by a bound in `upsert::on_conflict_extension::>::on_conflict` error[E0277]: the trait bound `&str: Column` is not satisfied - --> tests/fail/pg_on_conflict_requires_valid_conflict_target.rs:46:22 + --> tests/fail/pg_on_conflict_requires_valid_conflict_target.rs:49:22 | -46 | .on_conflict("id"); +49 | .on_conflict("id"); | ----------- ^^^^ the trait `Column` is not implemented for `&str`, which is required by `diesel::query_builder::upsert::on_conflict_target::ConflictTarget<&str>: diesel::query_builder::upsert::on_conflict_target::OnConflictTarget` | | | required by a bound introduced by this call 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 b1f107af0597..22aa926109ce 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,10 @@ table! { } } -define_sql_function!(fn lower(x: VarChar) -> VarChar); +#[declare_sql_function] +extern "SQL" { + fn lower(x: VarChar) -> VarChar; +} #[derive(Insertable)] #[diesel(table_name = users)] 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 65d36905638f..4dcaa4c022aa 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:28:25 + --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:31:25 | -28 | .filter(name.eq(any(Vec::::new()))) +31 | .filter(name.eq(any(Vec::::new()))) | ^^^ | = note: `#[warn(deprecated)]` on by default error[E0277]: `diesel::pg::expression::array_comparison::Any, Vec>>` is no valid SQL fragment for the `Sqlite` backend - --> 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:32:22 | -29 | .load::(&mut connection); +32 | .load::(&mut connection); | ---- ^^^^^^^^^^^^^^^ the trait `QueryFragment` is not implemented for `diesel::pg::expression::array_comparison::Any, Vec>>`, which is required by `SelectStatement, diesel::query_builder::select_clause::SelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause, Vec>>>>>>: LoadQuery<'_, _, i32>` | | | required by a bound introduced by this call @@ -32,9 +32,9 @@ note: required by a bound in `diesel::RunQueryDsl::load` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::load` error[E0277]: `diesel::pg::expression::operators::IsNotDistinctFrom>` is no valid SQL fragment for the `Sqlite` backend - --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:33:22 + --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:36:22 | -33 | .load::(&mut connection); +36 | .load::(&mut connection); | ---- ^^^^^^^^^^^^^^^ the trait `QueryFragment` is not implemented for `diesel::pg::expression::operators::IsNotDistinctFrom>`, which is required by `SelectStatement, diesel::query_builder::select_clause::SelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>: LoadQuery<'_, _, i32>` | | | required by a bound introduced by this call @@ -57,9 +57,9 @@ note: required by a bound in `diesel::RunQueryDsl::load` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::load` error[E0277]: `diesel::pg::expression::date_and_time::AtTimeZone>` is no valid SQL fragment for the `Sqlite` backend - --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:37:22 + --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:40:22 | -37 | .load::(&mut connection); +40 | .load::(&mut connection); | ---- ^^^^^^^^^^^^^^^ the trait `QueryFragment` is not implemented for `diesel::pg::expression::date_and_time::AtTimeZone>`, which is required by `SelectStatement, diesel::query_builder::select_clause::SelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>>>: LoadQuery<'_, _, i32>` | | | required by a bound introduced by this call @@ -82,12 +82,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>>,), table>>, ConflictTarget>>`, but its trait bounds were not satisfied - --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:41:10 + --> tests/fail/pg_specific_expressions_cant_be_used_in_a_sqlite_query.rs:44:10 | -38 | / insert_into(users) -39 | | .values(&NewUser("Sean")) -40 | | .on_conflict(on_constraint("name")) -41 | | .execute(&mut connection); +41 | / insert_into(users) +42 | | .values(&NewUser("Sean")) +43 | | .on_conflict(on_constraint("name")) +44 | | .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 812d2f1f9d8b..5f11f3f6361a 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 @@ -29,7 +29,11 @@ table! { joinable!(posts -> users (user_id)); joinable!(pets -> users (user_id)); allow_tables_to_appear_in_same_query!(posts, users, pets); -define_sql_function!(fn lower(x: Text) -> Text); + +#[declare_sql_function] +extern "SQL" { + 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 f714e9d12fc9..bd5fca31c496 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 @@ -1,7 +1,7 @@ error[E0271]: type mismatch resolving `>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:40:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:44:18 | -40 | let _ = join.select(posts::title); +44 | let _ = join.select(posts::title); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression>` @@ -19,9 +19,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0277]: Cannot select `posts::columns::title` from `users::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:40:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:44:18 | -40 | let _ = join.select(posts::title); +44 | let _ = join.select(posts::title); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `users::table` @@ -43,9 +43,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0271]: type mismatch resolving `
>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:46:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:50:18 | -46 | let _ = join.select(lower(posts::title)); +50 | let _ = join.select(lower(posts::title)); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression>` @@ -63,9 +63,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0277]: Cannot select `posts::columns::title` from `users::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:46:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:50:18 | -46 | let _ = join.select(lower(posts::title)); +50 | let _ = join.select(lower(posts::title)); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `users::table` @@ -87,9 +87,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:48:31 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:52:31 | -48 | let _ = join.select(lower(posts::title.nullable())); +52 | let _ = join.select(lower(posts::title.nullable())); | ----- ^^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Nullable` | | | required by a bound introduced by this call @@ -98,16 +98,19 @@ error[E0271]: type mismatch resolving ` as Expression>::SqlType found struct `Nullable` = note: required for `NullableExpression` to implement `AsExpression` note: required by a bound in `lower` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:32:1 - | -32 | 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) + --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1 + | +33 | #[declare_sql_function] + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower` +34 | extern "SQL" { +35 | fn lower(x: Text) -> Text; + | ----- required by a bound in this function + = note: this error originates in the attribute macro `declare_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:48:25 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:52:25 | -48 | let _ = join.select(lower(posts::title.nullable())); +52 | let _ = join.select(lower(posts::title.nullable())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Nullable`, found `Text` | = note: expected struct `Nullable` @@ -115,9 +118,9 @@ error[E0271]: type mismatch resolving ` as Expression>::SqlType = note: required for `NullableExpression` to implement `AsExpression` error[E0271]: type mismatch resolving `
>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:57:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:61:18 | -57 | let _ = join.select(posts::title); +61 | let _ = join.select(posts::title); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression>` @@ -135,9 +138,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0277]: Cannot select `posts::columns::title` from `users::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:57:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:61:18 | -57 | let _ = join.select(posts::title); +61 | let _ = join.select(posts::title); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `users::table` @@ -159,9 +162,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0271]: type mismatch resolving `
>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:63:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:67:18 | -63 | let _ = join.select(lower(posts::title)); +67 | let _ = join.select(lower(posts::title)); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression>` @@ -179,9 +182,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0277]: Cannot select `posts::columns::title` from `users::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:63:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:67:18 | -63 | let _ = join.select(lower(posts::title)); +67 | let _ = join.select(lower(posts::title)); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `users::table` @@ -203,9 +206,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:65:31 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:69:31 | -65 | let _ = join.select(lower(posts::title.nullable())); +69 | let _ = join.select(lower(posts::title.nullable())); | ----- ^^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Nullable` | | | required by a bound introduced by this call @@ -214,16 +217,19 @@ error[E0271]: type mismatch resolving ` as Expression>::SqlType found struct `Nullable` = note: required for `NullableExpression` to implement `AsExpression` note: required by a bound in `lower` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:32:1 - | -32 | 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) + --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1 + | +33 | #[declare_sql_function] + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower` +34 | extern "SQL" { +35 | fn lower(x: Text) -> Text; + | ----- required by a bound in this function + = note: this error originates in the attribute macro `declare_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:65:25 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:69:25 | -65 | let _ = join.select(lower(posts::title.nullable())); +69 | let _ = join.select(lower(posts::title.nullable())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Nullable`, found `Text` | = note: expected struct `Nullable` @@ -231,9 +237,9 @@ error[E0271]: type mismatch resolving ` as Expression>::SqlType = note: required for `NullableExpression` to implement `AsExpression` error[E0271]: type mismatch resolving `
>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:74:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:78:18 | -74 | let _ = join.select(posts::title); +78 | let _ = join.select(posts::title); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression>` @@ -251,9 +257,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0277]: Cannot select `posts::columns::title` from `users::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:74:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:78:18 | -74 | let _ = join.select(posts::title); +78 | let _ = join.select(posts::title); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `users::table` @@ -275,9 +281,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0271]: type mismatch resolving `
>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:80:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:84:18 | -80 | let _ = join.select(lower(posts::title)); +84 | let _ = join.select(lower(posts::title)); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression>` @@ -295,9 +301,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0277]: Cannot select `posts::columns::title` from `users::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:80:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:84:18 | -80 | let _ = join.select(lower(posts::title)); +84 | let _ = join.select(lower(posts::title)); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `users::table` @@ -319,9 +325,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>, pets::table, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:82:31 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:86:31 | -82 | let _ = join.select(lower(posts::title.nullable())); +86 | let _ = join.select(lower(posts::title.nullable())); | ----- ^^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Nullable` | | | required by a bound introduced by this call @@ -330,16 +336,19 @@ error[E0271]: type mismatch resolving ` as Expression>::SqlType found struct `Nullable` = note: required for `NullableExpression` to implement `AsExpression` note: required by a bound in `lower` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:32:1 - | -32 | 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) + --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1 + | +33 | #[declare_sql_function] + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower` +34 | extern "SQL" { +35 | fn lower(x: Text) -> Text; + | ----- required by a bound in this function + = note: this error originates in the attribute macro `declare_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:82:25 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:86:25 | -82 | let _ = join.select(lower(posts::title.nullable())); +86 | let _ = join.select(lower(posts::title.nullable())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Nullable`, found `Text` | = note: expected struct `Nullable` @@ -347,9 +356,9 @@ error[E0271]: type mismatch resolving ` as Expression>::SqlType = note: required for `NullableExpression` to implement `AsExpression` error[E0271]: type mismatch resolving `, Grouped, ...>>>>> as AppearsInFromClause<...>>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:89:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:93:18 | -89 | let _ = join.select(posts::title); +93 | let _ = join.select(posts::title); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, LeftOuter>>` @@ -367,9 +376,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0277]: Cannot select `posts::columns::title` from `pets::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:89:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:93:18 | -89 | let _ = join.select(posts::title); +93 | let _ = join.select(posts::title); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>>>, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `pets::table` @@ -391,9 +400,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0271]: type mismatch resolving `, Grouped, ...>>>>> as AppearsInFromClause<...>>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:95:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:99:18 | -95 | let _ = join.select(lower(posts::title)); +99 | let _ = join.select(lower(posts::title)); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, LeftOuter>>` @@ -411,9 +420,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0277]: Cannot select `posts::columns::title` from `pets::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:95:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:99:18 | -95 | let _ = join.select(lower(posts::title)); +99 | let _ = join.select(lower(posts::title)); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>>>, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `pets::table` @@ -435,37 +444,40 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, LeftOuter>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:97:31 - | -97 | let _ = join.select(lower(posts::title.nullable())); - | ----- ^^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Nullable` - | | - | required by a bound introduced by this call - | - = note: expected struct `diesel::sql_types::Text` - found struct `Nullable` - = note: required for `NullableExpression` to implement `AsExpression` + --> tests/fail/right_side_of_left_join_requires_nullable.rs:101:31 + | +101 | let _ = join.select(lower(posts::title.nullable())); + | ----- ^^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Nullable` + | | + | required by a bound introduced by this call + | + = note: expected struct `diesel::sql_types::Text` + found struct `Nullable` + = note: required for `NullableExpression` to implement `AsExpression` note: required by a bound in `lower` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:32:1 - | -32 | 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) + --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1 + | +33 | #[declare_sql_function] + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower` +34 | extern "SQL" { +35 | fn lower(x: Text) -> Text; + | ----- required by a bound in this function + = note: this error originates in the attribute macro `declare_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:97:25 - | -97 | let _ = join.select(lower(posts::title.nullable())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Nullable`, found `Text` - | - = note: expected struct `Nullable` - found struct `diesel::sql_types::Text` - = note: required for `NullableExpression` to implement `AsExpression` + --> tests/fail/right_side_of_left_join_requires_nullable.rs:101:25 + | +101 | let _ = join.select(lower(posts::title.nullable())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Nullable`, found `Text` + | + = note: expected struct `Nullable` + found struct `diesel::sql_types::Text` + = note: required for `NullableExpression` to implement `AsExpression` error[E0271]: type mismatch resolving `
>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:104:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:108:18 | -104 | let _ = join.select(posts::title); +108 | let _ = join.select(posts::title); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression>` @@ -483,9 +495,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0277]: Cannot select `posts::columns::title` from `users::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:104:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:108:18 | -104 | let _ = join.select(posts::title); +108 | let _ = join.select(posts::title); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>>>, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `users::table` @@ -507,9 +519,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl` error[E0271]: type mismatch resolving `
>::Count == Never` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:110:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:114:18 | -110 | let _ = join.select(lower(posts::title)); +114 | let _ = join.select(lower(posts::title)); | ^^^^^^ expected `Once`, found `Never` | note: required for `posts::columns::title` to implement `SelectableExpression>` @@ -527,9 +539,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0277]: Cannot select `posts::columns::title` from `users::table` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:110:18 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:114:18 | -110 | let _ = join.select(lower(posts::title)); +114 | let _ = join.select(lower(posts::title)); | ^^^^^^ the trait `SelectableExpression` is not implemented for `posts::columns::title`, which is required by `SelectStatement, diesel::expression::grouped::Grouped, NullableExpression>>>>>, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>: SelectDsl<_>` | = note: `posts::columns::title` is no valid selection for `users::table` @@ -551,9 +563,9 @@ note: required for `posts::columns::title` to implement `SelectableExpression, diesel::expression::grouped::Grouped, NullableExpression>>>>>, Inner>, diesel::expression::grouped::Grouped, NullableExpression>>>>>` to implement `SelectDsl>` error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:112:31 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:116:31 | -112 | let _ = join.select(lower(posts::title.nullable())); +116 | let _ = join.select(lower(posts::title.nullable())); | ----- ^^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Nullable` | | | required by a bound introduced by this call @@ -562,16 +574,19 @@ error[E0271]: type mismatch resolving ` as Expression>::SqlType found struct `Nullable` = note: required for `NullableExpression` to implement `AsExpression` note: required by a bound in `lower` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:32:1 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:33:1 | -32 | 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) +33 | #[declare_sql_function] + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lower` +34 | extern "SQL" { +35 | fn lower(x: Text) -> Text; + | ----- required by a bound in this function + = note: this error originates in the attribute macro `declare_sql_function` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving ` as Expression>::SqlType == Text` - --> tests/fail/right_side_of_left_join_requires_nullable.rs:112:25 + --> tests/fail/right_side_of_left_join_requires_nullable.rs:116:25 | -112 | let _ = join.select(lower(posts::title.nullable())); +116 | let _ = join.select(lower(posts::title.nullable())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Nullable`, found `Text` | = note: expected struct `Nullable` 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 a732b50e4c7e..68b9670bc010 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 @@ -25,8 +25,11 @@ struct User { name: String, } -define_sql_function!(fn foo(x: Integer) -> Integer); -define_sql_function!(fn bar(x: VarChar) -> VarChar); +#[declare_sql_function] +extern "SQL" { + fn foo(x: Integer) -> Integer; + fn bar(x: VarChar) -> VarChar; +} fn main() { use self::posts::title; 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 b5633720fe2d..ae979d7f0013 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 @@ -1,15 +1,15 @@ error[E0271]: type mismatch resolving `> as Expression>::SqlType == Text` - --> tests/fail/user_defined_functions_follow_same_selection_rules.rs:37:38 + --> tests/fail/user_defined_functions_follow_same_selection_rules.rs:40:38 | -37 | let _ = users::table.filter(name.eq(foo(1))); +40 | let _ = users::table.filter(name.eq(foo(1))); | ^^ expected `Integer`, found `Text` | = note: required for `foo_utils::foo>` to implement `AsExpression` error[E0271]: type mismatch resolving `
>::Count == Once` - --> tests/fail/user_defined_functions_follow_same_selection_rules.rs:41:23 + --> tests/fail/user_defined_functions_follow_same_selection_rules.rs:44:23 | -41 | .load::(&mut conn); +44 | .load::(&mut conn); | ---- ^^^^^^^^^ expected `Once`, found `Never` | | | required by a bound introduced by this call diff --git a/diesel_derives/src/lib.rs b/diesel_derives/src/lib.rs index 3b53aca5527b..8d211544a93f 100644 --- a/diesel_derives/src/lib.rs +++ b/diesel_derives/src/lib.rs @@ -23,6 +23,7 @@ extern crate quote; extern crate syn; use proc_macro::TokenStream; +use sql_function::ExternSqlBlock; use syn::{parse_macro_input, parse_quote}; mod attrs; @@ -1007,908 +1008,964 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream { .into() } -/// Declare a sql function for use in your code. -/// -/// Diesel only provides support for a very small number of SQL functions. -/// This macro enables you to add additional functions from the SQL standard, -/// as well as any custom functions your application might have. +/// A legacy version of [`#[declare_sql_function]`] that uses +/// a function like macro instead of a proc-macro attribute. +/// It otherwise support the same function declaration syntax +/// as the proc macro attribute and will generate the same code. /// -/// The syntax for this macro is very similar to that of a normal Rust function, -/// except the argument and return types will be the SQL types being used. -/// Typically, these types will come from [`diesel::sql_types`](../diesel/sql_types/index.html) +/// ```ignore +/// define_sql_function!(fn lower(x: Text) -> Text); +/// ``` +#[deprecated(since = "2.2.0", note = "Use [`define_sql_function`] instead")] +#[proc_macro] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +pub fn define_sql_function(input: TokenStream) -> TokenStream { + sql_function::expand(parse_macro_input!(input), false).into() +} + +/// A legacy version of [`define_sql_function!`]. /// -/// This macro will generate two items. A function with the name that you've -/// given, and a module with a helper type representing the return type of your -/// function. For example, this invocation: +/// The difference is that it makes the helper type available in a module named the exact same as +/// the function: /// /// ```ignore -/// define_sql_function!(fn lower(x: Text) -> Text); +/// sql_function!(fn lower(x: Text) -> Text); /// ``` /// /// will generate this code: /// /// ```ignore -/// pub fn lower(x: X) -> lower { +/// pub fn lower(x: X) -> lower::HelperType { /// ... /// } /// -/// pub type lower = ...; +/// pub(crate) mod lower { +/// pub type HelperType = ...; +/// } /// ``` /// -/// Most attributes given to this macro will be put on the generated function -/// (including doc comments). +/// 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). /// -/// # Adding Doc Comments +/// 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), true).into() +} + +/// This is an internal diesel macro that +/// helps to implement all traits for tuples of +/// various sizes +#[doc(hidden)] +#[proc_macro] +pub fn __diesel_for_each_tuple(input: TokenStream) -> TokenStream { + diesel_for_each_tuple::expand(parse_macro_input!(input)).into() +} + +/// This is an internal diesel macro that +/// helps to restrict the visibility of an item based +/// on a feature flag +#[doc(hidden)] +#[proc_macro_attribute] +pub fn __diesel_public_if(attrs: TokenStream, input: TokenStream) -> TokenStream { + diesel_public_if::expand(parse_macro_input!(attrs), parse_macro_input!(input)).into() +} + +/// Specifies that a table exists, and what columns it has. This will create a +/// new public module, with the same name, as the name of the table. In this +/// module, you will find a unit struct named `table`, and a unit struct with the +/// name of each column. /// -/// ```no_run +/// By default, this allows a maximum of 32 columns per table. +/// You can increase this limit to 64 by enabling the `64-column-tables` feature. +/// You can increase it to 128 by enabling the `128-column-tables` feature. +/// You can decrease it to 16 columns, +/// which improves compilation time, +/// by disabling the default features of Diesel. +/// Note that enabling 64 column tables or larger will substantially increase +/// the compile time of Diesel. +/// +/// Example usage +/// ------------- +/// +/// ```rust /// # extern crate diesel; -/// # use diesel::*; -/// # -/// # table! { crates { id -> Integer, name -> VarChar, } } -/// # -/// use diesel::sql_types::Text; /// -/// define_sql_function! { -/// /// Represents the `canon_crate_name` SQL function, created in -/// /// migration .... -/// fn canon_crate_name(a: Text) -> Text; +/// diesel::table! { +/// users { +/// id -> Integer, +/// name -> VarChar, +/// favorite_color -> Nullable, +/// } /// } -/// -/// # fn main() { -/// # use self::crates::dsl::*; -/// let target_name = "diesel"; -/// crates.filter(canon_crate_name(name).eq(canon_crate_name(target_name))); -/// // This will generate the following SQL -/// // SELECT * FROM crates WHERE canon_crate_name(crates.name) = canon_crate_name($1) -/// # } /// ``` /// -/// # Special Attributes +/// You may also specify a primary key if it is called something other than `id`. +/// Tables with no primary key aren't supported. /// -/// There are a handful of special attributes that Diesel will recognize. They -/// are: +/// ```rust +/// # extern crate diesel; /// -/// - `#[aggregate]` -/// - Indicates that this is an aggregate function, and that `NonAggregate` -/// shouldn't be implemented. -/// - `#[sql_name = "name"]` -/// - The SQL to be generated is different from the Rust name of the function. -/// This can be used to represent functions which can take many argument -/// types, or to capitalize function names. +/// diesel::table! { +/// users (non_standard_primary_key) { +/// non_standard_primary_key -> Integer, +/// name -> VarChar, +/// favorite_color -> Nullable, +/// } +/// } +/// ``` /// -/// Functions can also be generic. Take the definition of `sum`, for example: +/// For tables with composite primary keys, list all the columns in the primary key. /// -/// ```no_run +/// ```rust /// # extern crate diesel; -/// # use diesel::*; -/// # -/// # table! { crates { id -> Integer, name -> VarChar, } } -/// # -/// use diesel::sql_types::Foldable; /// -/// define_sql_function! { -/// #[aggregate] -/// #[sql_name = "SUM"] -/// fn sum(expr: ST) -> ST::Sum; +/// diesel::table! { +/// followings (user_id, post_id) { +/// user_id -> Integer, +/// post_id -> Integer, +/// favorited -> Bool, +/// } /// } -/// /// # fn main() { -/// # use self::crates::dsl::*; -/// crates.select(sum(id)); +/// # use diesel::prelude::Table; +/// # use self::followings::dsl::*; +/// # // Poor man's assert_eq! -- since this is type level this would fail +/// # // to compile if the wrong primary key were generated +/// # let (user_id {}, post_id {}) = followings.primary_key(); /// # } /// ``` /// -/// # SQL Functions without Arguments -/// -/// A common example is ordering a query using the `RANDOM()` sql function, -/// which can be implemented using `define_sql_function!` like this: +/// If you are using types that aren't from Diesel's core types, you can specify +/// which types to import. /// -/// ```rust +/// ``` /// # extern crate diesel; -/// # use diesel::*; -/// # -/// # table! { crates { id -> Integer, name -> VarChar, } } -/// # -/// define_sql_function!(fn random() -> Text); -/// -/// # fn main() { -/// # use self::crates::dsl::*; -/// crates.order(random()); +/// # mod diesel_full_text_search { +/// # #[derive(diesel::sql_types::SqlType)] +/// # pub struct TsVector; /// # } -/// ``` /// -/// # Use with SQLite +/// diesel::table! { +/// use diesel::sql_types::*; +/// # use crate::diesel_full_text_search::*; +/// # /* +/// use diesel_full_text_search::*; +/// # */ /// -/// 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` (in the generated function's `_internals` -/// module) with every connection before you can use the function. +/// posts { +/// id -> Integer, +/// title -> Text, +/// keywords -> TsVector, +/// } +/// } +/// # fn main() {} +/// ``` /// -/// These functions will only be generated if the `sqlite` feature is enabled, -/// and the function is not generic. -/// SQLite doesn't support generic functions and variadic functions. +/// If you want to add documentation to the generated code, you can use the +/// following syntax: /// -/// ```rust +/// ``` /// # extern crate diesel; -/// # use diesel::*; -/// # -/// # #[cfg(feature = "sqlite")] -/// # fn main() { -/// # run_test().unwrap(); -/// # } -/// # -/// # #[cfg(not(feature = "sqlite"))] -/// # fn main() { -/// # } -/// # -/// use diesel::sql_types::{Integer, Double}; -/// define_sql_function!(fn add_mul(x: Integer, y: Integer, z: Double) -> Double); /// -/// # #[cfg(feature = "sqlite")] -/// # fn run_test() -> Result<(), Box> { -/// let connection = &mut SqliteConnection::establish(":memory:")?; +/// diesel::table! { +/// /// The table containing all blog posts +/// posts { +/// /// The post's unique id +/// id -> Integer, +/// /// The post's title +/// title -> Text, +/// } +/// } +/// ``` /// -/// add_mul_utils::register_impl(connection, |x: i32, y: i32, z: f64| { -/// (x + y) as f64 * z -/// })?; +/// If you have a column with the same name as a Rust reserved keyword, you can use +/// the `sql_name` attribute like this: /// -/// let result = select(add_mul(1, 2, 1.5)) -/// .get_result::(connection)?; -/// assert_eq!(4.5, result); -/// # Ok(()) -/// # } /// ``` +/// # extern crate diesel; /// -/// ## Panics +/// diesel::table! { +/// posts { +/// id -> Integer, +/// /// This column is named `mytype` but references the table `type` column. +/// #[sql_name = "type"] +/// mytype -> Text, +/// } +/// } +/// ``` /// -/// If an implementation of the custom function panics and unwinding is enabled, the panic is -/// caught and the function returns to libsqlite with an error. It can't propagate the panics due -/// to the FFI boundary. +/// This module will also contain several helper types: /// -/// This is the same for [custom aggregate functions](#custom-aggregate-functions). +/// dsl +/// --- /// -/// ## Custom Aggregate Functions +/// This simply re-exports the table, renamed to the same name as the module, +/// and each of the columns. This is useful to glob import when you're dealing +/// primarily with one table, to allow writing `users.filter(name.eq("Sean"))` +/// instead of `users::table.filter(users::name.eq("Sean"))`. /// -/// Custom aggregate functions can be created in SQLite by adding an `#[aggregate]` -/// 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. -/// -/// ```rust -/// # extern crate diesel; -/// # use diesel::*; -/// # -/// # #[cfg(feature = "sqlite")] -/// # fn main() { -/// # run().unwrap(); -/// # } -/// # -/// # #[cfg(not(feature = "sqlite"))] -/// # fn main() { -/// # } -/// use diesel::sql_types::Integer; -/// # #[cfg(feature = "sqlite")] -/// use diesel::sqlite::SqliteAggregateFunction; +/// `all_columns` +/// ----------- /// -/// define_sql_function! { -/// #[aggregate] -/// fn my_sum(x: Integer) -> Integer; -/// } +/// A constant will be assigned called `all_columns`. This is what will be +/// selected if you don't otherwise specify a select clause. It's type will be +/// `table::AllColumns`. You can also get this value from the +/// `Table::all_columns` function. /// -/// #[derive(Default)] -/// struct MySum { sum: i32 } +/// star +/// ---- /// -/// # #[cfg(feature = "sqlite")] -/// impl SqliteAggregateFunction for MySum { -/// type Output = i32; +/// This will be the qualified "star" expression for this table (e.g. +/// `users.*`). Internally, we read columns by index, not by name, so this +/// column is not safe to read data out of, and it has had its SQL type set to +/// `()` to prevent accidentally using it as such. It is sometimes useful for +/// counting statements, however. It can also be accessed through the `Table.star()` +/// method. /// -/// fn step(&mut self, expr: i32) { -/// self.sum += expr; -/// } +/// `SqlType` +/// ------- /// -/// fn finalize(aggregator: Option) -> Self::Output { -/// aggregator.map(|a| a.sum).unwrap_or_default() -/// } -/// } -/// # table! { -/// # players { -/// # id -> Integer, -/// # score -> Integer, -/// # } -/// # } +/// A type alias called `SqlType` will be created. It will be the SQL type of +/// `all_columns`. The SQL type is needed for things like returning boxed +/// queries. /// -/// # #[cfg(feature = "sqlite")] -/// fn run() -> Result<(), Box> { -/// # use self::players::dsl::*; -/// let connection = &mut SqliteConnection::establish(":memory:")?; -/// # diesel::sql_query("create table players (id integer primary key autoincrement, score integer)") -/// # .execute(connection) -/// # .unwrap(); -/// # diesel::sql_query("insert into players (score) values (10), (20), (30)") -/// # .execute(connection) -/// # .unwrap(); +/// `BoxedQuery` +/// ---------- /// -/// my_sum_utils::register_impl::(connection)?; +/// ```ignore +/// pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, table, DB>; +/// ``` +#[proc_macro] +pub fn table_proc(input: TokenStream) -> TokenStream { + match syn::parse(input) { + Ok(input) => table::expand(input).into(), + Err(_) => quote::quote! { + compile_error!( + "Invalid `table!` syntax. Please see the `table!` macro docs for more info.\n\ + Docs available at: `https://docs.diesel.rs/master/diesel/macro.table.html`\n" + ); + } + .into(), + } +} + +/// This derives implements `diesel::Connection` and related traits for an enum of +/// connections to different databases. /// -/// let total_score = players.select(my_sum(score)) -/// .get_result::(connection)?; +/// By applying this derive to such an enum, you can use the enum as a connection type in +/// any location all the inner connections are valid. This derive supports enum +/// variants containing a single tuple field. Each tuple field type must implement +/// `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. /// -/// println!("The total score of all the players is: {}", total_score); +/// 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` +/// 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 +/// wrap the result into the enum. /// -/// # assert_eq!(60, total_score); -/// Ok(()) -/// } +/// # Example /// ``` -/// -/// With multiple function arguments, the arguments are passed as a tuple to `SqliteAggregateFunction` -/// -/// ```rust /// # extern crate diesel; -/// # use diesel::*; -/// # -/// # #[cfg(feature = "sqlite")] -/// # fn main() { -/// # run().unwrap(); -/// # } -/// # -/// # #[cfg(not(feature = "sqlite"))] -/// # fn main() { -/// # } -/// use diesel::sql_types::{Float, Nullable}; -/// # #[cfg(feature = "sqlite")] -/// use diesel::sqlite::SqliteAggregateFunction; +/// # use diesel::result::QueryResult; +/// use diesel::prelude::*; /// -/// define_sql_function! { -/// #[aggregate] -/// fn range_max(x0: Float, x1: Float) -> Nullable; +/// #[derive(diesel::MultiConnection)] +/// pub enum AnyConnection { +/// # #[cfg(feature = "postgres")] +/// Postgresql(diesel::PgConnection), +/// # #[cfg(feature = "mysql")] +/// Mysql(diesel::MysqlConnection), +/// # #[cfg(feature = "sqlite")] +/// Sqlite(diesel::SqliteConnection), /// } /// -/// #[derive(Default)] -/// struct RangeMax { max_value: Option } -/// -/// # #[cfg(feature = "sqlite")] -/// impl SqliteAggregateFunction<(T, T)> for RangeMax { -/// type Output = Option; -/// -/// fn step(&mut self, (x0, x1): (T, T)) { -/// # let max = if x0 >= x1 { -/// # x0 -/// # } else { -/// # x1 -/// # }; -/// # -/// # self.max_value = match self.max_value { -/// # Some(current_max_value) if max > current_max_value => Some(max), -/// # None => Some(max), -/// # _ => self.max_value, -/// # }; -/// // Compare self.max_value to x0 and x1 -/// } -/// -/// fn finalize(aggregator: Option) -> Self::Output { -/// aggregator?.max_value +/// diesel::table! { +/// users { +/// id -> Integer, +/// name -> Text, /// } /// } -/// # table! { -/// # student_avgs { -/// # id -> Integer, -/// # s1_avg -> Float, -/// # s2_avg -> Float, -/// # } -/// # } -/// -/// # #[cfg(feature = "sqlite")] -/// fn run() -> Result<(), Box> { -/// # use self::student_avgs::dsl::*; -/// let connection = &mut SqliteConnection::establish(":memory:")?; -/// # diesel::sql_query("create table student_avgs (id integer primary key autoincrement, s1_avg float, s2_avg float)") -/// # .execute(connection) -/// # .unwrap(); -/// # diesel::sql_query("insert into student_avgs (s1_avg, s2_avg) values (85.5, 90), (79.8, 80.1)") -/// # .execute(connection) -/// # .unwrap(); /// -/// range_max_utils::register_impl::, _, _>(connection)?; +/// fn use_multi(conn: &mut AnyConnection) -> QueryResult<()> { +/// // Use the connection enum as any other connection type +/// // for inserting/updating/loading/… +/// diesel::insert_into(users::table) +/// .values(users::name.eq("Sean")) +/// .execute(conn)?; /// -/// let result = student_avgs.select(range_max(s1_avg, s2_avg)) -/// .get_result::>(connection)?; +/// let users = users::table.load::<(i32, String)>(conn)?; /// -/// if let Some(max_semester_avg) = result { -/// println!("The largest semester average is: {}", max_semester_avg); -/// } +/// // Match on the connection type to access +/// // the inner connection. This allows us then to use +/// // backend specific methods. +/// # #[cfg(feature = "postgres")] +/// if let AnyConnection::Postgresql(ref mut conn) = conn { +/// // perform a postgresql specific query here +/// let users = users::table.load::<(i32, String)>(conn)?; +/// } /// -/// # assert_eq!(Some(90f32), result); -/// Ok(()) +/// Ok(()) /// } -/// ``` -#[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); +/// # fn main() {} /// ``` /// -/// will generate this code: +/// # Limitations /// -/// ```ignore -/// pub fn lower(x: X) -> lower::HelperType { -/// ... -/// } +/// The derived connection implementation can only cover the common subset of +/// all inner connection types. So, if one backend doesn't support certain SQL features, +/// like for example, returning clauses, the whole connection implementation doesn't +/// support this feature. In addition, only a limited set of SQL types is supported: /// -/// pub(crate) mod lower { -/// pub type HelperType = ...; -/// } +/// * `diesel::sql_types::SmallInt` +/// * `diesel::sql_types::Integer` +/// * `diesel::sql_types::BigInt` +/// * `diesel::sql_types::Double` +/// * `diesel::sql_types::Float` +/// * `diesel::sql_types::Text` +/// * `diesel::sql_types::Date` +/// * `diesel::sql_types::Time` +/// * `diesel::sql_types::Timestamp` +/// +/// Support for additional types can be added by providing manual implementations of +/// `HasSqlType`, `FromSql` and `ToSql` for the corresponding type, all databases included +/// in your enum, and the backend generated by this derive called `MultiBackend`. +/// For example to support a custom enum `MyEnum` with the custom SQL type `MyInteger`: /// ``` +/// extern crate diesel; +/// use diesel::backend::Backend; +/// use diesel::deserialize::{self, FromSql, FromSqlRow}; +/// use diesel::serialize::{self, IsNull, ToSql}; +/// use diesel::AsExpression; +/// use diesel::sql_types::{HasSqlType, SqlType}; +/// use diesel::prelude::*; /// -/// 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), true).into() -} - -/// This is an internal diesel macro that -/// helps to implement all traits for tuples of -/// various sizes -#[doc(hidden)] -#[proc_macro] -pub fn __diesel_for_each_tuple(input: TokenStream) -> TokenStream { - diesel_for_each_tuple::expand(parse_macro_input!(input)).into() -} - -/// This is an internal diesel macro that -/// helps to restrict the visibility of an item based -/// on a feature flag -#[doc(hidden)] -#[proc_macro_attribute] -pub fn __diesel_public_if(attrs: TokenStream, input: TokenStream) -> TokenStream { - diesel_public_if::expand(parse_macro_input!(attrs), parse_macro_input!(input)).into() -} - -/// Specifies that a table exists, and what columns it has. This will create a -/// new public module, with the same name, as the name of the table. In this -/// module, you will find a unit struct named `table`, and a unit struct with the -/// name of each column. -/// -/// By default, this allows a maximum of 32 columns per table. -/// You can increase this limit to 64 by enabling the `64-column-tables` feature. -/// You can increase it to 128 by enabling the `128-column-tables` feature. -/// You can decrease it to 16 columns, -/// which improves compilation time, -/// by disabling the default features of Diesel. -/// Note that enabling 64 column tables or larger will substantially increase -/// the compile time of Diesel. +/// #[derive(diesel::MultiConnection)] +/// pub enum AnyConnection { +/// # #[cfg(feature = "postgres")] +/// Postgresql(diesel::PgConnection), +/// # #[cfg(feature = "mysql")] +/// Mysql(diesel::MysqlConnection), +/// # #[cfg(feature = "sqlite")] +/// Sqlite(diesel::SqliteConnection), +/// } /// -/// Example usage -/// ------------- +/// // defining an custom SQL type is optional +/// // you can also use types from `diesel::sql_types` +/// #[derive(Copy, Clone, Debug, SqlType)] +/// #[diesel(postgres_type(name = "Int4"))] +/// #[diesel(mysql_type(name = "Long"))] +/// #[diesel(sqlite_type(name = "Integer"))] +/// struct MyInteger; /// -/// ```rust -/// # extern crate diesel; /// -/// diesel::table! { -/// users { -/// id -> Integer, -/// name -> VarChar, -/// favorite_color -> Nullable, -/// } +/// // our custom enum +/// #[repr(i32)] +/// #[derive(Debug, Clone, Copy, AsExpression, FromSqlRow)] +/// #[diesel(sql_type = MyInteger)] +/// pub enum MyEnum { +/// A = 1, +/// B = 2, /// } -/// ``` /// -/// You may also specify a primary key if it is called something other than `id`. -/// Tables with no primary key aren't supported. +/// // The `MultiBackend` type is generated by `#[derive(diesel::MultiConnection)]` +/// // This part is only required if you define a custom sql type +/// impl HasSqlType for MultiBackend { +/// fn metadata(lookup: &mut Self::MetadataLookup) -> Self::TypeMetadata { +/// // The `lookup_sql_type` function is exposed by the `MultiBackend` type +/// MultiBackend::lookup_sql_type::(lookup) +/// } +/// } /// -/// ```rust -/// # extern crate diesel; +/// impl FromSql for MyEnum { +/// fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { +/// // The `from_sql` function is exposed by the `RawValue` type of the +/// // `MultiBackend` type +/// // This requires a `FromSql` impl for each backend +/// bytes.from_sql::() +/// } +/// } /// -/// diesel::table! { -/// users (non_standard_primary_key) { -/// non_standard_primary_key -> Integer, -/// name -> VarChar, -/// favorite_color -> Nullable, -/// } +/// impl ToSql for MyEnum { +/// fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, MultiBackend>) -> serialize::Result { +/// /// `set_value` expects a tuple consisting of the target SQL type +/// /// and self for `MultiBackend` +/// /// This requires a `ToSql` impl for each backend +/// out.set_value((MyInteger, self)); +/// Ok(IsNull::No) +/// } /// } +/// # #[cfg(feature = "postgres")] +/// # impl ToSql for MyEnum { +/// # fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, diesel::pg::Pg>) -> serialize::Result { todo!() } +/// # } +/// # #[cfg(feature = "mysql")] +/// # impl ToSql for MyEnum { +/// # fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, diesel::mysql::Mysql>) -> serialize::Result { todo!() } +/// # } +/// # #[cfg(feature = "sqlite")] +/// # impl ToSql for MyEnum { +/// # fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, diesel::sqlite::Sqlite>) -> serialize::Result { todo!() } +/// # } +/// # #[cfg(feature = "postgres")] +/// # impl FromSql for MyEnum { +/// # fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { todo!() } +/// # } +/// # #[cfg(feature = "mysql")] +/// # impl FromSql for MyEnum { +/// # fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { todo!() } +/// # } +/// # #[cfg(feature = "sqlite")] +/// # impl FromSql for MyEnum { +/// # fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { todo!() } +/// # } +/// # fn main() {} /// ``` +#[proc_macro_derive(MultiConnection)] +pub fn derive_multiconnection(input: TokenStream) -> TokenStream { + multiconnection::derive(syn::parse_macro_input!(input)).into() +} + +/// Automatically annotates return type of a query fragment function /// -/// For tables with composite primary keys, list all the columns in the primary key. +/// This may be useful when factoring out common query fragments into functions. +/// If not using this, it would typically involve explicitly writing the full +/// type of the query fragment function, which depending on the length of said +/// query fragment can be quite difficult (especially to maintain) and verbose. +/// +/// # Example /// /// ```rust /// # extern crate diesel; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use schema::{users, posts}; +/// use diesel::dsl; /// -/// diesel::table! { -/// followings (user_id, post_id) { -/// user_id -> Integer, -/// post_id -> Integer, -/// favorited -> Bool, -/// } -/// } /// # fn main() { -/// # use diesel::prelude::Table; -/// # use self::followings::dsl::*; -/// # // Poor man's assert_eq! -- since this is type level this would fail -/// # // to compile if the wrong primary key were generated -/// # let (user_id {}, post_id {}) = followings.primary_key(); +/// # run_test().unwrap(); /// # } -/// ``` +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let conn = &mut establish_connection(); +/// # +/// #[dsl::auto_type] +/// fn user_has_post() -> _ { +/// dsl::exists(posts::table.filter(posts::user_id.eq(users::id))) +/// } /// -/// If you are using types that aren't from Diesel's core types, you can specify -/// which types to import. +/// let users_with_posts: Vec = users::table +/// .filter(user_has_post()) +/// .select(users::name) +/// .load(conn)?; /// -/// ``` -/// # extern crate diesel; -/// # mod diesel_full_text_search { -/// # #[derive(diesel::sql_types::SqlType)] -/// # pub struct TsVector; +/// assert_eq!( +/// &["Sean", "Tess"] as &[_], +/// users_with_posts +/// .iter() +/// .map(|s| s.as_str()) +/// .collect::>() +/// ); +/// # Ok(()) /// # } -/// -/// diesel::table! { -/// use diesel::sql_types::*; -/// # use crate::diesel_full_text_search::*; -/// # /* -/// use diesel_full_text_search::*; -/// # */ -/// -/// posts { -/// id -> Integer, -/// title -> Text, -/// keywords -> TsVector, -/// } -/// } -/// # fn main() {} /// ``` +/// # Limitations /// -/// If you want to add documentation to the generated code, you can use the -/// following syntax: -/// -/// ``` -/// # extern crate diesel; +/// While this attribute tries to support as much of diesels built-in DSL as possible it's +/// unfortunately not possible to support everything. Notable unsupported types are: /// -/// diesel::table! { -/// /// The table containing all blog posts -/// posts { -/// /// The post's unique id -/// id -> Integer, -/// /// The post's title -/// title -> Text, -/// } -/// } -/// ``` +/// * Update statements +/// * Insert from select statements +/// * Query constructed by `diesel::sql_query` +/// * Expressions using `diesel::dsl::sql` /// -/// If you have a column with the same name as a Rust reserved keyword, you can use -/// the `sql_name` attribute like this: +/// For these cases a manual type annotation is required. See the "Annotating Types" section below +/// for details. /// -/// ``` -/// # extern crate diesel; /// -/// diesel::table! { -/// posts { -/// id -> Integer, -/// /// This column is named `mytype` but references the table `type` column. -/// #[sql_name = "type"] -/// mytype -> Text, -/// } -/// } -/// ``` +/// # Advanced usage /// -/// This module will also contain several helper types: +/// By default, the macro will: +/// - Generate a type alias for the return type of the function, named the +/// exact same way as the function itself. +/// - Assume that functions, unless otherwise annotated, have a type alias for +/// their return type available at the same path as the function itself +/// (including case). (e.g. for the `dsl::not(x)` call, it expects that there +/// is a `dsl::not` type alias available) +/// - Assume that methods, unless otherwise annotated, have a type alias +/// available as `diesel::dsl::PascalCaseOfMethodName` (e.g. for the +/// `x.and(y)` call, it expects that there is a `diesel::dsl::And` type +/// alias available) /// -/// dsl -/// --- +/// The defaults can be changed by passing the following attributes to the +/// macro: +/// - `#[auto_type(no_type_alias)]` to disable the generation of the type alias. +/// - `#[auto_type(dsl_path = "path::to::dsl")]` to change the path where the +/// macro will look for type aliases for methods. This is required if you mix your own +/// custom query dsl extensions with diesel types. In that case, you may use this argument to +/// reference a module defined like so: +/// ```ignore +/// mod dsl { +/// /// export all of diesel dsl +/// pub use diesel::dsl::*; +/// +/// /// Export your extension types here +/// pub use crate::your_extension::dsl::YourType; +/// } +/// ``` +/// - `#[auto_type(method_type_case = "snake_case")]` to change the case of the +/// method type alias. +/// - `#[auto_type(function_type_case = "snake_case")]` to change the case of +/// the function type alias (if you don't want the exact same path but want to +/// change the case of the last element of the path). /// -/// This simply re-exports the table, renamed to the same name as the module, -/// and each of the columns. This is useful to glob import when you're dealing -/// primarily with one table, to allow writing `users.filter(name.eq("Sean"))` -/// instead of `users::table.filter(users::name.eq("Sean"))`. +/// The `dsl_path` attribute in particular may be used to declare an +/// intermediate module where you would define the few additional needed type +/// aliases that can't be inferred automatically. /// -/// `all_columns` -/// ----------- +/// ## Annotating types /// -/// A constant will be assigned called `all_columns`. This is what will be -/// selected if you don't otherwise specify a select clause. It's type will be -/// `table::AllColumns`. You can also get this value from the -/// `Table::all_columns` function. +/// Sometimes the macro can't infer the type of a particular sub-expression. In +/// that case, you can annotate the type of the sub-expression: /// -/// star -/// ---- +/// ```rust +/// # extern crate diesel; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use schema::{users, posts}; +/// use diesel::dsl; /// -/// This will be the qualified "star" expression for this table (e.g. -/// `users.*`). Internally, we read columns by index, not by name, so this -/// column is not safe to read data out of, and it has had its SQL type set to -/// `()` to prevent accidentally using it as such. It is sometimes useful for -/// counting statements, however. It can also be accessed through the `Table.star()` -/// method. +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let conn = &mut establish_connection(); +/// # +/// // This will generate a `user_has_post_with_id_greater_than` type alias +/// #[dsl::auto_type] +/// fn user_has_post_with_id_greater_than(id_greater_than: i32) -> _ { +/// dsl::exists( +/// posts::table +/// .filter(posts::user_id.eq(users::id)) +/// .filter(posts::id.gt(id_greater_than)), +/// ) +/// } /// -/// `SqlType` -/// ------- +/// #[dsl::auto_type] +/// fn users_with_posts_with_id_greater_than(id_greater_than: i32) -> _ { +/// // If we didn't specify the type for this query fragment, the macro would infer it as +/// // `user_has_post_with_id_greater_than`, which would be incorrect because there is +/// // no generic parameter. +/// let filter: user_has_post_with_id_greater_than = +/// user_has_post_with_id_greater_than(id_greater_than); +/// // The macro inferring that it has to pass generic parameters is still the convention +/// // because it's the most general case, as well as the common case within Diesel itself, +/// // and because annotating this way is reasonably simple, while the other way around +/// // would be hard. /// -/// A type alias called `SqlType` will be created. It will be the SQL type of -/// `all_columns`. The SQL type is needed for things like returning boxed -/// queries. +/// users::table.filter(filter).select(users::name) +/// } /// -/// `BoxedQuery` -/// ---------- +/// let users_with_posts: Vec = users_with_posts_with_id_greater_than(2).load(conn)?; /// -/// ```ignore -/// pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, table, DB>; +/// assert_eq!( +/// &["Tess"] as &[_], +/// users_with_posts +/// .iter() +/// .map(|s| s.as_str()) +/// .collect::>() +/// ); +/// # Ok(()) +/// # } /// ``` -#[proc_macro] -pub fn table_proc(input: TokenStream) -> TokenStream { - match syn::parse(input) { - Ok(input) => table::expand(input).into(), - Err(_) => quote::quote! { - compile_error!( - "Invalid `table!` syntax. Please see the `table!` macro docs for more info.\n\ - Docs available at: `https://docs.diesel.rs/master/diesel/macro.table.html`\n" - ); - } - .into(), - } +#[proc_macro_attribute] +pub fn auto_type( + attr: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + dsl_auto_type::auto_type_proc_macro_attribute( + proc_macro2::TokenStream::from(attr), + proc_macro2::TokenStream::from(input), + dsl_auto_type::DeriveSettings::builder() + .default_dsl_path(parse_quote!(diesel::dsl)) + .default_generate_type_alias(true) + .default_method_type_case(AUTO_TYPE_DEFAULT_METHOD_TYPE_CASE) + .default_function_type_case(AUTO_TYPE_DEFAULT_FUNCTION_TYPE_CASE) + .build(), + ) + .into() } -/// 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 -/// any location all the inner connections are valid. This derive supports enum -/// variants containing a single tuple field. Each tuple field type must implement -/// `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. +const AUTO_TYPE_DEFAULT_METHOD_TYPE_CASE: dsl_auto_type::Case = dsl_auto_type::Case::UpperCamel; +const AUTO_TYPE_DEFAULT_FUNCTION_TYPE_CASE: dsl_auto_type::Case = dsl_auto_type::Case::DoNotChange; + +/// Declare a sql function for use in your code. /// -/// 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` -/// 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 -/// wrap the result into the enum. +/// Diesel only provides support for a very small number of SQL functions. +/// This macro enables you to add additional functions from the SQL standard, +/// as well as any custom functions your application might have. /// -/// # Example -/// ``` -/// # extern crate diesel; -/// # use diesel::result::QueryResult; -/// use diesel::prelude::*; +/// The syntax for this attribute macro is designed to be applied to `extern "SQL"` blocks +/// with function definitions. These function typically use types +/// from [`diesel::sql_types`](../diesel/sql_types/index.html) as arguments and return types. +/// You can use such definitions to declare bindings to unsupported SQL functions. /// -/// #[derive(diesel::MultiConnection)] -/// pub enum AnyConnection { -/// # #[cfg(feature = "postgres")] -/// Postgresql(diesel::PgConnection), -/// # #[cfg(feature = "mysql")] -/// Mysql(diesel::MysqlConnection), -/// # #[cfg(feature = "sqlite")] -/// Sqlite(diesel::SqliteConnection), +/// For each function in this `extern` block the macro will generate two items. +/// A function with the name that you've given, and a module with a helper type +/// representing the return type of your function. For example, this invocation: +/// +/// ```ignore +/// #[declare_sql_function] +/// extern "SQL" { +/// fn lower(x: Text) -> Text /// } +/// ``` /// -/// diesel::table! { -/// users { -/// id -> Integer, -/// name -> Text, -/// } +/// will generate this code: +/// +/// ```ignore +/// pub fn lower(x: X) -> lower { +/// ... /// } /// -/// fn use_multi(conn: &mut AnyConnection) -> QueryResult<()> { -/// // Use the connection enum as any other connection type -/// // for inserting/updating/loading/… -/// diesel::insert_into(users::table) -/// .values(users::name.eq("Sean")) -/// .execute(conn)?; +/// pub type lower = ...; +/// ``` /// -/// let users = users::table.load::<(i32, String)>(conn)?; +/// Most attributes given to this macro will be put on the generated function +/// (including doc comments). /// -/// // Match on the connection type to access -/// // the inner connection. This allows us then to use -/// // backend specific methods. -/// # #[cfg(feature = "postgres")] -/// if let AnyConnection::Postgresql(ref mut conn) = conn { -/// // perform a postgresql specific query here -/// let users = users::table.load::<(i32, String)>(conn)?; -/// } +/// # Adding Doc Comments /// -/// Ok(()) +/// ```no_run +/// # extern crate diesel; +/// # use diesel::*; +/// # use diesel::expression::functions::declare_sql_function; +/// # +/// # table! { crates { id -> Integer, name -> VarChar, } } +/// # +/// use diesel::sql_types::Text; +/// +/// #[declare_sql_function] +/// extern "SQL" { +/// /// Represents the `canon_crate_name` SQL function, created in +/// /// migration .... +/// fn canon_crate_name(a: Text) -> Text; /// } /// -/// # fn main() {} +/// # fn main() { +/// # use self::crates::dsl::*; +/// let target_name = "diesel"; +/// crates.filter(canon_crate_name(name).eq(canon_crate_name(target_name))); +/// // This will generate the following SQL +/// // SELECT * FROM crates WHERE canon_crate_name(crates.name) = canon_crate_name($1) +/// # } /// ``` /// -/// # Limitations +/// # Special Attributes /// -/// The derived connection implementation can only cover the common subset of -/// all inner connection types. So, if one backend doesn't support certain SQL features, -/// like for example, returning clauses, the whole connection implementation doesn't -/// support this feature. In addition, only a limited set of SQL types is supported: +/// There are a handful of special attributes that Diesel will recognize. They +/// are: /// -/// * `diesel::sql_types::SmallInt` -/// * `diesel::sql_types::Integer` -/// * `diesel::sql_types::BigInt` -/// * `diesel::sql_types::Double` -/// * `diesel::sql_types::Float` -/// * `diesel::sql_types::Text` -/// * `diesel::sql_types::Date` -/// * `diesel::sql_types::Time` -/// * `diesel::sql_types::Timestamp` +/// - `#[aggregate]` +/// - Indicates that this is an aggregate function, and that `NonAggregate` +/// shouldn't be implemented. +/// - `#[sql_name = "name"]` +/// - The SQL to be generated is different from the Rust name of the function. +/// This can be used to represent functions which can take many argument +/// types, or to capitalize function names. /// -/// Support for additional types can be added by providing manual implementations of -/// `HasSqlType`, `FromSql` and `ToSql` for the corresponding type, all databases included -/// in your enum, and the backend generated by this derive called `MultiBackend`. -/// For example to support a custom enum `MyEnum` with the custom SQL type `MyInteger`: -/// ``` -/// extern crate diesel; -/// use diesel::backend::Backend; -/// use diesel::deserialize::{self, FromSql, FromSqlRow}; -/// use diesel::serialize::{self, IsNull, ToSql}; -/// use diesel::AsExpression; -/// use diesel::sql_types::{HasSqlType, SqlType}; -/// use diesel::prelude::*; +/// Functions can also be generic. Take the definition of `sum`, for example: /// -/// #[derive(diesel::MultiConnection)] -/// pub enum AnyConnection { -/// # #[cfg(feature = "postgres")] -/// Postgresql(diesel::PgConnection), -/// # #[cfg(feature = "mysql")] -/// Mysql(diesel::MysqlConnection), -/// # #[cfg(feature = "sqlite")] -/// Sqlite(diesel::SqliteConnection), -/// } +/// ```no_run +/// # extern crate diesel; +/// # use diesel::*; +/// # use diesel::expression::functions::declare_sql_function; +/// # +/// # table! { crates { id -> Integer, name -> VarChar, } } +/// # +/// use diesel::sql_types::Foldable; /// -/// // defining an custom SQL type is optional -/// // you can also use types from `diesel::sql_types` -/// #[derive(Copy, Clone, Debug, SqlType)] -/// #[diesel(postgres_type(name = "Int4"))] -/// #[diesel(mysql_type(name = "Long"))] -/// #[diesel(sqlite_type(name = "Integer"))] -/// struct MyInteger; +/// #[declare_sql_function] +/// extern "SQL" { +/// #[aggregate] +/// #[sql_name = "SUM"] +/// fn sum(expr: ST) -> ST::Sum; +/// } /// +/// # fn main() { +/// # use self::crates::dsl::*; +/// crates.select(sum(id)); +/// # } +/// ``` /// -/// // our custom enum -/// #[repr(i32)] -/// #[derive(Debug, Clone, Copy, AsExpression, FromSqlRow)] -/// #[diesel(sql_type = MyInteger)] -/// pub enum MyEnum { -/// A = 1, -/// B = 2, -/// } +/// # SQL Functions without Arguments /// -/// // The `MultiBackend` type is generated by `#[derive(diesel::MultiConnection)]` -/// // This part is only required if you define a custom sql type -/// impl HasSqlType for MultiBackend { -/// fn metadata(lookup: &mut Self::MetadataLookup) -> Self::TypeMetadata { -/// // The `lookup_sql_type` function is exposed by the `MultiBackend` type -/// MultiBackend::lookup_sql_type::(lookup) -/// } -/// } +/// A common example is ordering a query using the `RANDOM()` sql function, +/// which can be implemented using `define_sql_function!` like this: /// -/// impl FromSql for MyEnum { -/// fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { -/// // The `from_sql` function is exposed by the `RawValue` type of the -/// // `MultiBackend` type -/// // This requires a `FromSql` impl for each backend -/// bytes.from_sql::() -/// } +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # use diesel::expression::functions::declare_sql_function; +/// # +/// # table! { crates { id -> Integer, name -> VarChar, } } +/// # +/// #[declare_sql_function] +/// extern "SQL" { +/// fn random() -> Text; /// } /// -/// impl ToSql for MyEnum { -/// fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, MultiBackend>) -> serialize::Result { -/// /// `set_value` expects a tuple consisting of the target SQL type -/// /// and self for `MultiBackend` -/// /// This requires a `ToSql` impl for each backend -/// out.set_value((MyInteger, self)); -/// Ok(IsNull::No) -/// } -/// } -/// # #[cfg(feature = "postgres")] -/// # impl ToSql for MyEnum { -/// # fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, diesel::pg::Pg>) -> serialize::Result { todo!() } -/// # } -/// # #[cfg(feature = "mysql")] -/// # impl ToSql for MyEnum { -/// # fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, diesel::mysql::Mysql>) -> serialize::Result { todo!() } +/// # fn main() { +/// # use self::crates::dsl::*; +/// crates.order(random()); /// # } +/// ``` +/// +/// # Use with SQLite +/// +/// 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` (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. +/// SQLite doesn't support generic functions and variadic functions. +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # use diesel::expression::functions::declare_sql_function; +/// # /// # #[cfg(feature = "sqlite")] -/// # impl ToSql for MyEnum { -/// # fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, diesel::sqlite::Sqlite>) -> serialize::Result { todo!() } -/// # } -/// # #[cfg(feature = "postgres")] -/// # impl FromSql for MyEnum { -/// # fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { todo!() } +/// # fn main() { +/// # run_test().unwrap(); /// # } -/// # #[cfg(feature = "mysql")] -/// # impl FromSql for MyEnum { -/// # fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { todo!() } +/// # +/// # #[cfg(not(feature = "sqlite"))] +/// # fn main() { /// # } +/// # +/// use diesel::sql_types::{Integer, Double}; +/// +/// #[declare_sql_function] +/// extern "SQL" { +/// fn add_mul(x: Integer, y: Integer, z: Double) -> Double; +/// } +/// /// # #[cfg(feature = "sqlite")] -/// # impl FromSql for MyEnum { -/// # fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { todo!() } +/// # fn run_test() -> Result<(), Box> { +/// let connection = &mut SqliteConnection::establish(":memory:")?; +/// +/// add_mul_utils::register_impl(connection, |x: i32, y: i32, z: f64| { +/// (x + y) as f64 * z +/// })?; +/// +/// let result = select(add_mul(1, 2, 1.5)) +/// .get_result::(connection)?; +/// assert_eq!(4.5, result); +/// # Ok(()) /// # } -/// # fn main() {} /// ``` -#[proc_macro_derive(MultiConnection)] -pub fn derive_multiconnection(input: TokenStream) -> TokenStream { - multiconnection::derive(syn::parse_macro_input!(input)).into() -} - -/// Automatically annotates return type of a query fragment function /// -/// This may be useful when factoring out common query fragments into functions. -/// If not using this, it would typically involve explicitly writing the full -/// type of the query fragment function, which depending on the length of said -/// query fragment can be quite difficult (especially to maintain) and verbose. +/// ## Panics +/// +/// If an implementation of the custom function panics and unwinding is enabled, the panic is +/// caught and the function returns to libsqlite with an error. It can't propagate the panics due +/// to the FFI boundary. +/// +/// This is the same for [custom aggregate functions](#custom-aggregate-functions). /// -/// # Example +/// ## Custom Aggregate Functions +/// +/// Custom aggregate functions can be created in SQLite by adding an `#[aggregate]` +/// 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. /// /// ```rust /// # extern crate diesel; -/// # include!("../../diesel/src/doctest_setup.rs"); -/// # use schema::{users, posts}; -/// use diesel::dsl; -/// +/// # use diesel::*; +/// # use diesel::expression::functions::declare_sql_function; +/// # +/// # #[cfg(feature = "sqlite")] /// # fn main() { -/// # run_test().unwrap(); +/// # run().unwrap(); /// # } /// # -/// # fn run_test() -> QueryResult<()> { -/// # let conn = &mut establish_connection(); -/// # -/// #[dsl::auto_type] -/// fn user_has_post() -> _ { -/// dsl::exists(posts::table.filter(posts::user_id.eq(users::id))) -/// } -/// -/// let users_with_posts: Vec = users::table -/// .filter(user_has_post()) -/// .select(users::name) -/// .load(conn)?; -/// -/// assert_eq!( -/// &["Sean", "Tess"] as &[_], -/// users_with_posts -/// .iter() -/// .map(|s| s.as_str()) -/// .collect::>() -/// ); -/// # Ok(()) +/// # #[cfg(not(feature = "sqlite"))] +/// # fn main() { /// # } -/// ``` -/// # Limitations +/// use diesel::sql_types::Integer; +/// # #[cfg(feature = "sqlite")] +/// use diesel::sqlite::SqliteAggregateFunction; /// -/// While this attribute tries to support as much of diesels built-in DSL as possible it's -/// unfortunately not possible to support everything. Notable unsupported types are: +/// #[declare_sql_function] +/// extern "SQL" { +/// #[aggregate] +/// fn my_sum(x: Integer) -> Integer; +/// } /// -/// * Update statements -/// * Insert from select statements -/// * Query constructed by `diesel::sql_query` -/// * Expressions using `diesel::dsl::sql` +/// #[derive(Default)] +/// struct MySum { sum: i32 } /// -/// For these cases a manual type annotation is required. See the "Annotating Types" section below -/// for details. +/// # #[cfg(feature = "sqlite")] +/// impl SqliteAggregateFunction for MySum { +/// type Output = i32; /// +/// fn step(&mut self, expr: i32) { +/// self.sum += expr; +/// } /// -/// # Advanced usage +/// fn finalize(aggregator: Option) -> Self::Output { +/// aggregator.map(|a| a.sum).unwrap_or_default() +/// } +/// } +/// # table! { +/// # players { +/// # id -> Integer, +/// # score -> Integer, +/// # } +/// # } /// -/// By default, the macro will: -/// - Generate a type alias for the return type of the function, named the -/// exact same way as the function itself. -/// - Assume that functions, unless otherwise annotated, have a type alias for -/// their return type available at the same path as the function itself -/// (including case). (e.g. for the `dsl::not(x)` call, it expects that there -/// is a `dsl::not` type alias available) -/// - Assume that methods, unless otherwise annotated, have a type alias -/// available as `diesel::dsl::PascalCaseOfMethodName` (e.g. for the -/// `x.and(y)` call, it expects that there is a `diesel::dsl::And` type -/// alias available) +/// # #[cfg(feature = "sqlite")] +/// fn run() -> Result<(), Box> { +/// # use self::players::dsl::*; +/// let connection = &mut SqliteConnection::establish(":memory:")?; +/// # diesel::sql_query("create table players (id integer primary key autoincrement, score integer)") +/// # .execute(connection) +/// # .unwrap(); +/// # diesel::sql_query("insert into players (score) values (10), (20), (30)") +/// # .execute(connection) +/// # .unwrap(); /// -/// The defaults can be changed by passing the following attributes to the -/// macro: -/// - `#[auto_type(no_type_alias)]` to disable the generation of the type alias. -/// - `#[auto_type(dsl_path = "path::to::dsl")]` to change the path where the -/// macro will look for type aliases for methods. This is required if you mix your own -/// custom query dsl extensions with diesel types. In that case, you may use this argument to -/// reference a module defined like so: -/// ```ignore -/// mod dsl { -/// /// export all of diesel dsl -/// pub use diesel::dsl::*; -/// -/// /// Export your extension types here -/// pub use crate::your_extension::dsl::YourType; -/// } -/// ``` -/// - `#[auto_type(method_type_case = "snake_case")]` to change the case of the -/// method type alias. -/// - `#[auto_type(function_type_case = "snake_case")]` to change the case of -/// the function type alias (if you don't want the exact same path but want to -/// change the case of the last element of the path). +/// my_sum_utils::register_impl::(connection)?; /// -/// The `dsl_path` attribute in particular may be used to declare an -/// intermediate module where you would define the few additional needed type -/// aliases that can't be inferred automatically. +/// let total_score = players.select(my_sum(score)) +/// .get_result::(connection)?; /// -/// ## Annotating types +/// println!("The total score of all the players is: {}", total_score); /// -/// Sometimes the macro can't infer the type of a particular sub-expression. In -/// that case, you can annotate the type of the sub-expression: +/// # assert_eq!(60, total_score); +/// Ok(()) +/// } +/// ``` +/// +/// With multiple function arguments, the arguments are passed as a tuple to `SqliteAggregateFunction` /// /// ```rust /// # extern crate diesel; -/// # include!("../../diesel/src/doctest_setup.rs"); -/// # use schema::{users, posts}; -/// use diesel::dsl; -/// +/// # use diesel::*; +/// # use diesel::expression::functions::declare_sql_function; +/// # +/// # #[cfg(feature = "sqlite")] /// # fn main() { -/// # run_test().unwrap(); +/// # run().unwrap(); /// # } /// # -/// # fn run_test() -> QueryResult<()> { -/// # let conn = &mut establish_connection(); -/// # -/// // This will generate a `user_has_post_with_id_greater_than` type alias -/// #[dsl::auto_type] -/// fn user_has_post_with_id_greater_than(id_greater_than: i32) -> _ { -/// dsl::exists( -/// posts::table -/// .filter(posts::user_id.eq(users::id)) -/// .filter(posts::id.gt(id_greater_than)), -/// ) +/// # #[cfg(not(feature = "sqlite"))] +/// # fn main() { +/// # } +/// use diesel::sql_types::{Float, Nullable}; +/// # #[cfg(feature = "sqlite")] +/// use diesel::sqlite::SqliteAggregateFunction; +/// +/// #[declare_sql_function] +/// extern "SQL" { +/// #[aggregate] +/// fn range_max(x0: Float, x1: Float) -> Nullable; /// } /// -/// #[dsl::auto_type] -/// fn users_with_posts_with_id_greater_than(id_greater_than: i32) -> _ { -/// // If we didn't specify the type for this query fragment, the macro would infer it as -/// // `user_has_post_with_id_greater_than`, which would be incorrect because there is -/// // no generic parameter. -/// let filter: user_has_post_with_id_greater_than = -/// user_has_post_with_id_greater_than(id_greater_than); -/// // The macro inferring that it has to pass generic parameters is still the convention -/// // because it's the most general case, as well as the common case within Diesel itself, -/// // and because annotating this way is reasonably simple, while the other way around -/// // would be hard. +/// #[derive(Default)] +/// struct RangeMax { max_value: Option } /// -/// users::table.filter(filter).select(users::name) -/// } +/// # #[cfg(feature = "sqlite")] +/// impl SqliteAggregateFunction<(T, T)> for RangeMax { +/// type Output = Option; /// -/// let users_with_posts: Vec = users_with_posts_with_id_greater_than(2).load(conn)?; +/// fn step(&mut self, (x0, x1): (T, T)) { +/// # let max = if x0 >= x1 { +/// # x0 +/// # } else { +/// # x1 +/// # }; +/// # +/// # self.max_value = match self.max_value { +/// # Some(current_max_value) if max > current_max_value => Some(max), +/// # None => Some(max), +/// # _ => self.max_value, +/// # }; +/// // Compare self.max_value to x0 and x1 +/// } /// -/// assert_eq!( -/// &["Tess"] as &[_], -/// users_with_posts -/// .iter() -/// .map(|s| s.as_str()) -/// .collect::>() -/// ); -/// # Ok(()) +/// fn finalize(aggregator: Option) -> Self::Output { +/// aggregator?.max_value +/// } +/// } +/// # table! { +/// # student_avgs { +/// # id -> Integer, +/// # s1_avg -> Float, +/// # s2_avg -> Float, +/// # } /// # } +/// +/// # #[cfg(feature = "sqlite")] +/// fn run() -> Result<(), Box> { +/// # use self::student_avgs::dsl::*; +/// let connection = &mut SqliteConnection::establish(":memory:")?; +/// # diesel::sql_query("create table student_avgs (id integer primary key autoincrement, s1_avg float, s2_avg float)") +/// # .execute(connection) +/// # .unwrap(); +/// # diesel::sql_query("insert into student_avgs (s1_avg, s2_avg) values (85.5, 90), (79.8, 80.1)") +/// # .execute(connection) +/// # .unwrap(); +/// +/// range_max_utils::register_impl::, _, _>(connection)?; +/// +/// let result = student_avgs.select(range_max(s1_avg, s2_avg)) +/// .get_result::>(connection)?; +/// +/// if let Some(max_semester_avg) = result { +/// println!("The largest semester average is: {}", max_semester_avg); +/// } +/// +/// # assert_eq!(Some(90f32), result); +/// Ok(()) +/// } /// ``` #[proc_macro_attribute] -pub fn auto_type( - attr: proc_macro::TokenStream, +pub fn declare_sql_function( + _attr: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - dsl_auto_type::auto_type_proc_macro_attribute( - proc_macro2::TokenStream::from(attr), - proc_macro2::TokenStream::from(input), - dsl_auto_type::DeriveSettings::builder() - .default_dsl_path(parse_quote!(diesel::dsl)) - .default_generate_type_alias(true) - .default_method_type_case(AUTO_TYPE_DEFAULT_METHOD_TYPE_CASE) - .default_function_type_case(AUTO_TYPE_DEFAULT_FUNCTION_TYPE_CASE) - .build(), - ) - .into() + let input = proc_macro2::TokenStream::from(input); + let result = syn::parse2::(input.clone()).map(|res| { + let expanded = res + .function_decls + .into_iter() + .map(|decl| sql_function::expand(decl, false)); + quote::quote! { + #(#expanded)* + } + }); + match result { + Ok(token_stream) => token_stream.into(), + Err(e) => { + let mut output = input; + output.extend(e.into_compile_error()); + output.into() + } + } } - -const AUTO_TYPE_DEFAULT_METHOD_TYPE_CASE: dsl_auto_type::Case = dsl_auto_type::Case::UpperCamel; -const AUTO_TYPE_DEFAULT_FUNCTION_TYPE_CASE: dsl_auto_type::Case = dsl_auto_type::Case::DoNotChange; diff --git a/diesel_derives/src/sql_function.rs b/diesel_derives/src/sql_function.rs index c35a3fe95930..5ed628522c80 100644 --- a/diesel_derives/src/sql_function.rs +++ b/diesel_derives/src/sql_function.rs @@ -3,6 +3,7 @@ use quote::quote; use quote::ToTokens; use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; use syn::{ parenthesized, parse_quote, Attribute, GenericArgument, Generics, Ident, Meta, MetaNameValue, PathArguments, Token, Type, @@ -437,6 +438,32 @@ pub(crate) fn expand(input: SqlFunctionDecl, legacy_helper_type_and_module: bool } } +pub(crate) struct ExternSqlBlock { + pub(crate) function_decls: Vec, +} + +impl Parse for ExternSqlBlock { + fn parse(input: ParseStream) -> Result { + let block = syn::ItemForeignMod::parse(input)?; + if block.abi.name.as_ref().map(|n| n.value()) != Some("SQL".into()) { + return Err(syn::Error::new(block.abi.span(), "expect `SQL` as ABI")); + } + if block.unsafety.is_some() { + return Err(syn::Error::new( + block.unsafety.unwrap().span(), + "expect `SQL` function blocks to be safe", + )); + } + let function_decls = block + .items + .into_iter() + .map(|i| syn::parse2(quote! { #i })) + .collect::>>()?; + + Ok(ExternSqlBlock { function_decls }) + } +} + pub(crate) struct SqlFunctionDecl { attributes: Vec, fn_token: Token![fn], diff --git a/diesel_tests/tests/annotations.rs b/diesel_tests/tests/annotations.rs index 841b4761f4da..57b6d940728f 100644 --- a/diesel_tests/tests/annotations.rs +++ b/diesel_tests/tests/annotations.rs @@ -286,7 +286,10 @@ fn derive_insertable_with_option_for_not_null_field_with_default() { assert_eq!(Some(&User::new(123, "Bob")), bob); } -define_sql_function!(fn nextval(a: Text) -> Integer); +#[declare_sql_function] +extern "SQL" { + fn nextval(a: Text) -> Integer; +} #[test] #[cfg(feature = "postgres")] diff --git a/diesel_tests/tests/distinct.rs b/diesel_tests/tests/distinct.rs index abf240dab462..2493d1a0390b 100644 --- a/diesel_tests/tests/distinct.rs +++ b/diesel_tests/tests/distinct.rs @@ -212,15 +212,20 @@ fn distinct_of_multiple_columns() { .inner_join(users::table) .order(users::id) .distinct_on((users::id, posts::body)) - .load(&mut connection); - let expected = vec![ - (posts[0].clone(), sean.clone()), - (posts[1].clone(), sean.clone()), - (posts[4].clone(), tess.clone()), - (posts[7].clone(), tess.clone()), - ]; - - assert_eq!(Ok(expected), data); + .load::<(Post, User)>(&mut connection); + + assert!(data.is_ok(), "{:?}", data.unwrap_err()); + let data = data.unwrap(); + assert_eq!(data.len(), 4); + assert_eq!(data[0].1, sean.clone()); + assert_eq!(data[1].1, sean.clone()); + assert_eq!(data[2].1, tess.clone()); + assert_eq!(data[3].1, tess.clone()); + // post id's are non-deterministic + assert_eq!(data[0].0.body, Some("1".into())); + assert_eq!(data[1].0.body, Some("2".into())); + assert_eq!(data[2].0.body, Some("1".into())); + assert_eq!(data[3].0.body, Some("2".into())); // multi order by // multi distinct on @@ -283,16 +288,20 @@ fn distinct_of_multiple_columns() { .left_join(users::table) .order((users::id.nullable(), posts::body.nullable().desc())) .distinct_on((users::id.nullable(), posts::body.nullable())) - .load(&mut connection); - - let expected = vec![ - (posts[1].clone(), Some(sean.clone())), - (posts[0].clone(), Some(sean.clone())), - (posts[7].clone(), Some(tess.clone())), - (posts[6].clone(), Some(tess.clone())), - ]; - - assert_eq!(Ok(expected), data); + .load::<(Post, Option)>(&mut connection); + + assert!(data.is_ok(), "{:?}", data.unwrap_err()); + let data = data.unwrap(); + assert_eq!(data.len(), 4); + assert_eq!(data[0].1, Some(sean.clone())); + assert_eq!(data[1].1, Some(sean.clone())); + assert_eq!(data[2].1, Some(tess.clone())); + assert_eq!(data[3].1, Some(tess.clone())); + // post id's are non-deterministic + assert_eq!(data[0].0.body, Some("2".into())); + assert_eq!(data[1].0.body, Some("1".into())); + assert_eq!(data[2].0.body, Some("2".into())); + assert_eq!(data[3].0.body, Some("1".into())); let data = posts::table .left_join(users::table) diff --git a/diesel_tests/tests/expressions/mod.rs b/diesel_tests/tests/expressions/mod.rs index 5423cf56843c..b3c314099787 100644 --- a/diesel_tests/tests/expressions/mod.rs +++ b/diesel_tests/tests/expressions/mod.rs @@ -15,6 +15,20 @@ use diesel::query_builder::*; use diesel::sql_types::SqlType; use diesel::*; +#[declare_sql_function] +extern "SQL" { + fn coalesce( + x: sql_types::Nullable, + y: sql_types::VarChar, + ) -> sql_types::VarChar; +} + +#[cfg(feature = "postgres")] +#[declare_sql_function] +extern "SQL" { + fn unnest(a: sql_types::Array) -> sql_types::Int4; +} + #[test] fn test_count_counts_the_rows() { let connection = &mut connection(); @@ -219,8 +233,6 @@ fn test_min() { assert_eq!(Ok(None::), source.first(connection)); } -define_sql_function!(fn coalesce(x: sql_types::Nullable, y: sql_types::VarChar) -> sql_types::VarChar); - #[test] fn function_with_multiple_arguments() { use crate::schema::users::dsl::*; @@ -442,11 +454,6 @@ fn test_arrays_a() { assert_eq!(value, vec![1, 2]); } -#[cfg(feature = "postgres")] -use diesel::sql_types::{Array, Int4}; -#[cfg(feature = "postgres")] -define_sql_function!(fn unnest(a: Array) -> Int4); - #[test] #[cfg(feature = "postgres")] fn test_arrays_b() { diff --git a/diesel_tests/tests/filter.rs b/diesel_tests/tests/filter.rs index 9a54170622c2..240cfbc4fbee 100644 --- a/diesel_tests/tests/filter.rs +++ b/diesel_tests/tests/filter.rs @@ -1,4 +1,5 @@ use crate::schema::*; +use diesel::sql_types::VarChar; use diesel::*; macro_rules! assert_sets_eq { @@ -411,8 +412,10 @@ fn not_affects_arguments_passed_when_they_contain_higher_operator_precedence() { assert_eq!(Ok(2), count); } -use diesel::sql_types::VarChar; -define_sql_function!(fn lower(x: VarChar) -> VarChar); +#[declare_sql_function] +extern "SQL" { + 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 68dceaba8fc0..e63ccd004744 100644 --- a/diesel_tests/tests/joins.rs +++ b/diesel_tests/tests/joins.rs @@ -1,4 +1,5 @@ use crate::schema::*; +use diesel::sql_types::Text; use diesel::*; #[test] @@ -358,8 +359,10 @@ fn select_then_join() { assert_eq!(expected_data, data); } -use diesel::sql_types::Text; -define_sql_function!(fn lower(x: Text) -> Text); +#[declare_sql_function] +extern "SQL" { + 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 7a7a21563078..3c1876b22d42 100644 --- a/diesel_tests/tests/macros.rs +++ b/diesel_tests/tests/macros.rs @@ -7,7 +7,12 @@ use crate::schema::*; use diesel::sql_types::{BigInt, VarChar}; use diesel::*; -define_sql_function!(fn my_lower(x: VarChar) -> VarChar); +#[declare_sql_function] +extern "SQL" { + fn my_lower(x: VarChar) -> VarChar; + fn setval(x: VarChar, y: BigInt); + fn currval(x: VarChar) -> BigInt; +} #[test] fn test_sql_function() { @@ -40,9 +45,6 @@ fn test_sql_function() { ); } -define_sql_function!(fn setval(x: VarChar, y: BigInt)); -define_sql_function!(fn currval(x: VarChar) -> BigInt); - #[test] fn sql_function_without_return_type() { let connection = &mut connection(); diff --git a/diesel_tests/tests/schema/mod.rs b/diesel_tests/tests/schema/mod.rs index 9707fc50729c..cfce01d0fce6 100644 --- a/diesel_tests/tests/schema/mod.rs +++ b/diesel_tests/tests/schema/mod.rs @@ -318,7 +318,10 @@ pub fn drop_table_cascade(connection: &mut TestConnection, table: &str) { .unwrap(); } -define_sql_function!(fn nextval(a: sql_types::VarChar) -> sql_types::BigInt); +#[declare_sql_function] +extern "SQL" { + fn nextval(a: sql_types::VarChar) -> sql_types::BigInt; +} pub fn connection_with_sean_and_tess_in_users_table() -> TestConnection { let mut connection = connection(); diff --git a/examples/postgres/composite_types/examples/composite2rust_colors.rs b/examples/postgres/composite_types/examples/composite2rust_colors.rs index a038d14910e0..f28c43bb5ea4 100644 --- a/examples/postgres/composite_types/examples/composite2rust_colors.rs +++ b/examples/postgres/composite_types/examples/composite2rust_colors.rs @@ -5,12 +5,16 @@ use composite_types::establish_connection; use composite_types::schema::colors::{blue, color_id, color_name, dsl::colors, green, red}; // Define the signature of the SQL function we want to call: -use diesel::define_sql_function; +use diesel::declare_sql_function; use diesel::pg::Pg; use diesel::pg::PgValue; use diesel::sql_types::{Float, Integer, Record, Text}; -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); + +#[declare_sql_function] +extern "SQL" { + fn color2grey(r: Integer, g: Integer, b: Integer) -> Record<(Float, Text)>; + fn color2gray(r: Integer, g: Integer, b: Integer) -> PgGrayType; +} // Needed to select, construct the query and submit it. use diesel::deserialize::{self, FromSql, FromSqlRow}; diff --git a/examples/postgres/composite_types/examples/composite2rust_coordinates.rs b/examples/postgres/composite_types/examples/composite2rust_coordinates.rs index fcb53ea2f018..33bc992b6590 100644 --- a/examples/postgres/composite_types/examples/composite2rust_coordinates.rs +++ b/examples/postgres/composite_types/examples/composite2rust_coordinates.rs @@ -5,11 +5,15 @@ use composite_types::establish_connection; use composite_types::schema::coordinates::{coord_id, dsl::coordinates, xcoord, ycoord}; // Define the signature of the SQL function we want to call: -use diesel::define_sql_function; +use diesel::declare_sql_function; use diesel::sql_types::Integer; -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)>); + +#[declare_sql_function] +extern "SQL" { + fn distance_from_origin(re: Integer, im: Integer) -> Float; + fn shortest_distance() -> Record<(Integer, Float)>; + fn longest_distance() -> Record<(Integer, Float)>; +} // Needed to select, construct the query and submit it. use diesel::select;