> ## Documentation Index
> Fetch the complete documentation index at: https://villagesql.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust API Reference

> Complete reference for the VillageSQL Rust SDK — InValue, VdfReturn, extension!, func!, custom_type!, custom!, and manifest.json fields.

<Warning>
  The Rust SDK is in alpha — expect breaking API changes between
  releases. Function-only extensions and custom types (encode, decode,
  compare, hash) are supported. Aggregates, `prerun()`, `VarArgs`, system
  and status variables, keyring access, and the column storage ABI are
  C++-only today — use the [C++ SDK](/mysql-8.4/0.0.5/create) if you
  need any of those.
</Warning>

This page is a reference for the `villagesql` crate API. For the getting started tutorial, see [Creating Extensions in Rust](/mysql-8.4/0.0.5/rust-sdk). For custom types, see [Custom Types in Rust](/mysql-8.4/0.0.5/rust-custom-types).

## InValue

`InValue` is the enum the server passes for each function argument. Your function receives `args: &[InValue]` and must check each argument before using its value.

```rust theme={null}
pub enum InValue<'a> {
    String(&'a str),
    Real(f64),
    Int(i64),
    Null,
    Custom(&'a [u8]),
}
```

| Variant         | Rust type             | Corresponding SQL type                        |
| --------------- | --------------------- | --------------------------------------------- |
| `String(&str)`  | UTF-8 string slice    | `STRING` / `VARCHAR` / `TEXT`                 |
| `Real(f64)`     | 64-bit float          | `REAL` / `DOUBLE` / `FLOAT`                   |
| `Int(i64)`      | 64-bit signed integer | `INT` / `BIGINT` / `TINYINT`                  |
| `Null`          | —                     | SQL `NULL` for any type                       |
| `Custom(&[u8])` | Raw binary bytes      | Any custom type registered via `custom_type!` |

Always match on `Null` explicitly. Calling `.unwrap()` or pattern-matching only the value variants is a bug — SQL NULL is a normal input, not an error.

## VdfReturn

`VdfReturn` is what your function returns to the server. Construct it with one of the associated functions:

| Constructor                | SQL effect                                                          |
| -------------------------- | ------------------------------------------------------------------- |
| `VdfReturn::null()`        | Returns SQL NULL for this row                                       |
| `VdfReturn::string(s)`     | Returns a `String` value; `s` is `impl Into<String>`                |
| `VdfReturn::real(v)`       | Returns an `f64` value                                              |
| `VdfReturn::int(v)`        | Returns an `i64` value                                              |
| `VdfReturn::binary(bytes)` | Returns binary bytes for a custom type column; `bytes` is `Vec<u8>` |
| `VdfReturn::warning(msg)`  | Returns NULL for this row, adds a SQL warning, execution continues  |
| `VdfReturn::error(msg)`    | Aborts the statement with a fatal error                             |

**Warning vs error:**

Use `warning` for user-input validation failures where continuing with the rest of the result set makes sense. In strict mode, MySQL promotes warnings to errors on `INSERT` and `UPDATE`. Use `error` for conditions where proceeding is unsafe — corrupt stored data, internal invariant violations. A fatal error aborts the entire statement.

```rust theme={null}
fn validate_impl(args: &[InValue]) -> VdfReturn {
    match args.first() {
        Some(InValue::Int(n)) if *n >= 0 => VdfReturn::int(*n),
        Some(InValue::Int(_)) => VdfReturn::warning("value must be non-negative"),
        Some(InValue::Null) | None => VdfReturn::null(),
        _ => VdfReturn::error("validate: expected an INT argument"),
    }
}
```

## extension! macro

`extension!` generates the VEF entry points the server calls when loading your VEB file. It must appear exactly once in the crate.

```rust theme={null}
villagesql::extension! {
    funcs: [
        // One or more villagesql::func!(...) declarations
    ],
    types: [
        // One or more villagesql::custom_type!(...) declarations
    ]
}
```

Both sections are optional. A pure-function extension omits `types:`; a type-only extension omits `funcs:`. An empty `extension!` block (no funcs, no types) is valid but produces an extension that does nothing.

## func! macro

`func!` declares a SQL-callable function. Four forms (no parameters, `buffer_size` only, `deterministic` only, both):

```rust theme={null}
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, buffer_size: N)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, deterministic: true)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, buffer_size: N, deterministic: true)
```

