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

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

<Note>
  If you prefer C++, see [Creating Extensions in C++](/mysql-8.4/0.0.5/create) for the C++ SDK walkthrough.
</Note>

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](/mysql-8.4/0.0.5/source) 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:

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

Edit `Cargo.toml` to set the crate type and add the `villagesql` dependency:

```toml theme={null}
[package]
name = "vsql_rot13"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
villagesql = "0.0.1"
```

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.
</Note>

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_dir/"
# 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

<CardGroup cols={2}>
  <Card title="Custom Types in Rust" icon="shapes" href="/mysql-8.4/0.0.5/rust-custom-types">
    Define new column types with binary storage, ordering, and hashing.
  </Card>

  <Card title="Rust API Reference" icon="book" href="/mysql-8.4/0.0.5/rust-api-reference">
    InValue, VdfReturn, extension!, func!, and custom\_type! — all fields.
  </Card>

  <Card title="C++ SDK (Creating Extensions)" icon="code" href="/mysql-8.4/0.0.5/create">
    The C++ path — typed wrappers, builder API, and CMake setup.
  </Card>

  <Card title="Extension Architecture" icon="sitemap" href="/mysql-8.4/0.0.5/architecture">
    How VEB files load, lifecycle hooks, and symbol isolation.
  </Card>

  <Card title="Testing Network-Dependent Extensions" icon="network-wired" href="/mysql-8.4/0.0.5/testing-network">
    MTR port patterns for extensions that spawn HTTP servers or external listeners — applies equally to Rust and C++ extensions.
  </Card>
</CardGroup>
