> ## 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 C++

> Learn how to create custom VillageSQL extensions in C++ using the extension template and VEF.

<Warning>
  VEF Protocol 3 is stable as of v0.0.4. Protocol 4 is under development and is available only via opt-in dev ABI headers (`-DVSQL_USE_DEV_ABI=ON`). Extensions built against the old Protocol 2 are rejected by the server and must be rebuilt.
</Warning>

## Overview

VillageSQL's extension framework (VEF) lets you add custom functionality to the database server. This guide walks through building an extension in C++ using the C++ SDK and the extension template.

For writing VDF implementations in depth — argument and result types, aggregates, system variables, parameterized types — see the [Development Guide](/docs/mysql-9.7/dev/development).

<Note>
  If you prefer Rust, see [Creating Extensions in Rust](/docs/mysql-9.7/dev/rust-sdk).
</Note>

## What is a VillageSQL Extension?

A VillageSQL extension is packaged as a **VEB file** (VillageSQL Extension Bundle) containing:

* **Manifest** - Metadata about the extension (name, version, description)
* **Shared library** - Compiled C++ code implementing the functionality
* **Optional metadata** - Additional resources or configuration

C++ extensions are built using the **C++ SDK**, the C++ binding for VEF (the VillageSQL Extension Framework). It provides:

* C++ API for defining types and functions
* Automatic registration without SQL scripts
* Type-safe argument and result types
* Builder pattern for extension definition

<Note>
  **VDFs vs Traditional UDFs:** Functions registered through VEF are called VDFs (VillageSQL Defined Functions). VillageSQL also supports traditional MySQL UDFs registered via `CREATE FUNCTION ... SONAME`, but VDFs are recommended for new extensions.
</Note>

### Calling VDFs in SQL

VDFs can be called with or without the extension prefix:

```sql theme={null}
-- Unqualified (preferred for cleaner code)
SELECT complex_abs(impedance) FROM signals;

-- Qualified with extension name (explicit)
SELECT vsql_complex.complex_abs(impedance) FROM signals;
```

**Function Resolution Order:**

When you call a function without qualification, VillageSQL resolves it in this order:

1. **System functions** (built-in MySQL functions like `NOW()`, `CONCAT()`)
2. **UDFs** (traditional MySQL user-defined functions)
3. **VDFs** (extension functions) - only if there's exactly one function with that name
4. **Stored functions** (created with `CREATE FUNCTION`)

**When to Use Qualified Names:**

* Use `extension.function_name` when multiple extensions provide functions with the same name
* Use unqualified names for cleaner code when there's no ambiguity
* Qualification is never required if only one extension provides that function name

Extensions can add:

* **Custom functions (VDFs)** - SQL functions with automatic type checking and validation
* **Custom data types** - New column types like COMPLEX, UUID, or VECTOR that work with ORDER BY and indexes
* **Type operations** - Encode, decode, compare, and hash functions for custom types

## Prerequisites

