> ## 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.

# Creating Extensions in Rust

> Get started with the VillageSQL Rust SDK — write extension functions in safe Rust, package them as VEB files, and install them in VillageSQL.

> [!NOTE]
>
> If you prefer C++, see [Creating Extensions in C++](https://villagesql.com/docs/mysql-8.4/0.0.6/create.md) for the C++ SDK walkthrough.

VillageSQL extensions can be written in Rust using the `villagesql` crate. The SDK handles all FFI marshaling — you work in ordinary Rust types and the `extension!` macro generates the C entry points the server calls at load time.

## Prerequisites

Before you begin, build VillageSQL from source — extensions link against the server's build tree. Follow the [Build from Source](https://villagesql.com/docs/mysql-8.4/0.0.6/source.md) guide first.

You also need:

* **Rust stable toolchain** — install at [rustup.rs](https://rustup.rs)
* **Git** — for cloning the SDK and your extension repo
* **cargo-vsql** — the Cargo subcommand for packaging, installing, and testing extensions
* **VillageSQL build directory** — set `VillageSQL_BUILD_DIR` to your server build path for `cargo vsql install` and `cargo vsql test`
* **Basic Rust knowledge** — comfort with Cargo, enums, and pattern matching

Install `cargo-vsql` from the SDK repo:

```bash theme={null}
git clone https://github.com/villagesql/vsql-rust-sdk
cd vsql-rust-sdk
cargo install --path cargo-vsql
```

Verify it's available:

```bash theme={null}
cargo vsql --help
```

## Create a new crate

Create a new Rust library crate and add the SDK:

```bash theme={null}
cargo new --lib vsql_rot13
cd vsql_rot13
cargo add villagesql
```

Edit `Cargo.toml` to set the crate type:

```toml theme={null}
[lib]
crate-type = ["cdylib"]
```

The `cdylib` crate type tells Cargo to produce a shared library (`.so` on Linux, `.dylib` on macOS) that the server can load.

## Write your first function

Replace `src/lib.rs` with a complete extension:

```rust theme={null}
use villagesql::{InValue, VdfReturn};

fn rot13_impl(args: &[InValue]) -> VdfReturn {
    match args.first() {
        Some(InValue::String(s)) => VdfReturn::string(rot13(s)),
        Some(InValue::Null) | None => VdfReturn::null(),
        _ => VdfReturn::error("vsql_rot13: expected a STRING argument"),
    }
}

fn rot13(s: &str) -> String {
    s.chars()
        .map(|c| match c {
            'a'..='m' | 'A'..='M' => (c as u8 + 13) as char,
            'n'..='z' | 'N'..='Z' => (c as u8 - 13) as char,
            _ => c,
        })
        .collect()
}

villagesql::extension! {
    funcs: [
        villagesql::func!(rot13_impl, "vsql_rot13", [villagesql::Type::String] -> villagesql::Type::String),
    ]
}
```

The `func!` macro wires `rot13_impl` to the SQL name `vsql_rot13` with a `STRING -> STRING` signature.

The function signature is `fn(&[InValue]) -> VdfReturn`. `InValue` is an enum over the SQL types the server passes in. `VdfReturn` is what you send back. Check `args.first()` to handle both the NULL case and the wrong-type case before touching the value.

If your function always returns the same output for the same inputs, declare it deterministic — the optimizer can then cache results for identical inputs:

```rust theme={null}
villagesql::func!(rot13_impl, "vsql_rot13", [villagesql::Type::String] -> villagesql::Type::String, deterministic: true)
```

## Add manifest.json

Create `manifest.json` in the crate root (alongside `Cargo.toml`):

```json theme={null}
{
  "name": "vsql_rot13",
  "version": "0.1.0",
  "description": "ROT-13 encoding for VillageSQL",
  "author": "Your Name",
  "license": "GPL-2.0"
}
```

The server reads this at install time. The `name` field must match what you pass to `INSTALL EXTENSION`.

## Build and install

> [!NOTE]
>
> Run `cargo vsql package`, `cargo vsql install`, and `cargo vsql test` from inside the extension directory (where `Cargo.toml` and `manifest.json` live), not from the workspace root.

Package the extension into a `.veb` file:

```bash theme={null}
cargo vsql package
```

This produces `dist/vsql_rot13.veb`. To package and copy the VEB directly to your VillageSQL build directory:

```bash theme={null}
export VillageSQL_BUILD_DIR=/path/to/villagesql/build
cargo vsql install
```

To patch a dependency to a local checkout during development, pass `--config KEY=VALUE` (repeatable):

```bash theme={null}
cargo vsql install --config 'patch.crates-io.villagesql.path="/path/to/villagesql"'
```

You should see the VEB copied to the extensions directory. Verify it's there:

```bash theme={null}
ls "$VillageSQL_BUILD_DIR/veb_output_directory/"
# vsql_rot13.veb
```

## Test

Write a test file in `mysql-test/t/rot13_basic.test`:

```sql theme={null}
INSTALL EXTENSION vsql_rot13;
SELECT vsql_rot13('Hello');
SELECT vsql_rot13('');
SELECT vsql_rot13(NULL);
UNINSTALL EXTENSION vsql_rot13;
```

Generate the expected results:

```bash theme={null}
cargo vsql test --record
```

Run the suite:

```bash theme={null}
cargo vsql test
```

After modifying your function's behavior, re-run `cargo vsql test --record` to update the expected results, then `cargo vsql test` to confirm.

## Install in SQL

Once the VEB is in the extensions directory, install it:

```sql theme={null}
INSTALL EXTENSION vsql_rot13;
```

Verify:

```sql theme={null}
SELECT * FROM INFORMATION_SCHEMA.EXTENSIONS WHERE EXTENSION_NAME = 'vsql_rot13';
```

Call it:

```sql theme={null}
SELECT vsql_rot13('Hello, World!');
-- → Uryyb, Jbeyq!
```

## Next steps

> **[Custom Types in Rust](https://villagesql.com/docs/mysql-8.4/0.0.6/rust-custom-types.md)**
>
> Define new column types with binary storage, ordering, and hashing.

> **[Rust API Reference](https://villagesql.com/docs/mysql-8.4/0.0.6/rust-api-reference.md)**
>
> InValue, VdfReturn, extension!, func!, and custom\_type! — all fields.

> **[Preview Capabilities in Rust](https://villagesql.com/docs/mysql-8.4/0.0.6/rust-preview-capabilities.md)**
>
> Status variables, system variables, background thread workers, and
> keyring access via the requires: list.

> **[C++ SDK (Creating Extensions)](https://villagesql.com/docs/mysql-8.4/0.0.6/create.md)**
>
> The C++ path — typed wrappers, builder API, and CMake setup.

> **[Extension Architecture](https://villagesql.com/docs/mysql-8.4/0.0.6/architecture.md)**
>
> How VEB files load, lifecycle hooks, and symbol isolation.

> **[Testing Network-Dependent Extensions](https://villagesql.com/docs/mysql-8.4/0.0.6/testing-network.md)**
>
> MTR port patterns for extensions that spawn HTTP servers or external listeners — applies equally to Rust and C++ extensions.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.