# How to Create and Load Custom Extensions in Turso

> Learn to create and load custom extensions in Turso using Rust. Define, compile, and dynamically load shared libraries for SQLite-compatible extensions with the turso_ext macro system.

- Repository: [Turso Database/turso](https://github.com/tursodatabase/turso)
- Tags: how-to-guide
- Published: 2026-06-22

---

**Turso supports SQLite-compatible extensions written in Rust that you can define using the `turso_ext` macro system, compile as shared libraries, and dynamically load into active database connections.**

Creating and loading custom extensions in Turso allows you to extend SQL functionality with scalar functions, aggregates, virtual tables, or VFS modules written in Rust. The extension system mirrors SQLite's native extension mechanism while providing a type-safe, Rust-centric API. This guide walks through the complete lifecycle from writing your first extension in `tursodatabase/turso` to loading it via SQL, the Rust SDK, or the CLI.

## Creating a Custom Extension

The extension lifecycle begins with defining a Rust crate that exports a C-ABI entry point using Turso's macro system.

### Writing the Rust Entry Point

Your extension must export a `register_extension` function that Turso's loader can discover and invoke. The `turso_ext` crate provides the `register_extension!` macro to generate this boilerplate automatically.

In [`macros/src/ext/mod.rs`](https://github.com/tursodatabase/turso/blob/main/macros/src/ext/mod.rs), the macro generates a C-ABI function named `register_extension` (or `register_extension_static` for static linking) that receives a pointer to Turso's extension API. Inside the macro, you declare the functions, aggregates, or virtual tables you want to expose to SQL.

Each exported function is a standard Rust `fn` that receives `&[Value]` and returns a `Value`. Here is the structure from the built-in regexp extension at [`extensions/regexp/src/lib.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/regexp/src/lib.rs):

```rust
use turso_ext::{register_extension, scalar, Value, ValueType};

register_extension! {
    scalars: { regexp, regexp_like, regexp_substr, regexp_replace, regexp_capture }
}

#[scalar(name = "regexp")]
fn regexp(args: &[Value]) -> Value {
    // Implementation receives arguments and returns a Value
}

```

The macro expands to a C-ABI function that initializes your extension when the library is loaded.

### Compiling the Shared Library

To create a loadable extension, configure your [`Cargo.toml`](https://github.com/tursodatabase/turso/blob/main/Cargo.toml) to build a dynamic library:

```toml
[lib]
name = "my_extension"
crate-type = ["cdylib"]

```

Build with `cargo build --release`. On Linux and macOS, this produces `libmy_extension.so`; on Windows, `my_extension.dll`. The resulting file must contain the `register_extension` symbol that Turso's loader resolves at runtime.

## Loading Extensions at Runtime

Turso loads extensions dynamically through a secure mechanism that manages library lifetimes and schema updates.

### Enabling Extension Loading

By default, extension loading is disabled for security. You must explicitly enable it before calling `load_extension`.

In the Rust SDK ([`sdk-kit/src/rsapi.rs`](https://github.com/tursodatabase/turso/blob/main/sdk-kit/src/rsapi.rs)), toggle the flag:

```rust
conn.set_load_extension_enabled(true);

```

In SQL, extensions load only if the flag is already enabled:

```sql
SELECT load_extension('my_extension.so');

```

The CLI REPL implements enabling in [`cli/app.rs`](https://github.com/tursodatabase/turso/blob/main/cli/app.rs) via the `handle_load_extension` method.

### The Loading Mechanism

The core implementation resides in [`core/ext/dynamic.rs`](https://github.com/tursodatabase/turso/blob/main/core/ext/dynamic.rs). The `Connection::load_extension` method (lines 39-77) performs these steps:

1. Builds a Turso extension API struct for the connection.
2. Dynamically loads the `.so` file via `libloading::Library`.
3. Resolves the `register_extension` symbol.
4. Invokes the entry point, passing a pointer to the API.
5. Stores the library in a static `OnceLock<Arc<Mutex<Vec<(Arc<Library>, ExtensionApiRef)>>>>` to prevent unloading and keep symbols valid for the process lifetime.
6. Re-parses the database schema so new functions become immediately visible to SQL queries.

This mechanism ensures that extension functions persist as long as the connection remains open without leaking memory or allowing unsafe library unloading.

## Practical Examples

### Minimal Scalar Function Extension

Here is a complete custom extension that exposes a `my_len` function returning the character count of a text value:

```rust
// src/lib.rs
use turso_ext::{register_extension, scalar, Value, ValueType};

register_extension! { scalars: { my_len } }

#[scalar(name = "my_len")]
fn my_len(args: &[Value]) -> Value {
    match args.get(0) {
        Some(v) if v.value_type() == ValueType::Text => {
            let txt = v.to_text().unwrap_or_default();
            Value::from_integer(txt.chars().count() as i64)
        }
        _ => Value::null(),
    }
}

```

With [`Cargo.toml`](https://github.com/tursodatabase/turso/blob/main/Cargo.toml) configured as a `cdylib`, build and locate `target/release/libmy_len.so`.

### Loading from the Rust SDK

Use the SDK to programmatically load your extension:

```rust
use turso_sdk::Connection;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let conn = Connection::open("file:my.db?mode=memory")?;
    conn.set_load_extension_enabled(true);
    conn.load_extension("target/release/libmy_len.so")?;
    
    // The function is now available
    let rows = conn.execute("SELECT my_len('turso')")?;
    println!("{:?}", rows); // Returns 5
    Ok(())
}

```

The underlying call routes to `Connection::load_extension` in [`core/ext/dynamic.rs`](https://github.com/tursodatabase/turso/blob/main/core/ext/dynamic.rs), which handles the dynamic linking and registration.

### Loading from the CLI

In the `tursodb` REPL, use the dot command:

```bash
$ tursodb repl
> .load_extension target/release/libmy_len.so
Loaded extension from 'target/release/libmy_len.so'
> SELECT my_len('database');
9

```

The REPL invokes `handle_load_extension` at [`cli/app.rs`](https://github.com/tursodatabase/turso/blob/main/cli/app.rs) (lines 374-381) to process the command and confirm successful loading.

## Summary

- **Define extensions** using the `turso_ext` macro system in Rust, which generates the required `register_extension` C-ABI entry point.
- **Compile as `cdylib`** to produce `.so` (Linux/macOS) or `.dll` (Windows) files containing the registration symbol.
- **Enable loading** via `set_load_extension_enabled(true)` before calling `load_extension` on the connection.
- **Load dynamically** through [`core/ext/dynamic.rs`](https://github.com/tursodatabase/turso/blob/main/core/ext/dynamic.rs), which manages library lifetimes in a static `OnceLock` store and re-parses the schema to expose new functions.
- **Access via SQL**, Rust SDK, or CLI REPL once loaded, with functions immediately available to queries.

## Frequently Asked Questions

### What file format must Turso extensions use?

Turso extensions must be compiled as dynamic shared libraries. On Linux and macOS, use `.so` files; on Windows, use `.dll` files. Configure your [`Cargo.toml`](https://github.com/tursodatabase/turso/blob/main/Cargo.toml) with `crate-type = ["cdylib"]` to generate the correct format.

### Can I unload an extension after loading it?

No. Turso intentionally prevents unloading extensions to ensure memory safety. The loader stores each library in a static `EXTENSIONS` vector using `OnceLock` and `Arc` to keep the library alive for the process lifetime. This guarantees that function pointers and virtual table modules remain valid.

### Why does my extension function not appear in SQL immediately?

You must enable extension loading before attempting to load. Call `set_load_extension_enabled(true)` on the connection or use the equivalent SQL pragma. The `load_extension` function in [`core/ext/dynamic.rs`](https://github.com/tursodatabase/turso/blob/main/core/ext/dynamic.rs) automatically re-parses the schema after successful registration, so functions become visible immediately after the load call completes successfully.

### Does Turso support SQLite extensions written in C?

Turso supports SQLite-compatible extensions written in Rust using the `turso_ext` macro system. While the extension system is designed to be compatible with SQLite's extension mechanism, the recommended approach is writing Rust code that uses the `Value` type and `register_extension!` macro for type safety and integration with Turso's specific API.