#[declare_sql_function]
Expand description
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
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:
#[declare_sql_function]
extern "SQL" {
fn lower(x: Text) -> Text
}
will generate this code:
pub fn lower<X>(x: X) -> lower<X> {
...
}
pub type lower<X> = ...;
Most attributes given to this macro will be put on the generated function (including doc comments).
If the generate_return_type_helpers
attribute is specified, an additional module named
return_type_helpers
will be generated, containing all return type helpers. For more
information, refer to the Helper types generation
section.
§Adding Doc Comments
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;
}
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.
- Indicates that this is an aggregate function, and that
#[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.
#[variadic(argument_count)]
- Indicates that this is a variadic function, where
argument_count
is a nonnegative integer representing the number of variadic arguments the function accepts.
- Indicates that this is a variadic function, where
Functions can also be generic. Take the definition of sum
, for example:
use diesel::sql_types::Foldable;
#[declare_sql_function]
extern "SQL" {
#[aggregate]
#[sql_name = "SUM"]
fn sum<ST: Foldable>(expr: ST) -> ST::Sum;
}
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:
#[declare_sql_function]
extern "SQL" {
fn random() -> Text;
}
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.
use diesel::sql_types::{Double, Integer};
#[declare_sql_function]
extern "SQL" {
fn add_mul(x: Integer, y: Integer, z: Double) -> Double;
}
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::<f64>(connection)?;
assert_eq!(4.5, result);
§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 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
trait as a type parameter as shown in the examples below.
use diesel::sql_types::Integer;
use diesel::sqlite::SqliteAggregateFunction;
#[declare_sql_function]
extern "SQL" {
#[aggregate]
fn my_sum(x: Integer) -> Integer;
}
#[derive(Default)]
struct MySum { sum: i32 }
impl SqliteAggregateFunction<i32> for MySum {
type Output = i32;
fn step(&mut self, expr: i32) {
self.sum += expr;
}
fn finalize(aggregator: Option<Self>) -> Self::Output {
aggregator.map(|a| a.sum).unwrap_or_default()
}
}
fn run() -> Result<(), Box<dyn (::std::error::Error)>> {
let connection = &mut SqliteConnection::establish(":memory:")?;
my_sum_utils::register_impl::<MySum, _>(connection)?;
let total_score = players.select(my_sum(score))
.get_result::<i32>(connection)?;
println!("The total score of all the players is: {}", total_score);
Ok(())
}
With multiple function arguments, the arguments are passed as a tuple to SqliteAggregateFunction
use diesel::sql_types::{Float, Nullable};
use diesel::sqlite::SqliteAggregateFunction;
#[declare_sql_function]
extern "SQL" {
#[aggregate]
fn range_max(x0: Float, x1: Float) -> Nullable<Float>;
}
#[derive(Default)]
struct RangeMax<T> { max_value: Option<T> }
impl<T: Default + PartialOrd + Copy + Clone> SqliteAggregateFunction<(T, T)> for RangeMax<T> {
type Output = Option<T>;
fn step(&mut self, (x0, x1): (T, T)) {
// Compare self.max_value to x0 and x1
}
fn finalize(aggregator: Option<Self>) -> Self::Output {
aggregator?.max_value
}
}
fn run() -> Result<(), Box<dyn (::std::error::Error)>> {
let connection = &mut SqliteConnection::establish(":memory:")?;
range_max_utils::register_impl::<RangeMax<f32>, _, _>(connection)?;
let result = student_avgs.select(range_max(s1_avg, s2_avg))
.get_result::<Option<f32>>(connection)?;
if let Some(max_semester_avg) = result {
println!("The largest semester average is: {}", max_semester_avg);
}
Ok(())
}
§Variadic functions
Since Rust does not support variadic functions, the SQL variadic functions are
handled differently. For example, consider the variadic function json_array
.
To add support for it, you can use the #[variadic]
attribute:
#[declare_sql_function]
extern "SQL" {
#[variadic(1)]
fn json_array<V: SqlType + SingleValue>(value: V) -> Json;
}
This will generate multiple implementations, one for each possible argument
count (up to a predefined limit). For instance, it will generate functions like
json_array_0
, json_array_1
, and so on, which are equivalent to:
#[declare_sql_function]
extern "SQL" {
#[sql_name = "json_array"]
fn json_array_0() -> Json;
#[sql_name = "json_array"]
fn json_array_1<V1: SqlType + SingleValue>(value_1: V1) -> Json;
#[sql_name = "json_array"]
fn json_array_2<V1: SqlType + SingleValue, V2: SqlType + SingleValue>(
value_1: V1,
value_2: V2,
) -> Json;
// ...
}
The argument to the variadic
attribute specifies the number of trailing arguments to repeat.
For example, if you have a variadic function foo(a: A, b: B, c: C)
and want b: B
and c: C
to repeat, you would write:
#[declare_sql_function]
extern "SQL" {
#[variadic(2)]
fn foo<A, B, C>(a: A, b: B, c: C) -> Text;
}
Which will be equivalent to
#[declare_sql_function]
extern "SQL" {
#[sql_name = "foo"]
fn foo_0<A>(a: A) -> Text;
#[sql_name = "foo"]
fn foo_1<A, B1, C1>(a: A, b_1: B1, c_1: C1) -> Text;
#[sql_name = "foo"]
fn foo_2<A, B1, C1, B2, C2>(a: A, b_1: B1, c_1: C1, b_2: B2, c_2: C2) -> Text;
...
}
§Controlling the generation of variadic function variants
By default, only variants with 0, 1, and 2 repetitions of variadic arguments are generated. To
generate more variants, set the DIESEL_VARIADIC_FUNCTION_ARGS
environment variable to the
desired number of variants.
For a greater convenience this environment variable can also be set in a .cargo/config.toml
file as described in the cargo documentation.
§Helper types generation
When the generate_return_type_helpers
attribute is specified, for each function defined inside
an extern "SQL"
block, a return type alias with the same name as the function is created and
placed in the return_type_helpers
module:
#[declare_sql_function(generate_return_type_helpers = true)]
extern "SQL" {
fn f<V: SqlType + SingleValue>(arg: V);
}
type return_type_helper_for_f<V> = return_type_helpers::f<V>;
If you want to skip generating a type alias for a specific function, you can use the
#[skip_return_type_helper]
attribute, like this:
#[declare_sql_function(generate_return_type_helpers = true)]
extern "SQL" {
#[skip_return_type_helper]
fn f();
}
§Expanded Code
Expanded Code
§Input
#[diesel::declare_sql_function]
extern "SQL" {
fn lower(input: Text) -> Text;
}
§Expanded Code
The macro expands the input to the following Rust code:
#[allow(non_camel_case_types)]
pub fn lower<input>(input: input) -> lower<input>
where
input: diesel::expression::AsExpression<Text>,
{
lower_utils::lower {
input: input.as_expression(),
}
}
#[allow(non_camel_case_types, non_snake_case)]
///The return type of [`lower()`](super::fn_name)
pub type lower<input> = lower_utils::lower<
<input as diesel::expression::AsExpression<Text>>::Expression,
>;
#[doc(hidden)]
#[allow(non_camel_case_types, non_snake_case, unused_imports)]
pub(crate) mod lower_utils {
use diesel::{self, QueryResult};
use diesel::expression::{
AsExpression, Expression, SelectableExpression, AppearsOnTable, ValidGrouping,
};
use diesel::query_builder::{QueryFragment, AstPass};
use diesel::sql_types::*;
use diesel::internal::sql_functions::*;
use super::*;
#[derive(Debug, Clone, Copy, diesel::query_builder::QueryId)]
#[derive(diesel::sql_types::DieselNumericOps)]
pub struct lower<input> {
pub(super) input: input,
}
///The return type of [`lower()`](super::fn_name)
pub type HelperType<input> = lower<<input as AsExpression<Text>>::Expression>;
impl<input> Expression for lower<input>
where
(input): Expression,
{
type SqlType = Text;
}
impl<input, __DieselInternal> SelectableExpression<__DieselInternal> for lower<input>
where
input: SelectableExpression<__DieselInternal>,
Self: AppearsOnTable<__DieselInternal>,
{}
impl<input, __DieselInternal> AppearsOnTable<__DieselInternal> for lower<input>
where
input: AppearsOnTable<__DieselInternal>,
Self: Expression,
{}
impl<input, __DieselInternal> FunctionFragment<__DieselInternal> for lower<input>
where
__DieselInternal: diesel::backend::Backend,
input: QueryFragment<__DieselInternal>,
{
const FUNCTION_NAME: &'static str = "lower";
#[allow(unused_assignments)]
fn walk_arguments<'__b>(
&'__b self,
mut out: AstPass<'_, '__b, __DieselInternal>,
) -> QueryResult<()> {
let mut needs_comma = false;
if !self.input.is_noop(out.backend())? {
if needs_comma {
out.push_sql(", ");
}
self.input.walk_ast(out.reborrow())?;
needs_comma = true;
}
Ok(())
}
}
impl<input, __DieselInternal> QueryFragment<__DieselInternal> for lower<input>
where
__DieselInternal: diesel::backend::Backend,
input: QueryFragment<__DieselInternal>,
{
fn walk_ast<'__b>(
&'__b self,
mut out: AstPass<'_, '__b, __DieselInternal>,
) -> QueryResult<()> {
out.push_sql(<Self as FunctionFragment<__DieselInternal>>::FUNCTION_NAME);
out.push_sql("(");
self.walk_arguments(out.reborrow())?;
out.push_sql(")");
Ok(())
}
}
#[derive(ValidGrouping)]
pub struct __Derived<input>(input);
impl<input, __DieselInternal> ValidGrouping<__DieselInternal> for lower<input>
where
__Derived<input>: ValidGrouping<__DieselInternal>,
{
type IsAggregate = <__Derived<
input,
> as ValidGrouping<__DieselInternal>>::IsAggregate;
}
use diesel::sqlite::{Sqlite, SqliteConnection};
use diesel::serialize::ToSql;
use diesel::deserialize::{FromSqlRow, StaticallySizedRow};
#[allow(dead_code)]
/// Registers an implementation for this function on the given connection
///
/// This function must be called for every `SqliteConnection` before
/// this SQL function can be used on SQLite. The implementation must be
/// deterministic (returns the same result given the same arguments). If
/// the function is nondeterministic, call
/// `register_nondeterministic_impl` instead.
pub fn register_impl<F, Ret, input>(
conn: &mut SqliteConnection,
f: F,
) -> QueryResult<()>
where
F: Fn(input) -> Ret + std::panic::UnwindSafe + Send + 'static,
(input,): FromSqlRow<(Text,), Sqlite> + StaticallySizedRow<(Text,), Sqlite>,
Ret: ToSql<Text, Sqlite>,
{
conn.register_sql_function::<
(Text,),
Text,
_,
_,
_,
>("lower", true, move |(input,)| f(input))
}
#[allow(dead_code)]
/// Registers an implementation for this function on the given connection
///
/// This function must be called for every `SqliteConnection` before
/// this SQL function can be used on SQLite.
/// `register_nondeterministic_impl` should only be used if your
/// function can return different results with the same arguments (e.g.
/// `random`). If your function is deterministic, you should call
/// `register_impl` instead.
pub fn register_nondeterministic_impl<F, Ret, input>(
conn: &mut SqliteConnection,
mut f: F,
) -> QueryResult<()>
where
F: FnMut(input) -> Ret + std::panic::UnwindSafe + Send + 'static,
(input,): FromSqlRow<(Text,), Sqlite> + StaticallySizedRow<(Text,), Sqlite>,
Ret: ToSql<Text, Sqlite>,
{
conn.register_sql_function::<
(Text,),
Text,
_,
_,
_,
>("lower", false, move |(input,)| f(input))
}
}