Repository: diesel-rs/diesel (language: rust)

Issue:
sqlite: not possible to use SQL functions without Arguments
## Setup

### Versions

- **Rust:** 1.52.1
- **Diesel:** master
- **Database:** Sqlite
- **Operating System:** Linux

### Feature Flags

- **diesel:** sqlite

## Problem Description

[This condition](https://github.com/diesel-rs/diesel/blob/7587a3bda93bcbe8d44a22f8537da157b953ae5e/diesel_derives/src/sql_function.rs#L245) skips the code generation of `register_impl` and `register_nondeterministic_impl` for SQL functions without arguments. Because the [documentation](https://docs.diesel.rs/master/diesel/prelude/macro.sql_function.html#sql-functions-without-arguments) doesn't mention any limitations for Sqlite SQL functions without arguments, I'm assuming this to be a bug. 

I've tracked down the [commit that introduced that regression]( https://github.com/diesel-rs/diesel/commit/dd05ee2588f80f240896bd8c95cd8539d706d7e7#diff-3dc6bb01ed73137c473d075bf6f9ae482857b07fdfd5aa7d95691705daaebc6bR235) but its commit message doesn't help to understand why the condition was changed.

### What are you trying to accomplish?

I want to use a SQL function without arguments with Sqlite.

### What is the expected output?

No errors.

### What is the actual output?

> cannot find function `register_nondeterministic_impl` in module `foo`

### Are you seeing any additional errors?

No.

### Steps to reproduce

```rs
sql_function! {
    fn foo() -> Text;
}
fn main() {
    let conn = diesel::SqliteConnection::establish(":memory:");
    foo::register_impl(&conn, || "foo".to_string());
}
```

## Checklist

- [x] I have already looked over the [issue tracker](https://github.com/diesel-rs/diesel/issues)  and the [disussion forum](https://github.com/diesel-rs/diesel/discussions)  for similar possible closed issues.
<!--
If you are unsure if your issue is a duplicate of an existing issue please link the issue in question here
--> 
- [x] This issue can be reproduced on Rust's stable channel. (Your issue will be
  closed if this is not the case)
- [x] This issue can be reproduced without requiring a third party crate



Discussion / hints:
Technically speaking this is a feature request, as no version of diesel did support sqlite functions without argument. The check in question is there because otherwise the proc macro generates invalid code for sql functions without argument. 
We are happy to receive an PR to fix this, otherwise at least for me personally that's nothing that has priority right now.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f131e917d8af..4daecf91e391 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -62,6 +62,8 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
 
 * Add support for HAVING clauses.
 
+* Added support for SQL functions without arguments for SQLite.
+
 ### Removed
 
 * All previously deprecated items have been removed.
diff --git a/diesel/src/sqlite/connection/functions.rs b/diesel/src/sqlite/connection/functions.rs
index 11453e72c627..79034c73a885 100644
--- a/diesel/src/sqlite/connection/functions.rs
+++ b/diesel/src/sqlite/connection/functions.rs
@@ -40,6 +40,24 @@ where
     Ok(())
 }
 
+pub fn register_noargs<RetSqlType, Ret, F>(
+    conn: &RawConnection,
+    fn_name: &str,
+    deterministic: bool,
+    mut f: F,
+) -> QueryResult<()>
+where
+    F: FnMut() -> Ret + std::panic::UnwindSafe + Send + 'static,
+    Ret: ToSql<RetSqlType, Sqlite>,
+    Sqlite: HasSqlType<RetSqlType>,
+{
+    conn.register_sql_function(fn_name, 0, deterministic, move |_, _| {
+        let result = f();
+        process_sql_function_result::<RetSqlType, Ret>(result)
+    })?;
+    Ok(())
+}
+
 pub fn register_aggregate<ArgsSqlType, RetSqlType, Args, Ret, A>(
     conn: &RawConnection,
     fn_name: &str,
diff --git a/diesel/src/sqlite/connection/mod.rs b/diesel/src/sqlite/connection/mod.rs
index 1ed61b4d14e1..6b7d9ac9e30a 100644
--- a/diesel/src/sqlite/connection/mod.rs
+++ b/diesel/src/sqlite/connection/mod.rs
@@ -240,6 +240,21 @@ impl SqliteConnection {
         )
     }
 