Before you begin, you need the VillageSQL Extension SDK, and there are two ways to point CMake at one. If you have a VillageSQL build tree, pass `-DVillageSQL_BUILD_DIR=/path/to/villagesql/build` — the [Build from Source](/docs/mysql-9.7/dev/source) guide produces that tree. If you have an unpacked SDK directory — one containing `include/villagesql/vsql.h` — that is enough on its own: pass `-DVillageSQL_SDK_DIR=/path/to/sdk`. The [extension template README](https://github.com/villagesql/vsql-extension-template) covers how to get an SDK without building the server. Only the build-tree form sets a VEB install directory, so on the SDK-only path copy the built VEB file yourself into the directory `SHOW VARIABLES LIKE 'veb_dir'` reports.

You also need:

* **Git** - For cloning and version control
* **CMake** 3.18 or higher - Build system
* **C++ Compiler** - With C++17 support: the template sets `CMAKE_CXX_STANDARD 17`, and the SDK's CMake package rejects a lower standard
* **Basic C++ knowledge** - Understanding of C++ and function pointers

<Tip>
  **Building with an AI agent?** The [`vsql-extension-builder`](https://github.com/villagesql/villagesql-skills) skill automates this entire workflow — from scaffolding to testing — using Claude Code, Gemini, or other supported agents. Install it with:

  ```bash theme={null}
  curl -sSL https://villagesql.com/skills | bash
  ```
</Tip>

## Step 1: Get the Extension Template

You can start with the extension template in two ways:

### Option A: Use Template from VillageSQL Source

If you have VillageSQL source code, the template is included:

```bash theme={null}
cd /path/to/villagesql-source
cp -r villagesql/sdk/template my-extension
cd my-extension
```

### Option B: Fork from GitHub

Start by forking the VillageSQL extension template repository:

1. Visit the template repository on GitHub:
   ```
   https://github.com/villagesql/vsql-extension-template
   ```

2. Click the **"Fork"** button to create your own copy

3. Clone your fork locally:
   ```bash theme={null}
   git clone https://github.com/YOUR_USERNAME/vsql-extension-template.git
   cd vsql-extension-template
   ```

<Tip>
  Alternatively, use the "Use this template" button on GitHub to create a new repository based on the template without forking history.
</Tip>

## Step 2: Update the Manifest

Edit `manifest.json` to define your extension's metadata:

```json theme={null}
{
  "$schema": "https://raw.githubusercontent.com/villagesql/villagesql-docs/main/mysql-9.7/dev/manifest.json.schema.json",
  "name": "my_awesome_extension",
  "version": "1.0.0",
  "description": "My custom VillageSQL extension that does amazing things",
  "author": "Your Name",
  "license": "GPL-2.0"
}
```

The `$schema` field is optional but enables IDE autocomplete and inline validation
for all manifest fields.

### manifest.json Schema

| Field         | Required | Format                    | Description                                             |
| ------------- | -------- | ------------------------- | ------------------------------------------------------- |
| `name`        | ✅ Yes    | letters, digits, `_`, `-` | Unique identifier. Must match `INSTALL EXTENSION` name. |
| `version`     | ✅ Yes    | MAJOR.MINOR.PATCH         | Semantic version string                                 |
| `description` | No       | String                    | Brief explanation of functionality                      |
| `author`      | No       | String                    | Author name or organization                             |
| `license`     | No       | String                    | License identifier (GPL-2.0 recommended)                |

**Validation rules:**

* `name`: Must start with a letter and end with a letter or digit. May contain lowercase letters, digits, underscores, and hyphens. Max 64 characters. **Use underscores** — hyphens require backtick quoting in SQL.
* `version`: Must follow semantic versioning (e.g., 1.0.0, 0.2.1)
* Invalid manifest will cause `INSTALL EXTENSION` to fail

**Example:**

```sql theme={null}
-- manifest.json has "name": "my_awesome_extension"
INSTALL EXTENSION my_awesome_extension;  -- ✅ Correct: no quoting needed
INSTALL EXTENSION `my-awesome-extension`;  -- ⚠️ Works, but requires backtick quoting
```

For the full naming convention across SQL, filenames, and repository names, see [Extension Naming Conventions](/docs/mysql-9.7/dev/install#extension-naming-conventions).

## Step 3: Implement Your Extension with the C++ SDK

The C++ SDK provides a C++ API for defining extensions using a fluent builder pattern:

* Type-safe function definitions with compile-time checking
* Automatic argument validation and type conversion
* Support for custom types with compare/hash functions (enables ORDER BY and indexes)

### Include VillageSQL Headers

Create your main extension file (e.g., `src/extension.cc`) and include the C++ SDK headers:

```cpp theme={null}
#include <villagesql/vsql.h>

// Your implementation code here
```

The `<villagesql/vsql.h>` header pulls in the type builder, function builder, and
extension builder, and re-exports commonly used symbols into the `vsql` namespace.

### Define Your Extension

Use the `VEF_GENERATE_ENTRY_POINTS()` macro to define your extension:

```cpp theme={null}
VEF_GENERATE_ENTRY_POINTS(
  make_extension()
    .func(make_func<&my_reverse_impl>("my_reverse")
      .returns(STRING)
      .param(STRING)
      .build())
    .func(make_func<&count_vowels_impl>("count_vowels")
      .returns(INT)
      .param(STRING)
      .build())
);
```

**Function Builder Methods:**

* `make_func<&impl>("name")` - Create function with implementation pointer
* `.returns(type)` - Set return type (STRING, INT, REAL, or custom type name)
* `.param(type)` - Add parameter (maximum 8 parameters)
* `.buffer_size(size_t)` - Request specific output buffer size for STRING/CUSTOM returns
* `.max_result_length(size_t)` - Size the result column for a STRING return so a materialized result isn't truncated at the argument width. Values above `VEF_MAX_RESULT_LENGTH` (16 MiB) are capped by the server. STRING only; call `.returns(STRING)` first. Requires Protocol 4 (dev ABI).
* `.deterministic(bool = true)` - Declare that this function always returns the same output for the same inputs and has no side effects. Default is non-deterministic.
* `.prerun<func>()` - Set per-statement setup function (optional)
* `.postrun<func>()` - Set per-statement cleanup function (optional)
* `.build()` - Finalize function registration

<Note>
  **Parameter Limit:** Functions support a maximum of 8 parameters (defined by `kMaxParams`). If you need more, consider using structured types or multiple functions.
</Note>

### VDFs with Custom Type Arguments and Return Values

A VDF can take and return custom type values using `.param(TYPE_NAME)` and
`.returns(TYPE_NAME)` in the builder. The implementation uses `CustomArg`
for input and `CustomResult` for output — the same argument and result types used for type
operations:

```cpp theme={null}
void complex_conjugate_impl(CustomArg in, CustomResult out) {
    if (in.is_null()) { out.set_null(); return; }
    auto src = in.value();    // Span<const unsigned char> — raw binary
    auto dst = out.buffer();  // Span<unsigned char>
    // ... read src, write result to dst ...
    out.set_length(src.size());
}
```

Register with `.param(COMPLEX)` and `.returns(COMPLEX)`:

```cpp theme={null}
.func(make_func<&complex_conjugate_impl>("complex_conjugate")
          .returns(COMPLEX)
          .param(COMPLEX)
          .build())
```

See the [development guide](/docs/mysql-9.7/dev/development#argument-and-result-types)
for the full `CustomArg`/`CustomResult` API, including `CustomArgWith<P>`
and `CustomResultWith<P>` for parameterized types.

### Deterministic Functions

By default, VDFs are registered as non-deterministic. A non-deterministic function is blocked from three SQL contexts: generated columns, CHECK constraints, and expression default values (`DEFAULT (expr)` on a column) — using any of these features with a non-deterministic VDF returns an error. If your function always produces the same output for the same inputs and has no side effects, you can declare it deterministic by adding `.deterministic()` to the builder chain.

The optimizer may use this information to evaluate the function once per statement and reuse the value across rows, rather than calling it per row. Incorrectly marking a non-deterministic function as deterministic can therefore cause the server to return the same result for inputs that should produce different outputs. Only add `.deterministic()` when your function truly has no dependency on external state, randomness, or time.

**Builder signature:** `.deterministic(bool d = true)` — the zero-argument form defaults to `true`.

**Example:**

```cpp theme={null}
.func(make_func<&complex_add_impl>("complex_add")
          .returns(COMPLEX)
          .param(COMPLEX)
          .param(COMPLEX)
          .deterministic()
          .build())
```

Because `complex_add` is declared deterministic, it can be used in a generated column definition:

```sql theme={null}
-- Deterministic VDFs can be used in generated columns
CREATE TABLE t (
    a COMPLEX,
    b COMPLEX,
    result COMPLEX GENERATED ALWAYS AS (complex_add(a, b)) STORED
);
-- STORED vs VIRTUAL follows standard MySQL generated column rules
```

### Custom Buffer Sizes

For functions returning variable-length data, request a specific buffer size:

```cpp theme={null}
make_func<&large_result_impl>("large_result")
  .returns(STRING)
  .param(INT)
  .buffer_size(65536)  // Request 64KB buffer
  .build()
```

When you build the value yourself with `buffer()` and `set_length()`, never write
past `buffer().size()` — that overflows memory. But do not fail on a value that is
too large either: report the size you would have written and let the server grow
the buffer and call you again.

```cpp theme={null}
void large_result_impl(StringArg input, StringResult out) {
    if (input.is_null()) { out.set_null(); return; }
    size_t needed = calculate_output_size(input.value());

    auto buf = out.buffer();
    if (needed > buf.size()) {
        // Too small this time. Report what is needed and return: the server
        // grows the buffer to `needed` and re-invokes this function, and the
        // second call takes the branch below.
        out.set_length(needed);
        return;
    }

    // Write output into buf.data()
    out.set_length(actual_output_length);
}
```

For STRING results, `out.set(sv)` follows an snprintf-style overflow contract: it
copies as many bytes as fit in `out.buffer()`, but reports the value's *full*
size. The server detects that the reported length exceeds the buffer, grows the
buffer, and calls the function a second time — so a value larger than
`.buffer_size()` is returned whole rather than truncated. One retry always
suffices, because the reported length says exactly how large the buffer must be.

Growth is bounded by `max_allowed_packet`. A value larger than that is returned
as `NULL`, not truncated.

Writing into `out.buffer()` and calling `out.set_length()` yourself participates
in the same contract: report the full size you would have written, not the number
of bytes that fit. Reporting the truncated length defeats the retry and silently
drops the remainder.

That contract applies at row time. A *materialized* STRING result — in a
GROUP BY/DISTINCT temp table, `CREATE TABLE ... SELECT`, or UNION — still
truncates at the argument width unless the function declares
`.max_result_length(n)`, which sizes the result column (in characters, capped at
`VEF_MAX_RESULT_LENGTH`, 16 MiB):

```cpp theme={null}
make_func<&large_result_impl>("large_result")
  .returns(STRING)
  .max_result_length(65536)
  .build()
```

<Note>
  Request sufficient buffer size via `.buffer_size()` based on your function's maximum output size. Sizing the buffer correctly avoids the cost of a grow-and-retry round trip.
</Note>

<Note>
  Before implementing your functions, review the [C++ API Reference](/docs/mysql-9.7/dev/extension-api-reference) for the complete VDF contracts: null checking, result types, buffer sizing, and error handling.
</Note>

## Step 4: Creating Custom Types

Custom types let you define new column types — like `COMPLEX`, `UUID`, or
`VECTOR` — that work with `ORDER BY`, indexes, and aggregate functions.
If your extension only registers functions, skip to Step 5.

See [Custom Types in C++](/docs/mysql-9.7/dev/custom-types) for the full
implementation walkthrough. If your type takes parameters (e.g., `VECTOR(1536)`),
see [Parameterized Types](/docs/mysql-9.7/dev/type-operations#parameterized-types).

## Step 5: Update Build Configuration

Edit `CMakeLists.txt` to build your extension as a VEB file:

```cmake theme={null}
cmake_minimum_required(VERSION 3.18)
project(my_extension)

# Find VillageSQL Extension Framework
find_package(VillageSQLExtensionFramework QUIET)

# The framework detects build flags via 4 methods (in order):
# 1. Explicit MYSQL_INCLUDE_FLAGS/MYSQL_CXXFLAGS
# 2. VillageSQL_BUILD_DIR (reads CMakeCache.txt from VillageSQL build)
# 3. VSQL_BASE_DIR (uses mysql_config)
# 4. Default - mysql_config from PATH

# Build shared library with your source files
add_library(extension SHARED
    src/extension.cc
    src/my_functions.cc
)

# Create VEB archive
VEF_CREATE_VEB(
    NAME my_extension
    LIBRARY_TARGET extension
    MANIFEST ${CMAKE_CURRENT_SOURCE_DIR}/manifest.json
)

# Install VEB to VillageSQL extensions directory
install(FILES ${VEB_OUTPUT} DESTINATION ${INSTALL_DIR})
```

**Configuration notes:**

* `VillageSQLExtensionFramework` provides CMake helpers for building extensions
* `VEF_CREATE_VEB()` packages your library, manifest, and metadata into a `.veb` archive
* The framework automatically detects MySQL/VillageSQL build flags
* Library target name is typically `extension` (can be anything)
* VEB name must match your manifest.json name
* By default, extensions build against the stable ABI headers. Set `-DVSQL_USE_DEV_ABI=ON` to build against the unstable dev headers instead

***

## Step 6: Create a Build Directory

Create a separate build directory:

```bash theme={null}
mkdir build
cd build
```

## Step 7: Build with CMake and Make

Configure and build your extension:

```bash theme={null}
# Configure the build
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo

# Or, if building against VillageSQL source:
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVillageSQL_BUILD_DIR=/path/to/villagesql/build

# Or, to build against the unstable dev ABI headers:
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVSQL_USE_DEV_ABI=ON

# Build the extension
make
```

This creates:

* Compiled shared library (`.so` file)
* VEB package (`.veb` file) - a tar archive containing manifest and library

### Verify the Build

Check the contents of your VEB file:

```bash theme={null}
make show_veb
```

You should see:

```
manifest.json
lib/myext.so
```

## Step 8: Install and Test

### Option A: Install to VillageSQL Extensions Directory

Use the install target to copy the VEB to your VillageSQL installation:

```bash theme={null}
make install
```

This copies the `.veb` file to the directory configured via `VillageSQL_VEB_INSTALL_DIR`.

### Option B: Manual Installation

Copy the VEB file manually:

```bash theme={null}
# Find the VEF directory
mysql -u root -p -e "SHOW VARIABLES LIKE 'veb_dir';"

# Copy the VEB file
sudo cp my-awesome-extension.veb /path/to/veb_dir/
```

### Test Your Extension

1. **Connect to VillageSQL**:
   ```bash theme={null}
   mysql -u root -p
   ```

2. **Install the extension**:
   ```sql theme={null}
   INSTALL EXTENSION my_awesome_extension;
   ```

3. **Verify installation**:
   ```sql theme={null}
   SELECT * FROM INFORMATION_SCHEMA.EXTENSIONS;
   ```

4. **Test your functions**:
   ```sql theme={null}
   SELECT my_reverse('Hello, World!');
   -- Output: !dlroW ,olleH

   SELECT count_vowels('VillageSQL');
   -- Output: 3
   ```

## Creating Tests

Add test files to validate your extension works correctly:

1. **Create a test file** in `mysql-test/t/`:
   ```sql theme={null}
   -- mysql-test/t/my_basic.test
   SELECT my_reverse('abc');
   SELECT my_reverse('');
   SELECT my_reverse(NULL);
   ```

2. **Generate expected results**:
   ```bash theme={null}
   cd /path/to/villagesql/build/mysql-test
   perl mysql-test-run.pl --suite=/path/to/your/extension/mysql-test --record
   ```

3. **Run tests**:
   ```bash theme={null}
   perl mysql-test-run.pl --suite=/path/to/your/extension/mysql-test
   ```

## Troubleshooting

### Extension Won't Load

Check the error log and verify the VEB contents:

```bash theme={null}
make show_veb
tail -f /var/log/mysql/error.log
```

### Function Not Found

Verify installation and registration:

```sql theme={null}
SELECT * FROM INFORMATION_SCHEMA.EXTENSIONS;
```

### Build Errors

```bash theme={null}
# Verify mysql_config is available
which mysql_config
mysql_config --version

# Check compiler version
gcc --version  # or clang --version

# Verify CMake version (3.18+ required)
cmake --version
```

## Example Extensions

Learn from existing VillageSQL extensions:

<CardGroup cols={2}>
  <Card title="vsql_complex" icon="wave-square" href="https://github.com/villagesql/villagesql-server/tree/main/villagesql/examples/vsql-complex">
    Complex number data type implementation
  </Card>

  <Card title="vsql_extension_template" icon="code" href="https://github.com/villagesql/vsql-extension-template">
    Minimal template for creating extensions
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Using Extensions" icon="puzzle-piece" href="/docs/mysql-9.7/dev/install">
    Learn how to install and manage extensions
  </Card>

  <Card title="Development Guide" icon="code" href="/docs/mysql-9.7/dev/development">
    Argument and result types, aggregates, system variables, and testing
  </Card>

  <Card title="Extension Architecture" icon="sitemap" href="/docs/mysql-9.7/dev/architecture">
    Lifecycle, caching, performance, and security model
  </Card>

  <Card title="Build from Source" icon="hammer" href="/docs/mysql-9.7/dev/source">
    Build VillageSQL from source code
  </Card>
</CardGroup>

## Resources

* [VillageSQL Extension Template](https://github.com/villagesql/vsql-extension-template)
* [MySQL UDF API Documentation](https://dev.mysql.com/doc/extending-mysql/9.7/en/adding-loadable-function.html)
* [CMake Documentation](https://cmake.org/documentation/)
* [VillageSQL Community Discord](https://discord.gg/KSr6whd3Fr)
