define_sql_function

Macro define_sql_function 

Source
define_sql_function!() { /* proc-macro */ }
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.

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

This macro will generate two items. A function with the name that you’ve given, and a module with a helper type representing the return type of your function. For example, this invocation:

define_sql_function!(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).

§Adding Doc Comments

use diesel::sql_types::Text;

define_sql_function! {
    /// Represents the `canon_crate_name` SQL function, created in
    /// migration ....
    fn canon_crate_name(a: Text) -> Text;
}

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.

§Expanded Code

Expanded Code
§Input
define_sql_function! {
    fn lower(input : Text) -> Text;
}
§Expanded Code
Expanded code might use diesel internal API's and is only shown for educational purpose

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))
    }
}