+    #[doc(hidden)]
+    pub fn register_noarg_sql_function<RetSqlType, Ret, F>(
+        &self,
+        fn_name: &str,
+        deterministic: bool,
+        f: F,
+    ) -> QueryResult<()>
+    where
+        F: FnMut() -> Ret + std::panic::UnwindSafe + Send + 'static,
+        Ret: ToSql<RetSqlType, Sqlite>,
+        Sqlite: HasSqlType<RetSqlType>,
+    {
+        functions::register_noargs(&self.raw_connection, fn_name, deterministic, f)
+    }
+
     #[doc(hidden)]
     pub fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
         &mut self,
@@ -413,6 +428,26 @@ mod tests {
         assert_eq!(Ok(3), added);
     }
 
+    sql_function!(fn answer() -> Integer);
+
+    #[test]
+    fn register_noarg_function() {
+        let connection = &mut SqliteConnection::establish(":memory:").unwrap();
+        answer::register_impl(&connection, || 42).unwrap();
+
+        let answer = crate::select(answer()).get_result::<i32>(connection);
+        assert_eq!(Ok(42), answer);
+    }
+
+    #[test]
+    fn register_nondeterministic_noarg_function() {
+        let connection = &mut SqliteConnection::establish(":memory:").unwrap();
+        answer::register_nondeterministic_impl(&connection, || 42).unwrap();
+
+        let answer = crate::select(answer()).get_result::<i32>(connection);
+        assert_eq!(Ok(42), answer);
+    }
+
     sql_function!(fn add_counter(x: Integer) -> Integer);
 
     #[test]
diff --git a/diesel_derives/src/sql_function.rs b/diesel_derives/src/sql_function.rs
index d1a276869aa9..9fc66b648478 100644
--- a/diesel_derives/src/sql_function.rs
+++ b/diesel_derives/src/sql_function.rs
@@ -302,6 +302,66 @@ pub(crate) fn expand(input: SqlFunctionDecl) -> Result<TokenStream, Diagnostic>
                 }
             };
         }
+
+        if cfg!(feature = "sqlite")
+            && type_args.is_empty()
+            && arg_name.is_empty()
+            && is_sqlite_type(&return_type)
+        {
+            tokens = quote! {
+                #tokens
+
+                use diesel::sqlite::{Sqlite, SqliteConnection};
+                use diesel::serialize::ToSql;
+
+                #[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>(
+                    conn: &SqliteConnection,
+                    f: F,
+                ) -> QueryResult<()>
+                where
+                    F: Fn() -> Ret + std::panic::UnwindSafe + Send + 'static,
+                    Ret: ToSql<#return_type, Sqlite>,
+                {
+                    conn.register_noarg_sql_function::<#return_type, _, _>(
+                        #sql_name,
+                        true,
+                        f,
+                    )
+                }
+
+                #[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>(
+                    conn: &SqliteConnection,
+                    mut f: F,
+                ) -> QueryResult<()>
+                where
+                    F: FnMut() -> Ret + std::panic::UnwindSafe + Send + 'static,
+                    Ret: ToSql<#return_type, Sqlite>,
+                {
+                    conn.register_noarg_sql_function::<#return_type, _, _>(
+                        #sql_name,
+                        false,
+                        f,
+                    )
+                }
+            };
+        }
     }
 
     let args_iter = args.iter();
@@ -392,3 +452,21 @@ impl ToTokens for StrictFnArg {
         self.name.to_tokens(tokens);
     }
 }
+
+fn is_sqlite_type(ty: &syn::Type) -> bool {
+    [
+        "BigInt",
+        "Binary",
+        "Bool",
+        "Date",
+        "Double",
+        "Float",
+        "Integer",
+        "Numeric",
+        "SmallInt",
+        "Text",
+        "Time",
+        "Timestamp",
+    ]
+    .contains(&quote!(#ty).to_string().as_str())
+}


Test patch: