Skip to content

Commit

Permalink
Merge pull request diesel-rs#1983 from diesel-rs/sg-sql-function-proc
Browse files Browse the repository at this point in the history
Change `sql_function!` to be a proc macro
  • Loading branch information
sgrif authored Feb 16, 2019
2 parents 64faade + c2ba306 commit 03ec1d9
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 788 deletions.
2 changes: 1 addition & 1 deletion diesel/src/expression/count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ sql_function! {
///
/// ```rust
/// # #[macro_use] extern crate diesel;
/// # include!("../../doctest_setup.rs");
/// # include!("../doctest_setup.rs");
/// # use diesel::dsl::*;
/// #
/// # fn main() {
Expand Down
337 changes: 3 additions & 334 deletions diesel/src/expression/functions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,292 +1,3 @@
#[macro_export]
#[doc(hidden)]
macro_rules! __diesel_sql_function_body {
// Entry point with type arguments. Pull out the rest of the body.
(
data = (
meta = $meta:tt,
fn_name = $fn_name:tt,
),
type_args = $type_args:tt,
type_args_with_bounds = $type_args_with_bounds:tt,
unparsed_tokens = (
$args:tt -> $return_type:ty $(;)*
),
) => {
__diesel_sql_function_body! {
meta = $meta,
fn_name = $fn_name,
type_args = $type_args,
type_args_with_bounds = $type_args_with_bounds,
args = $args,
return_type = $return_type,
}
};

// Entry point. We need to search the meta items for our special attributes
(
meta = $meta:tt,
fn_name = $fn_name:ident,
$($rest:tt)*
) => {
__diesel_sql_function_body! {
aggregate = no,
sql_name = stringify!($fn_name),
unchecked_meta = $meta,
meta = (),
fn_name = $fn_name,
$($rest)*
}
};

// Found #[aggregate]
(
aggregate = $aggregate:tt,
sql_name = $sql_name:expr,
unchecked_meta = (#[aggregate] $($unchecked:tt)*),
meta = $meta:tt,
$($rest:tt)*
) => {
__diesel_sql_function_body! {
aggregate = yes,
sql_name = $sql_name,
unchecked_meta = ($($unchecked)*),
meta = $meta,
$($rest)*
}
};

// Found #[sql_name].
(
aggregate = $aggregate:tt,
sql_name = $ignored:expr,
unchecked_meta = (#[sql_name = $sql_name:expr] $($unchecked:tt)*),
meta = $meta:tt,
$($rest:tt)*
) => {
__diesel_sql_function_body! {
aggregate = $aggregate,
sql_name = $sql_name,
unchecked_meta = ($($unchecked)*),
meta = $meta,
$($rest)*
}
};

// Didn't find a special attribute
(
aggregate = $aggregate:tt,
sql_name = $sql_name:expr,
unchecked_meta = (#$checked:tt $($unchecked:tt)*),
meta = ($($meta:tt)*),
$($rest:tt)*
) => {
__diesel_sql_function_body! {
aggregate = $aggregate,
sql_name = $sql_name,
unchecked_meta = ($($unchecked)*),
meta = ($($meta)* #$checked),
$($rest)*
}
};

// Done searching for special attributes
(
aggregate = $aggregate:tt,
sql_name = $sql_name:expr,
unchecked_meta = (),
meta = ($($meta:tt)*),
fn_name = $fn_name:ident,
type_args = ($($type_args:ident,)*),
type_args_with_bounds = ($($type_args_bounds:tt)*),
args = ($($arg_name:ident: $arg_type:ty),*),
return_type = $return_type:ty,
) => {
$($meta)*
#[allow(non_camel_case_types)]
pub fn $fn_name<$($type_args_bounds)* $($arg_name),*>($($arg_name: $arg_name),*)
-> $fn_name::HelperType<$($type_args,)* $($arg_name),*>
where
$($arg_name: $crate::expression::AsExpression<$arg_type>),+
{
$fn_name::$fn_name {
$($arg_name: $arg_name.as_expression(),)+
$($type_args: ::std::marker::PhantomData,)*
}
}

#[doc(hidden)]
#[allow(non_camel_case_types, non_snake_case, unused_imports)]
pub(crate) mod $fn_name {
use super::*;
use $crate::sql_types::*;

#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps)]
pub struct $fn_name<$($type_args,)* $($arg_name),*> {
$(pub(in super) $arg_name: $arg_name,)*
$(pub(in super) $type_args: ::std::marker::PhantomData<$type_args>,)*
}

pub type HelperType<$($type_args,)* $($arg_name),*> = $fn_name<
$($type_args,)*
$(
<$arg_name as $crate::expression::AsExpression<$arg_type>>::Expression
),*
>;

impl<$($type_args_bounds)* $($arg_name),*>
$crate::expression::Expression
for $fn_name<$($type_args,)* $($arg_name),*>
where
($($arg_name),*): $crate::expression::Expression,
{
type SqlType = $return_type;
}

impl<$($type_args_bounds)* $($arg_name),*, DB>
$crate::query_builder::QueryFragment<DB>
for $fn_name<$($type_args,)* $($arg_name),*>
where
DB: $crate::backend::Backend,
for<'a> ($(&'a $arg_name),*): $crate::query_builder::QueryFragment<DB>,
{
fn walk_ast(&self, mut out: $crate::query_builder::AstPass<DB>) -> $crate::result::QueryResult<()> {
out.push_sql(concat!($sql_name, "("));
$crate::query_builder::QueryFragment::walk_ast(
&($(&self.$arg_name),*), out.reborrow())?;
out.push_sql(")");
Ok(())
}
}

impl<$($type_args_bounds)* $($arg_name),*, QS>
$crate::expression::SelectableExpression<QS>
for $fn_name<$($type_args,)* $($arg_name),*>
where
$($arg_name: $crate::expression::SelectableExpression<QS>,)*
Self: $crate::expression::AppearsOnTable<QS>,
{
}

impl<$($type_args_bounds)* $($arg_name),*, QS>
$crate::expression::AppearsOnTable<QS>
for $fn_name<$($type_args,)* $($arg_name),*>
where
$($arg_name: $crate::expression::AppearsOnTable<QS>,)*
Self: $crate::expression::Expression,
{
}

static_cond! {
if $aggregate == no {
impl<$($type_args_bounds)* $($arg_name),*>
$crate::expression::NonAggregate
for $fn_name<$($type_args,)* $($arg_name),*>
where
$($arg_name: $crate::expression::NonAggregate,)*
Self: $crate::expression::Expression,
{
}
}
}

__diesel_sqlite_register_fn! {
type_args = ($($type_args)*),
aggregate = $aggregate,
fn_name = $fn_name,
args = ($($arg_name,)+),
sql_args = ($($arg_type,)+),
ret = $return_type,
}
}
}
}

#[macro_export]
#[doc(hidden)]
#[cfg(feature = "sqlite")]
macro_rules! __diesel_sqlite_register_fn {
// We can't handle generic functions for SQLite
(
type_args = ($($type_args:tt)+),
$($rest:tt)*
) => {
};

// We don't currently support aggregate functions for SQLite
(
type_args = $ignored:tt,
aggregate = yes,
$($rest:tt)*
) => {
};

(
type_args = (),
aggregate = no,
fn_name = $fn_name:ident,
args = ($($args:ident,)+),
sql_args = $sql_args:ty,
ret = $ret:ty,
) => {
#[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, $($args,)+>(
conn: &$crate::SqliteConnection,
f: F,
) -> $crate::QueryResult<()>
where
F: Fn($($args,)+) -> Ret + Send + 'static,
($($args,)+): $crate::deserialize::Queryable<$sql_args, $crate::sqlite::Sqlite>,
Ret: $crate::serialize::ToSql<$ret, $crate::sqlite::Sqlite>,
{
conn.register_sql_function::<$sql_args, $ret, _, _, _>(
stringify!($fn_name),
true,
move |($($args,)+)| f($($args),+),
)
}

#[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, $($args,)+>(
conn: &$crate::SqliteConnection,
mut f: F,
) -> $crate::QueryResult<()>
where
F: FnMut($($args,)+) -> Ret + Send + 'static,
($($args,)+): $crate::deserialize::Queryable<$sql_args, $crate::sqlite::Sqlite>,
Ret: $crate::serialize::ToSql<$ret, $crate::sqlite::Sqlite>,
{
conn.register_sql_function::<$sql_args, $ret, _, _, _>(
stringify!($fn_name),
false,
move |($($args,)+)| f($($args),+),
)
}
};
}

#[macro_export]
#[doc(hidden)]
#[cfg(not(feature = "sqlite"))]
macro_rules! __diesel_sqlite_register_fn {
($($token:tt)*) => {};
}

#[macro_export]
/// Declare a sql function for use in your code.
///
Expand Down Expand Up @@ -452,51 +163,9 @@ macro_rules! __diesel_sqlite_register_fn {
/// # }
/// ```
macro_rules! sql_function {
($(#$meta:tt)* fn $fn_name:ident $args:tt $(;)*) => {
sql_function!($(#$meta)* fn $fn_name $args -> ());
};

($(#$meta:tt)* fn $fn_name:ident $args:tt -> $return_type:ty $(;)*) => {
sql_function!($(#$meta)* fn $fn_name <> $args -> $return_type);
};

(
$(#$meta:tt)*
fn $fn_name:ident
<
$($tokens:tt)*
) => {
__diesel_parse_type_args!(
data = (
meta = ($(#$meta)*),
fn_name = $fn_name,
),
callback = __diesel_sql_function_body,
tokens = ($($tokens)*),
);
};

($fn_name:ident, $struct_name:ident, $args:tt -> $return_type:ty) => {
sql_function!($fn_name, $struct_name, $args -> $return_type, "");
};

($fn_name:ident, $struct_name:ident, $args:tt -> $return_type:ty, $docs:expr) => {
sql_function!($fn_name, $struct_name, $args -> $return_type, $docs, "");
};

($fn_name:ident, $struct_name:ident, ($($arg_name:ident: $arg_type:ty),*)) => {
sql_function!($fn_name, $struct_name, ($($arg_name: $arg_type),*) -> ());
};

(
$fn_name:ident,
$struct_name:ident,
$args:tt -> $return_type:ty,
$docs:expr,
$helper_ty_docs:expr
) => {
sql_function_body!($fn_name, $struct_name, $args -> $return_type, $docs, $helper_ty_docs);
};
($($args:tt)*) => {
sql_function_proc! { $($args)* }
}
}

#[macro_export]
Expand Down
Loading

0 comments on commit 03ec1d9

Please sign in to comment.