Skip to main content

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples in this guide work on VillageSQL. Install Now →
VillageSQL extensions are shared libraries that the server loads at runtime. The VEF SDK gives you typed C++ wrappers so you can write extension functions without touching raw FFI. This guide walks through building a minimal extension from scratch.

When to Choose C++

C++ is the right choice when:
  • Your team already has a C++ codebase and wants to keep the extension in the same language
  • You need custom types with precise binary layouts (the C++ type builder is the most complete API)
  • You’re working with existing C++ libraries that you want to expose as SQL functions
If you prefer Rust and don’t have C++ requirements, see Writing Rust Extensions.

Prerequisites

Before writing extension code, you need a VillageSQL build — extensions link against the server’s SDK headers and build tree. You also need:
  • C++17 compiler — GCC 8+, Clang 8+, or MSVC 2019+
  • CMake 3.18+ — the extension template uses CMake
Follow the Build from Source guide first if you haven’t built the server yet.

CMake Setup

The fastest way to start is with the vsql-extension-template. Fork it or clone it:
git clone https://github.com/villagesql/vsql-extension-template my-extension
cd my-extension
The template’s CMakeLists.txt handles the VEF build setup for you — copy the template and adapt it rather than writing CMake from scratch. Configure the build directory with -DVillageSQL_BUILD_DIR:
mkdir build && cd build
cmake .. -DVillageSQL_BUILD_DIR=/path/to/villagesql/build
make
-DVillageSQL_BUILD_DIR tells CMake where your VillageSQL build lives so it can find the SDK headers. This produces a .veb file in the build output. See Creating Extensions and the vsql-extension-template for the full CMake setup.

A Minimal VDF in C++

Include the VEF SDK header and use the builder API to register your function. Here’s a complete extension implementing a string reversal function:
#include <villagesql/vsql.h>

using namespace vsql;

void my_reverse_impl(StringArg input, StringResult out) {
    if (input.is_null()) {
        out.set_null();
        return;
    }

    auto sv = input.value();   // std::string_view
    auto buf = out.buffer();   // Span<char>

    if (sv.size() > buf.size()) {
        out.error("my_reverse: input exceeds buffer size");
        return;
    }

    for (size_t i = 0; i < sv.size(); i++) {
        buf.data()[i] = sv[sv.size() - 1 - i];
    }
    out.set_length(sv.size());
}

VEF_GENERATE_ENTRY_POINTS(
    make_extension()
        .func(make_func<&my_reverse_impl>("my_reverse")
            .returns(STRING)
            .param(STRING)
            .build())
)
A few things to notice:
  • Include <villagesql/vsql.h>, not <villagesql/extension.h> (that’s the old V1 header)
  • Typed argument wrappers (StringArg, IntArg, RealArg) check nulls and expose typed values — no raw pointer arithmetic
  • out.set_null(), out.warning(msg), and out.error(msg) are the three non-success returns
  • VEF_GENERATE_ENTRY_POINTS takes a make_extension() builder, not a raw struct
For functions returning integers or reals:
void count_vowels_impl(StringArg input, IntResult out) {
    if (input.is_null()) { out.set_null(); return; }

    long long count = 0;
    for (char c : input.value()) {
        char lower = std::tolower(c);
        if (lower == 'a' || lower == 'e' || lower == 'i' ||
            lower == 'o' || lower == 'u') {
            count++;
        }
    }
    out.set(count);
}
To declare a function deterministic — same inputs always produce the same output — add .deterministic() to the builder chain:
.func(make_func<&my_reverse_impl>("my_reverse")
    .returns(STRING)
    .param(STRING)
    .deterministic()
    .build())
Deterministic functions can be used in generated columns and functional indexes. Only add .deterministic() when it’s actually true.

Building

After the cmake configure step from the CMake setup section above, build and install:
make
make install
make install copies the .veb file to your VillageSQL extensions directory.

Installing and Testing

Connect to VillageSQL and install the extension:
INSTALL EXTENSION my_extension;
Verify it loaded:
SELECT * FROM INFORMATION_SCHEMA.EXTENSIONS WHERE EXTENSION_NAME = 'my_extension';
Call your function:
SELECT my_reverse('Hello, World!');
-- → !dlroW ,olleH
For automated testing, write a MySQL Test Framework suite in mysql-test/t/:
-- mysql-test/t/basic.test
INSTALL EXTENSION my_extension;
SELECT my_reverse('abc');
SELECT my_reverse('');
SELECT my_reverse(NULL);
UNINSTALL EXTENSION my_extension;
Generate expected results and run:
cd /path/to/villagesql/build/mysql-test
perl mysql-test-run.pl --suite=/path/to/my-extension/mysql-test --record
perl mysql-test-run.pl --suite=/path/to/my-extension/mysql-test

See also