From ed4d25f5a2099b23b602dd41b9f1b73fc2d0cd6b Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 26 Nov 2024 20:23:01 +0100 Subject: [PATCH] Introduce a `#[declare_sql_function]` attribute macro This commit introduces a new `#[declare_sql_function]` attribute macro that can be applied to `extern "SQL"` blocks. This is essentially the same as the existing `define_sql_function!` function like macro in terms of functionality. I see the following advantages of using an attribute macro + an `extern "SQL"` block instead: * This is closer to rust syntax, so rustfmt will understand that and work correctly inside these blocks * This allows to put several functions into the same block * Maybe in the future this also allows to apply attributes to the whole block instead of to each item The downside of this change is that we then have three variants to declare sql functions: * `sql_function!()` (deprectated) * `define_sql_function!()` (introduced in 2.2, we might want to deprecate that as well?) * The new attribute macro --- CHANGELOG.md | 13 +- .../connection/statement_cache/strategy.rs | 8 +- diesel/src/expression/count.rs | 5 +- .../expression/functions/aggregate_folding.rs | 7 +- .../functions/aggregate_ordering.rs | 7 +- .../src/expression/functions/date_and_time.rs | 6 +- diesel/src/expression/functions/mod.rs | 4 + diesel/src/lib.rs | 2 + diesel/src/pg/expression/functions.rs | 401 ++--- diesel/src/pg/metadata_lookup.rs | 5 +- diesel/src/sqlite/connection/mod.rs | 34 +- diesel/src/sqlite/connection/row.rs | 5 +- .../src/sqlite/types/date_and_time/chrono.rs | 9 +- diesel/src/sqlite/types/date_and_time/time.rs | 9 +- .../information_schema.rs | 5 +- .../src/infer_schema_internals/mysql.rs | 8 +- diesel_cli/src/infer_schema_internals/pg.rs | 41 +- ...mix_aggregate_and_non_aggregate_selects.rs | 5 +- ...aggregate_and_non_aggregate_selects.stderr | 18 +- .../numeric_ops_require_numeric_column.stderr | 3 + ...conflict_requires_valid_conflict_target.rs | 5 +- ...lict_requires_valid_conflict_target.stderr | 12 +- ...ressions_cant_be_used_in_a_sqlite_query.rs | 5 +- ...ions_cant_be_used_in_a_sqlite_query.stderr | 26 +- ...ght_side_of_left_join_requires_nullable.rs | 6 +- ...side_of_left_join_requires_nullable.stderr | 211 +-- ...d_functions_follow_same_selection_rules.rs | 7 +- ...nctions_follow_same_selection_rules.stderr | 8 +- diesel_derives/src/lib.rs | 1497 +++++++++-------- diesel_derives/src/sql_function.rs | 27 + diesel_tests/tests/annotations.rs | 5 +- diesel_tests/tests/distinct.rs | 47 +- diesel_tests/tests/expressions/mod.rs | 21 +- diesel_tests/tests/filter.rs | 7 +- diesel_tests/tests/joins.rs | 7 +- diesel_tests/tests/macros.rs | 10 +- diesel_tests/tests/schema/mod.rs | 5 +- .../examples/composite2rust_colors.rs | 10 +- .../examples/composite2rust_coordinates.rs | 12 +- 39 files changed, 1346 insertions(+), 1177 deletions(-) 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;