This page is a reference for extension authors. For the step-by-step tutorial, see Creating Extensions. For custom column types, see Creating Custom Types.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.
VDF Function Contracts
These contracts govern how VDF implementation functions interact with the VEF runtime. Every function registered viamake_func<> must follow them.
The types referenced below are defined in #include <villagesql/vsql.h>.
Part A: VDF Function Contracts
1. VDF implementation functions arevoid — they never return a value.
The server calls your function through this typedef:
result->type.
2. Set result->type to exactly one of the four result constants.
Four constants exist in vef_return_value_type_t:
| Constant | Value | Meaning |
|---|---|---|
VEF_RESULT_VALUE | 0 | Success — the output is in the appropriate union field |
VEF_RESULT_NULL | 1 | The result is SQL NULL |
VEF_RESULT_WARNING | 2 | Row-level warning — execution continues, NULL is returned for this row, and a SQL warning is added. In strict mode, MySQL promotes this to an error on INSERT/UPDATE. |
VEF_RESULT_ERROR | 3 | Fatal error — statement execution is aborted; message is in result->error_msg |
VEF_RESULT_VALUE is the single success
constant for strings, integers, reals, and custom types alike. The type of
output is determined by which union field you write to (str_buf,
int_value, real_value, bin_buf).
3. Check input->is_null before reading any other field on an input argument.
If is_null is true, every other field on that vef_invalue_t is undefined.
Accessing str_value, int_value, or any other field without checking
is_null first is undefined behavior.
result->str_buf and set result->actual_len. Check result->max_str_len before writing.
result->str_bufis a server-managed buffer. Write your output here.result->actual_lenmust be set to the number of bytes written. There is no field namedstr_lenon the result struct.result->max_str_lenis the size ofstr_bufin bytes. Always check it before writing. Do not usesizeof(result->str_buf).
result->error_msg, not to result->str_buf. Use VEF_MAX_ERROR_LEN (512 bytes) as the snprintf size limit.
error_msg is a separate, caller-provided buffer dedicated to error text.
It is independent of str_buf. The maximum size including the null
terminator is VEF_MAX_ERROR_LEN (512).
Part B: Encode/Decode Return Convention
The bool return convention applies only to encode and decode function pointers used by custom types. It does not apply to VDF-name type operations (which use the standard VDFvoid convention) or to VDF implementation functions.
bool where:
false= success (the operation completed without error)true= error (the conversion failed)
Implement Wrapper Functions
Implementation functions use the VEF API signature:Handling NULL Values
Check for NULL via theis_null flag and return NULL by setting the result type:
- Input NULL check:
if (input->is_null) - Return NULL:
result->type = VEF_RESULT_NULL - Return value:
result->type = VEF_RESULT_VALUE+ write to type-specific buffer - Return warning:
result->type = VEF_RESULT_WARNING+ warning message inresult->error_msg(returns NULL for this row, adds SQL warning, continues execution; in strict mode, MySQL promotes this to an error on INSERT/UPDATE) - Return error:
result->type = VEF_RESULT_ERROR+ error message inresult->error_msg
Error Handling
Return errors with custom messages for validation failures or invalid input:VEF_RESULT_VALUE- SuccessVEF_RESULT_NULL- NULL valueVEF_RESULT_WARNING- Row-level warning (returns NULL, adds SQL warning, continues execution; strict mode promotes to error on INSERT/UPDATE)VEF_RESULT_ERROR- Fatal error (aborts statement execution)
Per-Statement State with Prerun/Postrun
For VEF SDK functions that need setup/cleanup per SQL statement (not per row):Most extensions don’t need prerun/postrun hooks. The VEF SDK automatically handles common cases like type checking and buffer allocation. Use prerun/postrun only when you need expensive per-statement setup (like opening connections) that shouldn’t happen per-row.If you find you need prerun/postrun for your use case, please share your scenario on the VillageSQL Discord - the team may be able to add SDK support to handle it automatically.
Aggregate Functions
Built-in aggregates COUNT(DISTINCT), MIN, MAX, and GROUP_CONCAT work with custom types out of the box. MIN and MAX require a compare function registered on the type. Custom aggregate VDFs are also supported. Register one withmake_aggregate_func<State, &result_fn>("name"), then chain .returns(),
.param(), .clear<>(), and .accumulate<>() before calling .build().
Both .clear<>() and .accumulate<>() are required. See
Aggregate VDFs for the
builder API and callback signatures.
Built-in aggregate operations with custom types:
- Each function call processes one row with its own result buffer (thread-safe)
prerun/postrunprovide per-statement setup/teardown- Avoid global state - use function parameters and return values instead
- If you must use global state, protect it with mutexes/locks
Window Functions
The following window functions work with custom types:Temporary Tables
Custom types work in temporary tables.CREATE TEMPORARY TABLE, INSERT,
and ALTER TABLE behave the same as with permanent tables.
Preview APIs
Some VEF capabilities are available as opt-in headers undervillagesql/preview/ in the SDK include tree. The ABI and API are still
under active development and may change without notice.
To opt in, add the include to your extension source. For example:
<villagesql/vsql.h>; you must include it
directly when you opt in.
Namespace layout under vsql::preview is per-capability — there is no single
universal pattern. The keyring API uses vsql::preview_keyring::KeyringCapability;
the thread worker API uses vsql::preview_thread_worker::ThreadWorkerCapability;
the SQL query API uses vsql::preview_sql_query::SqlQueryCapability and must be
opened from a background worker thread handle (vef_thread_handle_t *).
Check each header for the exact namespace and class name it defines.
For the full preview API documentation, see Preview Capabilities.
Triggers
Triggers fire on tables with custom type columns. The trigger body can reference non-custom-type columns fromNEW and OLD. Accessing custom
type column values inside a trigger body is not yet supported.

