Skip to main content
The Rust SDK is in alpha — expect breaking API changes between releases. Preview capabilities are additionally unstable on the server side: their ABIs may change between server releases. An extension built against a preview capability may fail to load after a server update.
Preview capabilities are server features exposed to extensions before their APIs are finalized. The Rust SDK wraps four of them: status variables, system variables, background thread workers, and keyring access. This page covers declaring and using each one from Rust. For the concept, the preview tier, and the capabilities that exist only in C++, see Preview Capabilities.

Prerequisites

An extension that uses any preview capability installs only when vsql_allow_preview_extensions is ON:
See Enabling the Preview Tier for the details, including why SET GLOBAL is rejected for this variable. You also need a working Rust extension setup — the Creating Extensions in Rust walkthrough covers the toolchain, cargo-vsql, and your first extension! block.

What the Rust SDK Wraps

The remaining preview capabilities — sql_query, statement_event, and the column storage ABI — are C++-only today. Use the C++ SDK if you need one of those.

Registration Pattern

Declare each capability as a static, then list it by reference in the requires: section of extension! — the Rust equivalent of the C++ SDK’s .with():
The server populates the capability object at load time; before that, its accessor methods report the capability as unavailable rather than crashing. The static is required — the server holds pointers into the capability for the extension’s whole lifetime.

Status Variables

The status_var capability exposes extension-owned counters through SHOW GLOBAL STATUS. Your extension owns the storage as 'static atomics and writes to them; the server reads through the pointers each time the status variable is queried. Declare each variable as a StatusVarSpecInt backed by an AtomicI64, or Double backed by the SDK’s AtomicF64 (Rust’s standard library has no atomic f64, so the SDK provides one with new, load, and store):
After INSTALL EXTENSION, the variables appear with the extension name as a prefix:

System Variables

The sys_var capability registers MySQL system variables owned by your extension. Three types are supported: Bool, Int (with min/max bounds), and Str. Every spec carries a name, a comment shown in SHOW VARIABLES metadata, a default, and an optional on_change callback. Names and string defaults are &'static CStr values — write them as C-string literals (c"enabled"):
The funcs: section must be present before requires:, even when empty. After install, the variables are addressable with the extension name as a prefix:

Reacting to Changes

on_change is a raw C callback, invoked by the server after a variable is set. It runs on the server’s thread: keep it quick, and don’t panic — a panic here crosses the FFI boundary. The callback receives a *const vef_sys_var_change_t from the raw ABI layer:
Wire it into a spec with on_change: Some(on_enabled_change).

Reading and Writing from Extension Code

SysVarCapability also exposes get() and set() for programmatic access through the server (so range validation and persistence are handled for you). set() takes a scope argument that selects persistence: null changes only the running value, so it reverts on restart; "PERSIST" changes the running value and writes it to the persisted config; "PERSIST_ONLY" writes to the persisted config without touching the running value, so it applies on the next restart. Both are unsafe FFI methods taking NUL-terminated C strings, and both use the inverted C convention: Some(false) means success, Some(true) means the server reported an error, and None means the capability is unavailable. On a successful get, the server writes a malloc’d string you must release with the C free(). The vsql_sys_var example shows the full pattern, including the free extern and the safety comments.

Thread Worker

The thread_worker capability runs a function you provide on a server-managed background thread. The server registers a control system variable at load time; while it is ON, your work function is called on a periodic timer, on file-descriptor readiness, or on enable/disable transitions. Your work function is plain safe Rust:
ThreadWorkerCapability::new takes the work function, a thread-name suffix, the initial sleep interval, and an optional control-variable name override. When the override is None, the control variable is named {suffix}_enabled and registered under the extension’s prefix:

Wakeups

WakeupReason tells you why the server called: Enable, Periodic, PollFd, or Disable. Your return value adjusts the next wakeup:
  • NextWakeup::unchanged() — keep the current sleep interval and poll fd.
  • NextWakeup::after(duration) — wake again after duration.
  • Set the poll_fd field to a file descriptor greater than zero to also wake when it becomes readable, or to -1 to clear a previously set one.
A zero-length Duration collapses to “no change” — the underlying C ABI reserves 0 for that, so an instant wakeup can’t be expressed. If the work function panics, the SDK catches the panic at the FFI boundary and treats the call as returning NextWakeup::unchanged() — the worker keeps running. The ThreadHandle parameter is reserved for opening SQL sessions from the worker once the sql_query capability is ported to Rust; it has no methods yet.

Keyring Access

The keyring capability reads and writes secrets stored in the MySQL keyring component — API keys, encryption keys, anything that shouldn’t live in a table. A keyring component (for example component_keyring_file) must be installed on the server; without one, every read and write fails with KeyringError::NoComponent.
read(data_id, auth_id, buf) fills the buffer you pass and returns Ok(Some(n)) with the secret’s length, or Ok(None) when no secret exists under data_id — a normal outcome, not an error. write(data_id, auth_id, data) returns Ok(()) on success. auth_id is the owning user; pass None for internal keys not associated with a specific user. Both return Err(KeyringError) on failure: CapabilityUnavailable (the capability was never wired up), NoComponent (no keyring component on the server), or Other. The keyring has no size probe: a secret larger than the buffer you pass to read comes back as Ok(None), indistinguishable from a missing key. Size your buffer for the largest secret you expect to store.

Next Steps

Preview Capabilities (C++)

The full capability index, the preview tier, and the C++-only capabilities: sql_query, statement_event, and column storage.

Rust API Reference

InValue, VdfReturn, extension!, func!, and custom_type! — all fields.