# How to Extend Turso with Virtual Tables (vtab): Implementation Guide

> Learn how to extend Turso functionality with virtual tables vtab. Explore pragma, external, and internal mechanisms to enhance query capabilities without core engine modifications.

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

---

**Turso supports three virtual table mechanisms—pragma, external, and internal—that let developers extend queryable functionality without modifying the core storage engine.**

Turso, the open-source edge database maintained by `tursodatabase/turso`, exposes a modular **virtual table (vtab)** subsystem for adding custom table-like objects. By implementing specific traits or dynamic library interfaces, you can expose system metadata, integrate external data sources, or create lightweight Rust-based tables that participate in SQL queries alongside regular tables.

## Types of Virtual Tables in Turso

Turso categorizes virtual tables based on where the implementation lives and how it is loaded:

| Kind | Location | Typical Use |
|------|----------|-------------|
| **Pragma** | Built-in tables exposing pragma information | Read-only system metadata |
| **External** | Dynamic library loaded at runtime | Custom extensions (CSV, regexp, crypto) |
| **Internal** | Rust structs implementing `InternalVirtualTable` | Lightweight tables exposing internal state |

The core type unifying all implementations is `VirtualTable` in [[`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs)](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs#L19-L25). It stores the table name, column metadata, a `VTabKind` discriminant, and a `VirtualTableType` that determines cursor construction during query execution.

## Implementing Internal Virtual Tables in Rust

**Internal virtual tables** are pure-Rust objects that implement the `InternalVirtualTable` trait defined in [`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs). This approach requires no external shared libraries and keeps the extension code compiled directly into your Turso application.

### The InternalVirtualTable Trait

To create an internal table, implement the following interface:

```rust
fn name(&self) -> String;
fn sql(&self) -> String;  // CREATE TABLE statement for column parsing
fn open(&self, conn: Arc<Connection>) -> Result<Arc<RwLock<dyn InternalVirtualTableCursor>>>;
fn best_index(&self, constraints: &[turso_ext::ConstraintInfo],
              order_by: &[turso_ext::OrderByInfo]) -> Result<turs_ext::IndexInfo, ResultCode>;

```

### Registration Flow

1. Implement `InternalVirtualTable` for your struct
2. Call `Database::register_internal_vtab` (defined in [[`core/lib.rs`](https://github.com/tursodatabase/turso/blob/main/core/lib.rs)](https://github.com/tursodatabase/turso/blob/main/core/lib.rs#L2704-L2708))
3. This forwards to `Schema::register_internal_vtab` ([[`core/schema.rs`](https://github.com/tursodatabase/turso/blob/main/core/schema.rs)](https://github.com/tursodatabase/turso/blob/main/core/schema.rs#L956-L967)), which wraps your object via `VirtualTable::wrap_internal_table` and inserts it into the catalog

### Complete Rust Example

```rust
use turso::{Database, Connection, Value, Result};

/// Simple read-only internal table returning two rows.
#[derive(Debug)]
struct SimpleTable;

impl turso::vtab::InternalVirtualTable for SimpleTable {
    fn name(&self) -> String { 
        "simple_demo".into() 
    }
    
    fn sql(&self) -> String { 
        "CREATE TABLE simple_demo(key TEXT, value INTEGER)".into() 
    }
    
    fn open(&self, _conn: std::sync::Arc<Connection>)
        -> turso::Result<std::sync::Arc<std::sync::RwLock<dyn turso::vtab::InternalVirtualTableCursor>>>
    {
        // Cursor implementation omitted—see the test suite's StaticCursor for a minimal example
        unimplemented!()
    }
    
    fn best_index(&self, _c: &[turso_ext::ConstraintInfo],
                  _o: &[turso_ext::OrderByInfo])
        -> std::result::Result<turso_ext::IndexInfo, ResultCode>
    {
        // No special indexing; any query plan works
        Ok(turso_ext::IndexInfo::default())
    }
}

fn main() -> Result<()> {
    let db = Database::open_file_with_flags(
        std::sync::Arc::new(turso::MemoryIO::new()),
        turso::util::MEMORY_PATH,
        turso::OpenFlags::Create,
        turso::DatabaseOpts::new(),
        None,
    )?;
    
    // Register the table; Turso returns the name actually inserted
    let name = db.register_internal_vtab(SimpleTable)?;
    assert_eq!(name, "simple_demo");

    let conn = db.connect()?;
    let mut stmt = conn.prepare("SELECT key, value FROM simple_demo")?;
    let rows = stmt.run_collect_rows()?;
    Ok(())
}

```

This pattern mirrors the repository's own test suite (`testing/system/vtab.test` and the `StaticTable`/`StaticCursor` structs in [`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs)).

## Creating External Virtual Table Extensions

**External virtual tables** reside in separate dynamic libraries that implement the `VTabModule` trait (see [[`extensions/core/src/vtabs.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/vtabs.rs)](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/vtabs.rs#L35-L45)). These extensions communicate with Turso through a C-compatible ABI defined in `VTabModuleImpl`.

### Extension Lifecycle

When Turso loads an extension via `Database::load_extension` ([[`core/lib.rs`](https://github.com/tursodatabase/turso/blob/main/core/lib.rs)](https://github.com/tursodatabase/turso/blob/main/core/lib.rs)), it registers the module name in the global `SymbolTable`. Upon executing `CREATE VIRTUAL TABLE ... USING module_name`, Turso calls `ExtVirtualTable::create` ([[`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs)](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs#L45-L70)), which:

1. Invokes the module's `create` entry point to obtain schema definitions and an instance pointer
2. Stores the pointer in an atomic field (`table_ptr`) for subsequent operations
3. Returns a `VirtualTable` of type `External`

The cursor implementation `ExtVirtualTableCursor` ([[`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs)](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs#L66-L100)) forwards `filter`, `column`, `next`, and `eof` calls to the function pointers supplied by your extension.

### External Module Skeleton (C-ABI)

```c
#include "turso_ext.h"

typedef struct MyTable {
    // custom state
} MyTable;

typedef struct MyCursor {
    MyTable *tbl;
    int pos;
} MyCursor;

VTabCreateResult my_create(const Value *args, int argc) {
    MyTable *tbl = malloc(sizeof(MyTable));
    const char *schema = "CREATE TABLE x(col TEXT, val INTEGER)";
    return (VTabCreateResult){
        .code = RESULT_OK,
        .schema = schema,
        .table = tbl
    };
}

void *my_open(void *table, const Conn *conn) {
    MyCursor *cur = malloc(sizeof(MyCursor));
    cur->tbl = table;
    cur->pos = -1;
    return cur;
}

ResultCode my_filter(void *cursor, int argc, const Value *argv,
                    const char *idx_str, int idx_num) {
    // Apply constraints, prepare iteration
    return RESULT_OK;
}

Value my_column(void *cursor, unsigned int col) {
    return Value_from_text("hello");
}

ResultCode my_next(void *cursor) {
    // Advance cursor; return RESULT_EOF at end
    return RESULT_OK;
}

ResultCode turso_register_module(void *ctx) {
    VTabModuleImpl impl = {
        .name = "my_mod",
        .readonly = false,
        .create = my_create,
        .open = my_open,
        .filter = my_filter,
        .column = my_column,
        .next = my_next,
        // ... other callbacks
    };
    return RegisterModule(ctx, "my_mod", impl, VTAB_KIND_VIRTUAL_TABLE);
}

```

### Loading External Extensions

After compiling the library (e.g., `libmy_mod.so`), load it at runtime:

```rust
let conn = db.connect()?;
conn.load_extension("libmy_mod.so")?;  // registers the module
conn.execute("CREATE VIRTUAL TABLE demo USING my_mod()")?;
let mut stmt = conn.prepare("SELECT * FROM demo")?;
let rows = stmt.run_collect_rows()?;

```

## Core Architecture and Wiring

Understanding how Turso wires virtual tables together helps debug extension behavior and optimize query plans:

- **`VirtualTable`** ([[`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs)](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs#L19-L25)): The unified representation holding table metadata and the `VirtualTableType` discriminant (pragma, external, or internal)

- **`VirtualTable::open`**: Dispatches to cursor constructors `new_pragma`, `new_external`, or `new_internal` based on the table type

- **`VirtualTableCursor`**: A thin wrapper trait that forwards `next`, `column`, `filter`, and `rowid` to concrete implementations (`PragmaVirtualTableCursor`, `ExtVirtualTableCursor`, or internal cursors)

- **`ExtVirtualTable`**: Holds the implementation pointer (`VTabModuleImpl`) and opaque table instance (`table_ptr`). Its methods invoke the extension's C functions (lines 45-71 in [`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs))

- **`VTabModule` trait and macros**: The `#[derive(VTabModule)]` macro in [[`macros/src/ext/vtab_derive.rs`](https://github.com/tursodatabase/turso/blob/main/macros/src/ext/vtab_derive.rs)](https://github.com/tursodatabase/turso/blob/main/macros/src/ext/vtab_derive.rs) generates the required `VTabModuleImpl` structure for Rust-based extensions

## Summary

- **Three virtual table kinds**: Pragma (system metadata), External (dynamic libraries), and Internal (Rust structs)

- **Internal registration**: Implement `InternalVirtualTable`, then call `Database::register_internal_vtab` to insert into the catalog via `Schema::register_internal_vtab`

- **External modules**: Implement `VTabModuleImpl` in a shared library, load with `Connection::load_extension`, and create tables using `CREATE VIRTUAL TABLE ... USING module_name`

- **Key implementation files**: [`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs) (core types and dispatch), [`core/schema.rs`](https://github.com/tursodatabase/turso/blob/main/core/schema.rs) (registration), [`core/lib.rs`](https://github.com/tursodatabase/turso/blob/main/core/lib.rs) (public API), and [`extensions/core/src/vtabs.rs`](https://github.com/tursodatabase/turso/blob/main/extensions/core/src/vtabs.rs) (module interface)

- **Cursor lifecycle**: Turso calls `open` to create a cursor, `filter` to apply constraints, then iterates with `next` and `column` until `eof` returns true

## Frequently Asked Questions

### What is the difference between internal and external virtual tables in Turso?

**Internal virtual tables** are Rust structs implementing the `InternalVirtualTable` trait that compile directly into your application binary, ideal for exposing lightweight state like metrics or configuration. **External virtual tables** are dynamic libraries implementing the C-compatible `VTabModuleImpl` interface, suitable for complex data sources or extensions written in other languages that must be loaded at runtime.

### How do I register a custom virtual table with Turso?

For internal tables, call `Database::register_internal_vtab` with your struct implementing `InternalVirtualTable`, which stores the table in the schema catalog via `Schema::register_internal_vtab`. For external tables, compile your extension as a shared library, load it using `Connection::load_extension`, then execute `CREATE VIRTUAL TABLE` with your module name.

### Can external virtual tables interact with regular Turso tables?

Yes. When Turso calls your extension's `open` callback ([[`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs)](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs)), it passes a `Conn` pointer that allows the extension to execute queries against the database. This enables joining external data with standard tables or accessing schema information from within the virtual table implementation.

### Where are virtual table cursor implementations defined in the source code?

Cursor implementations reside in [[`core/vtab.rs`](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs)](https://github.com/tursodatabase/turso/blob/main/core/vtab.rs). The file contains `ExtVirtualTableCursor` (lines 66-100) for external modules, `PragmaVirtualTableCursor` for system tables, and the `InternalVirtualTableCursor` trait for Rust-based internal tables. The `VirtualTableCursor` enum dispatches calls to these concrete implementations based on the virtual table type.