<Note>
  The `buffer_size` parameter requires the `villagesql` crate **0.0.2 or
  later**. The current [crates.io](https://crates.io/crates/villagesql) release
  (`0.0.1`) doesn't expose it — until `0.0.2` ships, use the forms without
  `buffer_size`.
</Note>

| Argument              | Description                                                                                                                                                                                                                                                                                           |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `rust_fn`             | The Rust function implementing the VDF. Signature: `fn(&[InValue]) -> VdfReturn`.                                                                                                                                                                                                                     |
| `"sql_name"`          | The SQL function name as a string literal. This is what users call from SQL.                                                                                                                                                                                                                          |
| `[param_types]`       | Comma-separated list of `villagesql::Type::*` or `villagesql::custom!("name")` values. Use `[]` for zero-arity functions.                                                                                                                                                                             |
| `return_type`         | `villagesql::Type::*` or `villagesql::custom!("name")`.                                                                                                                                                                                                                                               |
| `buffer_size: N`      | Optional. Size in bytes of the result buffer for string/binary returns. Use `0` for the server default (256 bytes). When a function returns a string or binary value larger than `buffer_size`, the function errors rather than truncating — declare a larger `buffer_size` to handle larger results. |
| `deterministic: true` | Optional. Declares the function deterministic — same inputs always produce the same output, no side effects. The optimizer can cache results for identical inputs. Only set this when it's true.                                                                                                      |

**Type constants** for use in `func!`:

| `villagesql::Type::*`      | SQL type |
| -------------------------- | -------- |
| `villagesql::Type::String` | `STRING` |
| `villagesql::Type::Real`   | `REAL`   |
| `villagesql::Type::Int`    | `INT`    |

## custom\_type! macro

`custom_type!` registers a new column type. `type_name`, `persisted_length`, `max_decode_buffer_length`, `encode`, `decode`, and `compare` are required. `hash` and `default` are optional but recommended.

```rust theme={null}
villagesql::custom_type!(
    type_name: "sql_type_name",
    persisted_length: N,
    max_decode_buffer_length: M,
    encode: encode_fn,
    decode: decode_fn,
    compare: compare_fn,
    hash: hash_fn,
    default: "default_string",
)
```

| Field                      | Type                                     | Description                                                                                                                                          |
| -------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type_name`                | `&str` literal                           | SQL name for the type. Case-insensitive in SQL. Must be unique among all installed extensions.                                                       |
| `persisted_length`         | `usize`                                  | Fixed byte length for on-disk storage. All encoded values must produce exactly this many bytes.                                                      |
| `max_decode_buffer_length` | `usize`                                  | Maximum byte length of the decoded string. Used to size the output buffer before calling `decode`.                                                   |
| `encode`                   | `fn(&str) -> Result<Vec<u8>, String>`    | Called at `INSERT` time. Converts a SQL string literal to binary. Return `Err(msg)` to reject the input.                                             |
| `decode`                   | `fn(&[u8]) -> Result<String, String>`    | Called to display the value. Converts binary back to a string.                                                                                       |
| `compare`                  | `fn(&[u8], &[u8]) -> std::cmp::Ordering` | Called for `ORDER BY`, `MIN`, `MAX`. Return `Less`, `Equal`, or `Greater`.                                                                           |
| `hash`                     | `fn(&[u8]) -> usize`                     | Optional. Called for `COUNT(DISTINCT)` and set operations. Values that compare `Equal` must hash to the same value. Recommended for indexed columns. |
| `default`                  | `&str` literal                           | Optional. A valid string the server encodes at type initialization to verify the callback works. Must encode to exactly `persisted_length` bytes.    |

The `default` field is not a column default value — it's a startup probe. The server calls `encode(default)` when loading the extension to verify the callback works. If `encode` returns `Err` for the default, the extension fails to load.

## custom! macro

`villagesql::custom!("type_name")` references a custom type by name in a `func!` declaration:

```rust theme={null}
villagesql::func!(
    my_fn,
    "my_sql_func",
    [villagesql::custom!("mytype")] -> villagesql::custom!("mytype"),
    deterministic: true
)
```

Use it anywhere a `villagesql::Type::*` would appear in a parameter list or return type position. The string must match the `type_name` declared in the corresponding `custom_type!`.

## manifest.json fields

Every extension needs a `manifest.json` alongside its `Cargo.toml`:

```json theme={null}
{
  "name": "vsql_my_extension",
  "version": "0.1.0",
  "description": "Brief description of what the extension does",
  "author": "Your Name",
  "license": "GPL-2.0"
}
```

| Field         | Required | Format                              | Description                                                                                                               |
| ------------- | -------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `name`        | Yes      | lowercase letters, digits, `_`, `-` | Extension identifier. Must match the `INSTALL EXTENSION` name. Use underscores — hyphens require backtick quoting in SQL. |
| `version`     | Yes      | MAJOR.MINOR.PATCH                   | Semantic version.                                                                                                         |
| `description` | No       | String                              | Shown in `INFORMATION_SCHEMA.EXTENSIONS`.                                                                                 |
| `author`      | No       | String                              | Author name or organization.                                                                                              |
| `license`     | No       | String                              | License identifier. `GPL-2.0` recommended for open-source extensions.                                                     |

`name` validation rules: must start with a letter, end with a letter or digit, max 64 characters. An invalid manifest causes `INSTALL EXTENSION` to fail.
