diesel-rs/diesel · error · syn::Error

expect `SQL` function blocks to be safe

Error message

expect `SQL` function blocks to be safe

What it means

Diesel's `sql_function!` foreign block must be safe; declaring it `unsafe extern "SQL"` is rejected in `parse`. SQL functions are not real FFI and never produce unsafe code, so the `unsafe` keyword is meaningless and disallowed.

Solutions

  1. Remove the `unsafe` keyword from the `extern "SQL"` block.
  2. Keep the block plain: `extern "SQL" { ... }`.

Example fix

// before
sql_function! {
    unsafe extern "SQL" {
        fn md5(x: Text) -> Text;
    }
}

// after
sql_function! {
    extern "SQL" {
        fn md5(x: Text) -> Text;
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Reject unsafe in sql_function blocks before compiling:
// WRONG: unsafe extern "SQL" { ... }
// RIGHT: extern "SQL" { ... }

Prevention

When it happens

Trigger: Writing `unsafe extern "SQL" { ... }` inside `sql_function!` or `#[sql_function]` macro input.

Common situations: Copy-pasting an `unsafe extern "C"` FFI block and only changing the ABI string to `SQL`; assuming SQL function declarations need `unsafe` like normal externs.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of diesel-rs/diesel@6fa6ed01b2 (2026-09-07). Data as JSON: /api/errors/c2213d37846fbe27. Report an issue: GitHub.

Appendix: source

Thrown at diesel_derives/src/sql_function.rs:1180

        let mut combine_error = |e: syn::Error| {
            error = Some(
                error
                    .take()
                    .map(|mut o| {
                        o.combine(e.clone());
                        o
                    })
                    .unwrap_or(e),
            )
        };

        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 let Some(unsafety) = block.unsafety {
            return Err(syn::Error::new(
                unsafety.span(),
                "expect `SQL` function blocks to be safe",
            ));
        }

        let parsed_block_attrs = parse_attributes(&mut combine_error, block.attrs);

        let item_count = block.items.len();
        let function_decls_input = block
            .items
            .into_iter()
            .map(|i| syn::parse2::<SqlFunctionDecl>(quote! { #i }));

        let mut function_decls = Vec::with_capacity(item_count);
        for decl in function_decls_input {
            match decl {
                Ok(mut decl) => {
                    decl.attributes = merge_attributes(&parsed_block_attrs, decl.attributes);

View on GitHub (pinned to 6fa6ed01b2)