From 59c91174ce88b342e8351b239aa32c233b5ed40a 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) * The new attribute macro --- CHANGELOG.md | 9 +- .../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 | 432 ++++++------ diesel/src/pg/metadata_lookup.rs | 5 +- diesel/src/sqlite/connection/mod.rs | 34 +- diesel/src/sqlite/connection/row.rs | 5 +- diesel/src/sqlite/expression/functions.rs | 25 +- .../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 +- ...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 | 614 +++++++++++------- diesel_derives/src/sql_function.rs | 27 + diesel_tests/tests/annotations.rs | 5 +- 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 +- 38 files changed, 928 insertions(+), 714 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a3dff67da41..cac056fc9da1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,18 @@ Increasing the minimal supported Rust version will always be coupled at least wi * Added embedded struct support for `AsChangeset` via `#[diesel(embed)]` * Added a `#[diesel(skip_update)]` attribute for the `AsChangeset` derive to skip updating a field present in the struct * Support for libsqlite3-sys 0.31.0 -* Add support for built-in PostgreSQL range operators and functions + * Add support for built-in PostgreSQL range operators and 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 support for built-in PostgreSQL range operators and functions +* Added support for various built-in PostgreSQL array functions * 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. * Generate `InstrumentationEvent::BeginTransaction` for immediate and exclusive transactions in SQLite * Added `wasm32-unknown-unknown` target support for sqlite backend. diff --git a/diesel/src/connection/statement_cache/strategy.rs b/diesel/src/connection/statement_cache/strategy.rs index 93f73166c4e8..da76e622f9b7 100644 --- a/diesel/src/connection/statement_cache/strategy.rs +++ b/diesel/src/connection/statement_cache/strategy.rs @@ -149,7 +149,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}; @@ -159,6 +158,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, @@ -210,8 +214,6 @@ mod tests_pg { assert_eq!(2, count_cache_calls(connection)); } - define_sql_function!(fn lower(x: VarChar) -> VarChar); - #[diesel_test_helper::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 040f6f4e8a1f..a7b937781f47 100644 --- a/diesel/src/lib.rs +++ b/diesel/src/lib.rs @@ -735,6 +735,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 7830e98e15ba..04a8e431e2b0 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 and the item designated by path does not exist. /// @@ -2475,14 +2443,16 @@ 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; -#[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. @@ -2575,12 +2545,14 @@ define_sql_function! { #[sql_name = "jsonb_set"] fn jsonb_set_create_if_missing< E: JsonbOrNullableJsonb + SingleValue, - Arr: TextArrayOrNullableTextArray + CombinedNullableValue - >(base: E, path: Arr, new_value: E, create_if_missing: Bool) -> Arr::Out; -} + Arr: TextArrayOrNullableTextArray + CombinedNullableValue, + >( + base: E, + path: Arr, + new_value: E, + create_if_missing: Bool, + ) -> Arr::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 and the item designated by path does not exist. /// @@ -2663,12 +2635,15 @@ define_sql_function! { /// ``` fn jsonb_set_lax< E: JsonbOrNullableJsonb + SingleValue, - Arr: TextArrayOrNullableTextArray + CombinedNullableValue, - >(base: E, path: Arr, new_value: E, create_if_missing: Bool, null_value_treatment: NullValueTreatmentEnum) -> Arr::Out; -} + Arr: TextArrayOrNullableTextArray + CombinedNullableValue, + >( + base: E, + path: Arr, + new_value: E, + create_if_missing: Bool, + null_value_treatment: NullValueTreatmentEnum, + ) -> Arr::Out; -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns target with `new_value` inserted into `base`. /// /// If the item designated by the `path` is an array element, `new_value` will be inserted before that item @@ -2726,10 +2701,7 @@ define_sql_function! { path: Arr, new_value: E, ) -> Arr::Out; -} -#[cfg(feature = "postgres_backend")] -define_sql_function! { /// Returns target with `new_value` inserted into `base`. /// /// If the item designated by the `path` is an array element, `new_value` will be inserted before that 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 3de85462a47f..c51aa42803e7 100644 --- a/diesel/src/sqlite/connection/mod.rs +++ b/diesel/src/sqlite/connection/mod.rs @@ -580,12 +580,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; + } + #[diesel_test_helper::test] fn database_serializes_and_deserializes_successfully() { let expected_users = vec![ @@ -621,9 +634,6 @@ mod tests { assert_eq!(expected_users, actual_users); } - use crate::sql_types::Text; - define_sql_function!(fn fun_case(x: Text) -> Text); - #[diesel_test_helper::test] fn register_custom_function() { let connection = &mut connection(); @@ -647,8 +657,6 @@ mod tests { assert_eq!("fOoBaR", mapped_string); } - define_sql_function!(fn my_add(x: Integer, y: Integer) -> Integer); - #[diesel_test_helper::test] fn register_multiarg_function() { let connection = &mut connection(); @@ -658,8 +666,6 @@ mod tests { assert_eq!(Ok(3), added); } - define_sql_function!(fn answer() -> Integer); - #[diesel_test_helper::test] fn register_noarg_function() { let connection = &mut connection(); @@ -678,8 +684,6 @@ mod tests { assert_eq!(Ok(42), answer); } - define_sql_function!(fn add_counter(x: Integer) -> Integer); - #[diesel_test_helper::test] fn register_nondeterministic_function() { let connection = &mut connection(); @@ -695,11 +699,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, @@ -765,11 +764,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 5056924c3f39..7852253b0f92 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; + } #[diesel_test_helper::test] #[cfg(feature = "returning_clauses_for_sqlite_3_35")] diff --git a/diesel/src/sqlite/expression/functions.rs b/diesel/src/sqlite/expression/functions.rs index cc609d15fe88..f5bc1e99bd4d 100644 --- a/diesel/src/sqlite/expression/functions.rs +++ b/diesel/src/sqlite/expression/functions.rs @@ -1,5 +1,5 @@ //! SQLite specific functions -use crate::expression::functions::define_sql_function; +use crate::expression::functions::declare_sql_function; use crate::sql_types::*; use crate::sqlite::expression::expression_methods::BinaryOrNullableBinary; use crate::sqlite::expression::expression_methods::JsonOrNullableJsonOrJsonbOrNullableJsonb; @@ -7,7 +7,8 @@ use crate::sqlite::expression::expression_methods::MaybeNullableValue; use crate::sqlite::expression::expression_methods::TextOrNullableText; #[cfg(feature = "sqlite")] -define_sql_function! { +#[declare_sql_function] +extern "SQL" { /// Verifies that its argument is a valid JSON string or JSONB blob and returns a minified /// version of that JSON string with all unnecessary whitespace removed. /// @@ -47,10 +48,7 @@ define_sql_function! { /// # } /// ``` fn json>(e: E) -> E::Out; -} -#[cfg(feature = "sqlite")] -define_sql_function! { /// The jsonb(X) function returns the binary JSONB representation of the JSON provided as argument X. /// /// # Example @@ -105,10 +103,7 @@ define_sql_function! { /// # } /// ``` fn jsonb>(e: E) -> E::Out; -} -#[cfg(feature = "sqlite")] -define_sql_function! { /// Converts the given json value to pretty-printed, indented text /// /// # Example @@ -248,11 +243,10 @@ define_sql_function! { /// # Ok(()) /// # } /// ``` - fn json_pretty>(j: J) -> J::Out; -} + fn json_pretty>( + j: J, + ) -> J::Out; -#[cfg(feature = "sqlite")] -define_sql_function! { /// Converts the given json value to pretty-printed, indented text /// /// # Example @@ -420,5 +414,10 @@ define_sql_function! { /// # } /// ``` #[sql_name = "json_pretty"] - fn json_pretty_with_indentation>(j: J, indentation: Nullable) -> J::Out; + fn json_pretty_with_indentation< + J: JsonOrNullableJsonOrJsonbOrNullableJsonb + MaybeNullableValue, + >( + j: J, + indentation: Nullable, + ) -> J::Out; } diff --git a/diesel/src/sqlite/types/date_and_time/chrono.rs b/diesel/src/sqlite/types/date_and_time/chrono.rs index 12e7fda4797a..9eb7148ff00b 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; + } #[diesel_test_helper::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 3721c454cad1..7436225d89cc 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; + } #[diesel_test_helper::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 569ef01830d1..0f8ec06edfdc 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` | = 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` | = 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` | = 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/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 dc1cc4e44475..cc32a124f4a9 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` | | | 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` | | | 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 74f24dd47f61..46cf38f47553 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); | ---- ^^^^^^^^^^^^^^^ unsatisfied trait bound | | | required by a bound introduced by this call @@ -33,9 +33,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); | ---- ^^^^^^^^^^^^^^^ unsatisfied trait bound | | | required by a bound introduced by this call @@ -59,9 +59,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); | ---- ^^^^^^^^^^^^^^^ unsatisfied trait bound | | | required by a bound introduced by this call @@ -85,12 +85,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 e7fd4dd8667f..c0a647811d99 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 `Never`, found `Once` | 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` | = 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 `Never`, found `Once` | 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` | = 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 `Text`, found `Nullable` | = note: expected struct `diesel::sql_types::Text` @@ -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 `Never`, found `Once` | 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` | = 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 `Never`, found `Once` | 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` | = 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 `Text`, found `Nullable` | = note: expected struct `diesel::sql_types::Text` @@ -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 `Never`, found `Once` | 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` | = 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 `Never`, found `Once` | 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` | = 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 `Text`, found `Nullable` | = note: expected struct `diesel::sql_types::Text` @@ -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 `Never`, found `Once` | 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` | = 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 `Never`, found `Once` | 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` | = 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 `Text`, found `Nullable` - | - = 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:25 + | +101 | let _ = join.select(lower(posts::title.nullable())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Nullable` + | + = note: expected struct `diesel::sql_types::Text` + found struct `Nullable` + = 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 `Never`, found `Once` | 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` | = 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 `Never`, found `Once` | 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` | = 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 `Text`, found `Nullable` | = note: expected struct `diesel::sql_types::Text` 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 9b07cfa5a877..2ba19fcf33a9 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 `Text`, found `Integer` | = 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 8659d2d3d11b..169adc8e6d6b 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; @@ -1015,6 +1016,10 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream { /// This macro enables you to add additional functions from the SQL standard, /// as well as any custom functions your application might have. /// +/// This is a legacy variant of the [`#[declare_sql_function]`] attribute macro, which +/// should be preferred instead. It will generate the same code as the attribute macro +/// and also it will accept the same syntax as the other macro. +/// /// 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) @@ -1078,253 +1083,6 @@ pub fn derive_valid_grouping(input: TokenStream) -> TokenStream { /// This can be used to represent functions which can take many argument /// types, or to capitalize function names. /// -/// Functions can also be generic. Take the definition of `sum`, for example: -/// -/// ```no_run -/// # 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; -/// } -/// -/// # fn main() { -/// # use self::crates::dsl::*; -/// crates.select(sum(id)); -/// # } -/// ``` -/// -/// # 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: -/// -/// ```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()); -/// # } -/// ``` -/// -/// # 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::*; -/// # -/// # #[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:")?; -/// -/// 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(()) -/// # } -/// ``` -/// -/// ## 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). -/// -/// ## 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; -/// # 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; -/// -/// define_sql_function! { -/// #[aggregate] -/// fn my_sum(x: Integer) -> Integer; -/// } -/// -/// #[derive(Default)] -/// struct MySum { sum: i32 } -/// -/// # #[cfg(feature = "sqlite")] -/// impl SqliteAggregateFunction for MySum { -/// type Output = i32; -/// -/// fn step(&mut self, expr: i32) { -/// self.sum += expr; -/// } -/// -/// fn finalize(aggregator: Option) -> Self::Output { -/// aggregator.map(|a| a.sum).unwrap_or_default() -/// } -/// } -/// # table! { -/// # players { -/// # id -> Integer, -/// # score -> Integer, -/// # } -/// # } -/// -/// # #[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(); -/// -/// my_sum_utils::register_impl::(connection)?; -/// -/// let total_score = players.select(my_sum(score)) -/// .get_result::(connection)?; -/// -/// println!("The total score of all the players is: {}", total_score); -/// -/// # assert_eq!(60, total_score); -/// Ok(()) -/// } -/// ``` -/// -/// 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; -/// -/// define_sql_function! { -/// #[aggregate] -/// fn range_max(x0: Float, x1: Float) -> Nullable; -/// } -/// -/// #[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 -/// } -/// } -/// # 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] pub fn define_sql_function(input: TokenStream) -> TokenStream { sql_function::expand(parse_macro_input!(input), false).into() @@ -1914,3 +1672,365 @@ pub fn auto_type( 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. +/// +/// 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. +/// +/// 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. +/// +/// 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 +/// } +/// ``` +/// +/// will generate this code: +/// +/// ```ignore +/// pub fn lower(x: X) -> lower { +/// ... +/// } +/// +/// pub type lower = ...; +/// ``` +/// +/// Most attributes given to this macro will be put on the generated function +/// (including doc comments). +/// +/// # Adding Doc Comments +/// +/// ```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() { +/// # 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 +/// +/// There are a handful of special attributes that Diesel will recognize. They +/// are: +/// +/// - `#[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. +/// +/// Functions can also be generic. Take the definition of `sum`, for example: +/// +/// ```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; +/// +/// #[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)); +/// # } +/// ``` +/// +/// # 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: +/// +/// ```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; +/// } +/// +/// # 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")] +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # #[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")] +/// # 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(()) +/// # } +/// ``` +/// +/// ## 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). +/// +/// ## 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; +/// # use diesel::*; +/// # use diesel::expression::functions::declare_sql_function; +/// # +/// # #[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; +/// +/// #[declare_sql_function] +/// extern "SQL" { +/// #[aggregate] +/// fn my_sum(x: Integer) -> Integer; +/// } +/// +/// #[derive(Default)] +/// struct MySum { sum: i32 } +/// +/// # #[cfg(feature = "sqlite")] +/// impl SqliteAggregateFunction for MySum { +/// type Output = i32; +/// +/// fn step(&mut self, expr: i32) { +/// self.sum += expr; +/// } +/// +/// fn finalize(aggregator: Option) -> Self::Output { +/// aggregator.map(|a| a.sum).unwrap_or_default() +/// } +/// } +/// # table! { +/// # players { +/// # id -> Integer, +/// # score -> Integer, +/// # } +/// # } +/// +/// # #[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(); +/// +/// my_sum_utils::register_impl::(connection)?; +/// +/// let total_score = players.select(my_sum(score)) +/// .get_result::(connection)?; +/// +/// println!("The total score of all the players is: {}", total_score); +/// +/// # assert_eq!(60, total_score); +/// Ok(()) +/// } +/// ``` +/// +/// With multiple function arguments, the arguments are passed as a tuple to `SqliteAggregateFunction` +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # use diesel::expression::functions::declare_sql_function; +/// # +/// # #[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; +/// +/// #[declare_sql_function] +/// extern "SQL" { +/// #[aggregate] +/// fn range_max(x0: Float, x1: Float) -> Nullable; +/// } +/// +/// #[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 +/// } +/// } +/// # 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 declare_sql_function( + _attr: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + 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() + } + } +} 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 edb354db51f2..7ddb3d19e7ae 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; +} #[diesel_test_helper::test] #[cfg(feature = "postgres")] diff --git a/diesel_tests/tests/expressions/mod.rs b/diesel_tests/tests/expressions/mod.rs index d8fabdab0c5b..7372c9dd777d 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; +} + +#[declare_sql_function] +#[cfg(feature = "postgres")] +extern "SQL" { + fn unnest(a: sql_types::Array) -> sql_types::Int4; +} + #[diesel_test_helper::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); - #[diesel_test_helper::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); - #[diesel_test_helper::test] #[cfg(feature = "postgres")] fn test_arrays_b() { diff --git a/diesel_tests/tests/filter.rs b/diesel_tests/tests/filter.rs index f56743a50341..2f77174cbf2e 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; +} #[diesel_test_helper::test] fn filter_by_boxed_predicate() { diff --git a/diesel_tests/tests/joins.rs b/diesel_tests/tests/joins.rs index 47ec0f4bf95c..efea0c800c26 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::*; #[diesel_test_helper::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; +} #[diesel_test_helper::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 6ec3f588b0ed..0859affb050e 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; +} #[diesel_test_helper::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); - #[diesel_test_helper::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;