> ## 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.

# Rust API 参考

> VillageSQL Rust SDK 的完整参考 — InValue、VdfReturn、extension!、func!、agg_func!、varargs_func!、custom_type!、custom! 以及 manifest.json 字段。

<Warning>
  Rust SDK 处于 alpha 阶段——各版本之间可能出现破坏性的 API 变更。支持纯函数扩展、聚合函数、变长参数函数以及自定义类型（encode、decode、compare、hash），同时也支持 `sys_var`、`status_var`、`thread_worker` 和 `keyring` 预览功能。列存储 ABI 目前仅在 C++ SDK 中提供——如果您需要它，请使用 [C++ SDK](/docs/zh/mysql-9.7/stable/create)。
</Warning>

本页是 `villagesql` crate API 的参考。有关入门教程，请参阅 [使用 Rust 构建扩展](/docs/zh/mysql-9.7/stable/rust-sdk)。有关自定义类型，请参阅 [Rust 中的自定义类型](/docs/zh/mysql-9.7/stable/rust-custom-types)。

## InValue

`InValue` 是服务器为每个函数参数传递的枚举。您的函数接收 `args: &[InValue]`，并且必须在使用其值之前检查每个参数。

```rust theme={null}
pub enum InValue<'a> {
    String(&'a str),
    Real(f64),
    Int(i64),
    Null,
    Custom(&'a [u8]),
    CustomWithParams { bytes: &'a [u8], params: TypeParams<'a> },
}
```

| 变体                                   | Rust 类型             | 对应的 SQL 类型                                                                                         |
| ------------------------------------ | ------------------- | -------------------------------------------------------------------------------------------------- |
| `String(&str)`                       | UTF-8 字符串切片         | `STRING` / `VARCHAR` / `TEXT`                                                                      |
| `Real(f64)`                          | 64 位浮点数             | `REAL` / `DOUBLE` / `FLOAT`                                                                        |
| `Int(i64)`                           | 64 位有符号整数           | `INT` / `BIGINT` / `TINYINT`                                                                       |
| `Null`                               | —                   | SQL 中任何类型的 `NULL`                                                                                  |
| `Custom(&[u8])`                      | 原始二进制字节             | 通过 `custom_type!` 注册的任何自定义类型                                                                       |
| `CustomWithParams { bytes, params }` | 原始字节 + `TypeParams` | 通过 [`parameterized_type!`](/docs/zh/mysql-9.7/stable/rust-custom-types#parameterized-types) 注册的参数化自定义类型 |

始终显式匹配 `Null`。调用 `.unwrap()` 或仅模式匹配值变体是一种错误——SQL NULL 是一种正常的输入，而不是错误。

## VdfReturn

`VdfReturn` 是您的函数返回给服务器的内容。使用以下关联函数之一构造它：

| 构造函数                       | SQL 效果                                    |
| -------------------------- | ----------------------------------------- |
| `VdfReturn::null()`        | 为此行返回 SQL NULL                            |
| `VdfReturn::string(s)`     | 返回一个 `String` 值；`s` 是 `impl Into<String>` |
| `VdfReturn::real(v)`       | 返回一个 `f64` 值                              |
| `VdfReturn::int(v)`        | 返回一个 `i64` 值                              |
| `VdfReturn::binary(bytes)` | 返回自定义类型列的二进制字节；`bytes` 是 `Vec<u8>`        |
| `VdfReturn::warning(msg)`  | 为此行返回 NULL，添加 SQL 警告，执行继续                 |
| `VdfReturn::error(msg)`    | 因致命错误中止语句                                 |

**警告与错误：**

对于用户输入验证失败的情况，如果继续处理其余结果集是有意义的，请使用 `warning`。在严格模式下，MySQL 会在 `INSERT` 和 `UPDATE` 上将警告升级为错误。对于继续操作不安全的情况（损坏存储的数据、违反内部不变量），请使用 `error`。致命错误会中止整个语句。

```rust theme={null}
fn validate_impl(args: &[InValue]) -> VdfReturn {
    match args.first() {
        Some(InValue::Int(n)) if *n >= 0 => VdfReturn::int(*n),
        Some(InValue::Int(_)) => VdfReturn::warning("value must be non-negative"),
        Some(InValue::Null) | None => VdfReturn::null(),
        _ => VdfReturn::error("validate: expected an INT argument"),
    }
}
```

## extension! 宏

`extension!` 生成服务器在加载 VEB 文件时调用的 VEF 入口点。它必须在 crate 中恰好出现一次。

```rust theme={null}
villagesql::extension! {
    funcs: [
        // One or more villagesql::func!(...) declarations
    ],
    types: [
        // One or more villagesql::custom_type!(...) declarations
    ],
    requires: [
        // Zero or more &'static capability references, e.g. &KEYRING
    ]
}
```

`types:` 和 `requires:` 各自都是可选的，但 `funcs:` 必须始终存在——对于仅类型扩展，请写 `funcs: []`。纯函数扩展省略 `types:`。一个带有 `funcs: []` 且没有类型的 `extension!` 块是有效的，但会生成一个不执行任何操作的扩展。

`requires:` 声明扩展所使用的 [Rust 中的预览功能](/docs/zh/mysql-9.7/stable/rust-preview-capabilities)，以对 `static` 功能对象的引用的形式给出。它必须放在最后，位于 `funcs:` 部分之后——如果扩展不注册任何函数，请包含 `funcs: []`。

## func! 宏

`func!` 声明一个可从 SQL 调用的函数。有六种形式——四种不带每个语句状态的形式（不带可选参数、仅 `buffer_size`、仅 `deterministic`、两者都有），以及两种通过 `prerun` 函数附加每个语句状态的形式：

```rust theme={null}
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, buffer_size: N)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, deterministic: true)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, buffer_size: N, deterministic: true)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, state: StateType, prerun: prerun_fn)
villagesql::func!(rust_fn, "sql_name", [param_types] -> return_type, state: StateType, prerun: prerun_fn, buffer_size: N, deterministic: true)
```

<Note>
  `buffer_size` 参数需要 `villagesql` crate **0.0.2 或更高版本**。当前的 [crates.io](https://crates.io/crates/villagesql) 发布版本（`0.0.1`）尚未公开该参数——在 `0.0.2` 发布之前，请使用不带 `buffer_size` 的形式。
</Note>

| 参数                    | 描述                                                                                                                                 |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `rust_fn`             | 实现 VDF 的 Rust 函数。签名：`fn(&[InValue]) -> VdfReturn`，对于 `state:` / `prerun:` 形式则为 `fn(&mut StateType, &[InValue]) -> VdfReturn`。      |
| `"sql_name"`          | SQL 函数名，作为一个字符串字面量。这是用户从 SQL 中调用的名称。                                                                                               |
| `[param_types]`       | 以逗号分隔的 `villagesql::Type::*` 或 `villagesql::custom!("name")` 值列表。对于零参数函数，使用 `[]`。                                                  |
| `return_type`         | `villagesql::Type::*` 或 `villagesql::custom!("name")`。                                                                             |
| `buffer_size: N`      | 可选。字符串/二进制返回值的结果缓冲区大小（以字节为单位）。使用 `0` 表示服务器默认值（256 字节）。当函数返回的字符串或二进制值大于 `buffer_size` 时，函数会报错而不是截断——声明一个更大的 `buffer_size` 以处理更大的结果。 |
| `deterministic: true` | 可选。声明该函数是确定性的——相同的输入始终产生相同的输出，没有副作用。优化器可以缓存相同输入的结果。仅当为真时才设置此项。                                                                     |

在 `func!` 中使用的 **类型常量**：

| `villagesql::Type::*`      | SQL 类型   |
| -------------------------- | -------- |
| `villagesql::Type::String` | `STRING` |
| `villagesql::Type::Real`   | `REAL`   |
| `villagesql::Type::Int`    | `INT`    |

<h3 id="per-statement-state">
  每个语句的状态
</h3>

有些函数需要跨单个语句的每一行都存在的状态——调用计数器、累加器。使用 `state:` 声明状态类型，使用 `prerun:` 声明一个设置函数。prerun 函数在第一行之前运行一次；随后行函数对每一行运行一次，并对该状态拥有 `&mut` 访问权限。

prerun 函数的签名是 `fn(PrerunArgs, PrerunResult<T>)`，它所供给状态的行函数将状态作为第一个参数：`fn(state: &mut T, args: &[InValue]) -> VdfReturn`。`T` 是由 `state:` 指定的类型，编译器会检查 prerun 与行函数在该类型上是否一致。

| `PrerunResult<T>` 方法                       | 效果                                                 |
| ------------------------------------------ | -------------------------------------------------- |
| `set_state(self, state: T)`                | 分配每个语句的状态并将其交给服务器。它消耗 `self` 而不是借用它，因此编译器会拒绝第二次调用。 |
| `request_buffer_size(&mut self, n: usize)` | 向服务器请求特定的结果缓冲区大小。                                  |
| `error(self, msg: &str)`                   | 以一条消息使整个语句失败。                                      |

`PrerunArgs::len()` 是每一行将接收的参数数量，当函数调用时不带任何参数时，`PrerunArgs::is_empty()` 为真。

您不能自行释放该状态：`func!` 会生成在语句结束时丢弃它的 postrun。这与 C++ SDK 相反，在 C++ SDK 中，您的 postrun 必须调用 `delete_state<T>()`——请参阅[每个语句的状态](/docs/zh/mysql-9.7/stable/development#per-statement-state-prerun-and-postrun)。

<Note>
  `state` 和 `prerun` 参数尚未包含在已发布的版本中。当前的
  [crates.io](https://crates.io/crates/villagesql) 发布版本（`0.0.1`）
  尚未公开它们。
</Note>

一个完整的扩展示例，其函数返回它在语句内自身的调用序号：

```rust theme={null}
use villagesql::{InValue, PrerunArgs, PrerunResult, VdfReturn};

/// Per-statement state: how many times the row function has been called.
struct CallCounter {
    n: i64,
}

/// Runs once, before the first row: allocate the counter at zero.
fn call_index_prerun(_args: PrerunArgs, out: PrerunResult<CallCounter>) {
    out.set_state(CallCounter { n: 0 });
}

/// Runs once per row: bump the counter and return its new value.
fn call_index(state: &mut CallCounter, _args: &[InValue]) -> VdfReturn {
    state.n += 1;
    VdfReturn::int(state.n)
}

villagesql::extension! {
    funcs: [
        villagesql::func!(call_index, "call_index", [] -> villagesql::Type::Int,
            state: CallCounter, prerun: call_index_prerun),
    ]
}
```

按照[使用 Rust 构建扩展](/docs/zh/mysql-9.7/stable/rust-sdk)中的说明构建并安装它，然后：

```sql theme={null}
INSTALL EXTENSION vsql_call_index;
CREATE TABLE t (id INT);
INSERT INTO t VALUES (10), (20), (30);
SELECT SUM(vsql_call_index.call_index()) AS total FROM t;
-- → 6
SELECT SUM(vsql_call_index.call_index()) AS total FROM t;
-- → 6
```

该表有三行，因此 `call_index()` 运行三次，依次返回 `1`、`2`、`3`——每行一个值。`SUM` 将这三个值相加，得到 `6`。

第二个 `SELECT` 返回与第一个相同的总和，而不是更大的值：该计数器为一个语句分配，并在该语句结束时被丢弃。

## agg\_func! 宏

`agg_func!` 声明一个聚合 SQL 函数——类似 SUM/COUNT 的风格，对每个分组的各行调用，而不是每行调用一次。有两种形式：

```rust theme={null}
villagesql::agg_func!(result_fn, "sql_name", [param_types] -> return_type,
    state: StateType, clear: clear_fn, accumulate: accumulate_fn)
villagesql::agg_func!(result_fn, "sql_name", [param_types] -> return_type,
    state: StateType, clear: clear_fn, accumulate: accumulate_fn,
    buffer_size: N, deterministic: true)
```

| 参数                                       | 描述                                                                                                                 |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `result_fn`                              | `fn(&State) -> VdfReturn`。从完成累加的累加器生成一个分组的输出。在该分组的最后一行被折叠进累加器之后，每个分组运行一次。接收 `&State` 而不是 `&mut`——结果函数只读取累加器，不会重置它。 |
| `"sql_name"`                             | SQL 函数名，作为一个字符串字面量。                                                                                                |
| `[param_types]`                          | 参数列表，与 `func!` 相同——`villagesql::Type::*` 或 `villagesql::custom!("name")` 值。                                        |
| `return_type`                            | `villagesql::Type::*` 或 `villagesql::custom!("name")`。                                                             |
| `state: StateType`                       | 累加器类型。它必须实现 `Default`：`agg_func!` 会为您生成 prerun，该 prerun 使用 `StateType::default()` 对累加器进行值初始化。请派生 `Default` 或手动实现它。 |
| `clear: clear_fn`                        | `fn(&mut State)`。在每个分组开始时重置累加器。                                                                                    |
| `accumulate: accumulate_fn`              | `fn(&mut State, &[InValue])`。将一行折叠进累加器。每行运行一次。不返回任何内容——值只能通过 `result_fn` 输出。                                       |
| `buffer_size: N` / `deterministic: true` | 可选，但必须一起提供或都不提供——`agg_func!` 没有像 `func!` 那样的单选项形式。给出时，含义与 `func!` 中相同。                                             |

<Note>
  `agg_func!` 尚未包含在已发布的版本中。当前的
  [crates.io](https://crates.io/crates/villagesql) 发布版本（`0.0.1`）
  尚未公开它。
</Note>

累加器在每个语句中分配一次，并在语句结束时被丢弃——`agg_func!` 会同时生成创建它的 prerun 和丢弃它的 postrun，因此这两者您都无需编写。`clear_fn` 是实现按分组行为的机制：使用 `GROUP BY` 时，同一个累加器会在各分组之间复用，因此任何不得在分组之间泄漏的字段都必须在那里重置。

一个完整的、等价于 SUM 的聚合——SDK 仓库中的 `vsql_agg_sum` 示例：

```rust theme={null}
use villagesql::{InValue, VdfReturn};

/// Accumulator for `agg_sum`: the running total for the current group.
#[derive(Default)]
struct SumState {
    total: i64,
    seen: bool,
}

/// clear: reset the total at the start of each group.
fn agg_sum_clear(state: &mut SumState) {
    state.total = 0;
    state.seen = false;
}

/// accumulate: fold one row's int into the running total.
fn agg_sum_acc(state: &mut SumState, args: &[InValue]) {
    if let Some(InValue::Int(n)) = args.first() {
        state.total += *n;
        state.seen = true;
    }
}

/// result: emit the group's total once every row has been folded in.
fn agg_sum_result(state: &SumState) -> VdfReturn {
    if state.seen {
        VdfReturn::int(state.total)
    } else {
        VdfReturn::Null
    }
}

villagesql::extension! {
    funcs: [
        villagesql::agg_func!(agg_sum_result, "agg_sum",
            [villagesql::Type::Int] -> villagesql::Type::Int,
            state: SumState, clear: agg_sum_clear, accumulate: agg_sum_acc),
    ]
}
```

`accumulate` 只匹配 `InValue::Int`，这正是跳过 NULL 的方式，与内置 `SUM` 一致。`seen` 标志使得全为 NULL 的分组和空分组返回 NULL 而不是 `0`：

```sql theme={null}
INSTALL EXTENSION vsql_agg_sum;
CREATE TABLE t (grp INT, val INT);
INSERT INTO t VALUES (1, 10), (1, 20), (2, 100), (2, 200), (2, 300);
SELECT grp, vsql_agg_sum.agg_sum(val) AS mine, SUM(val) AS builtin
  FROM t GROUP BY grp ORDER BY grp;
-- grp  mine  builtin
--   1    30       30
--   2   600      600
```

## varargs\_func! 宏

`varargs_func!` 声明一个接受任意数量、任意类型参数的 VDF。参数列表写作 `[..]`——这是一个必需的字面量，而不是零参数 `func!` 所使用的 `[]`。

<Warning>
  对于变长参数 VDF，服务器不执行任何参数数量和参数类型的验证。没有已声明的参数列表可用来检查调用，因此每次调用都会把 SQL 文本传入的任何内容原样送达您的函数，包括零个参数和您从未预期的类型。验证完全是 `prerun` 钩子的职责。变长参数注册还需要 VEF Protocol 3——较旧的服务器会在安装时拒绝该扩展。这与 C++ SDK 一致，在 C++ SDK 中，[框架同样无法为变长参数 VDF 验证参数数量或类型](/docs/zh/mysql-9.7/stable/development#varargs-vdfs)。
</Warning>

共有六种形式——三种形态，每种形态都有一个简写形式和一个完整形式，完整形式同时添加 `buffer_size` 和 `deterministic`（决不单独添加）：

```rust theme={null}
// Per-statement state plus a validating prerun.
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    state: StateType, prerun: prerun_fn)
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    state: StateType, prerun: prerun_fn, buffer_size: N, deterministic: true)

// Validating prerun, no state.
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    prerun: prerun_fn)
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    prerun: prerun_fn, buffer_size: N, deterministic: true)

// Bare: no prerun, no state, no validation.
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type)
villagesql::varargs_func!(impl_fn, "sql_name", [..] -> return_type,
    buffer_size: N, deterministic: true)
```

| 参数                                       | 描述                                                                                                                                               |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `impl_fn`                                | 行函数。对于仅 prerun 形式和裸形式，签名为 `fn(&[InValue]) -> VdfReturn`；对于 `state:` 形式，签名为 `fn(&mut StateType, &[InValue]) -> VdfReturn`。`args` 切片的长度在每次调用之间会变化。 |
| `"sql_name"`                             | SQL 函数名，作为一个字符串字面量。                                                                                                                              |
| `[..]`                                   | 将该函数标记为变长参数；没有类型列表。                                                                                                                              |
| `return_type`                            | `villagesql::Type::*` 或 `villagesql::custom!("name")`。即使参数不固定，返回类型也是固定的。                                                                         |
| `state: StateType`                       | 可选。每个语句的状态，由 `prerun` 分配，并由每次行调用以 `&mut` 借用。需要 `prerun:`。与 `agg_func!` 不同，这里没有 `Default` 约束——由您的 prerun 构建该值。                                    |
| `prerun: prerun_fn`                      | 可选，并且是唯一能进行参数验证的地方。签名为 `fn(PrerunArgs, PrerunResult<T>)`，其中 `T` 是 `state:` 命名的类型，在仅 prerun 形式中为 `()`。                                            |
| `buffer_size: N` / `deterministic: true` | 可选，必须一起提供或都不提供。给出时，含义与 `func!` 中相同——对于变长参数，固定值往往是错误的选择，因为结果通常随参数数量增长；请改为在 prerun 中使用 `request_buffer_size` 确定其大小。                                |

裸形式没有任何验证，并且接受零参数调用——对于一个对每种输入都有定义的函数来说，这是一个合理的选择，但这意味着行函数必须独自处理它可能收到的每一种输入。只有 `state:` 形式会分配和丢弃每个语句的状态；仅 prerun 形式使用 `PrerunResult<()>` 且不存储任何内容，因此它没有 postrun——这样的 prerun 只将 `PrerunResult` 用于 `error` 和 `request_buffer_size`，决不调用 `set_state`。

### 在 prerun 中检查参数类型

由于服务器不做任何验证，变长参数的 prerun 需要在第一行运行之前查看参数类型。`PrerunArgs::type_at` 提供了这一视图，此外还有 `len()`/`is_empty()`，以及[每个语句的状态](#per-statement-state)中描述的 `PrerunResult` 方法。

| `PrerunArgs` 方法     | 返回值                                                   |
| ------------------- | ----------------------------------------------------- |
| `type_at(i: usize)` | `Option<ArgType>`——参数 `i` 的已声明类型；当 `i` 超出范围时为 `None`。 |

| `ArgType` 方法    | 返回值                                                                  |
| --------------- | -------------------------------------------------------------------- |
| `is_str()`      | 对 `STRING` 参数返回 `true`。                                              |
| `is_real()`     | 对 `REAL` 参数返回 `true`。                                                |
| `is_int()`      | 对 `INT` 参数返回 `true`。                                                 |
| `is_custom()`   | 对任何已安装扩展所注册的自定义类型的参数返回 `true`。                                       |
| `custom_name()` | `Option<&str>`——自定义类型的名称。仅当 `is_custom()` 为真时为 `Some`；对标量类型为 `None`。 |

将 `is_custom()` 与 `custom_name()` 搭配使用，可以只接受某一个自定义类型：单独使用 `is_custom()` 会接受服务器中的每一个自定义类型。

<Note>
  `varargs_func!` 和 `PrerunArgs::type_at` 尚未包含在已发布的版本中。当前的
  [crates.io](https://crates.io/crates/villagesql) 发布版本（`0.0.1`）
  尚未公开它们。
</Note>

SDK 仓库中的 `vsql_varargs` 示例为每种形式各声明了一个函数。下面是一个有状态的变长参数函数，它在 prerun 中进行验证，并携带一个每个语句的调用计数器：

```rust theme={null}
use villagesql::{InValue, PrerunArgs, PrerunResult, VdfReturn};

/// Per-statement state: how many times the row handler has run this statement.
#[derive(Default)]
struct JoinState {
    calls: i64,
}

/// Validate the call and set up the statement. The server does no validation for
/// varargs, so this is the only gate.
fn str_join_prerun(args: PrerunArgs, mut out: PrerunResult<JoinState>) {
    // Reject a zero-argument call.
    if args.is_empty() {
        out.error("str_join requires at least one argument");
        return;
    }

    // Every argument must be a string.
    for i in 0..args.len() {
        if !args.type_at(i).is_some_and(|t| t.is_str()) {
            out.error("str_join: every argument must be a string");
            return;
        }
    }

    // Size the result buffer from the arg count.
    out.request_buffer_size(32 + args.len() * 64);

    // Hand the fresh counter to the server.
    out.set_state(JoinState::default());
}

/// Join a variable number of string arguments, prefixed with the per-statement
/// call count.
fn str_join(state: &mut JoinState, args: &[InValue]) -> VdfReturn {
    state.calls += 1;

    let mut joined = String::new();
    for (i, arg) in args.iter().enumerate() {
        match arg {
            InValue::String(s) => {
                if i > 0 {
                    joined.push_str(", ");
                }
                joined.push_str(s);
            }
            // A string column can carry NULL. SQL-style: NULL in -> NULL out.
            InValue::Null => return VdfReturn::Null,
            _ => return VdfReturn::error("str_join: non-string argument at runtime"),
        }
    }
    VdfReturn::string(format!("#{}: {joined}", state.calls))
}

/// Bare varargs: no prerun, no state, no validation. Returns how many arguments
/// it was called with, including zero.
fn arg_count(args: &[InValue]) -> VdfReturn {
    VdfReturn::int(i64::try_from(args.len()).unwrap_or(i64::MAX))
}

villagesql::extension! {
    funcs: [
        villagesql::varargs_func!(str_join, "str_join", [..] -> villagesql::Type::String,
            state: JoinState, prerun: str_join_prerun),
        villagesql::varargs_func!(arg_count, "arg_count", [..] -> villagesql::Type::Int),
    ]
}
```

即使 prerun 已证明每个参数都是字符串，`str_join` 仍在行函数中对 `InValue` 进行匹配：prerun 看到的是已声明的类型，而不是值，并且 `STRING` 列在任何一行上都可能携带 NULL。

```sql theme={null}
INSTALL EXTENSION vsql_varargs;
SELECT vsql_varargs.str_join('alpha', 'beta');
-- #1: alpha, beta
SELECT vsql_varargs.str_join('a', 'b', 'c', 'd');
-- #1: a, b, c, d
SELECT vsql_varargs.str_join('ok', 123);
-- ERROR 1123 (HY000): Can't initialize function 'str_join'; str_join: every argument must be a string
SELECT vsql_varargs.arg_count();
-- 0
SELECT vsql_varargs.arg_count(1, 2.5, 'mix');
-- 3
```

该示例还声明了 `describe`（一个仅 prerun 的函数，它拒绝零参数和非标量参数，然后格式化一个混合类型的参数列表）和 `point_path`（它使用 `is_custom()` 和 `custom_name()` 进行验证）。请参阅 [Rust SDK 仓库](https://github.com/villagesql/vsql-rust-sdk)中的 `examples/vsql_varargs/src/lib.rs`。

## custom\_type! 宏

`custom_type!` 注册一个新的列类型。`type_name`、`persisted_length`、`max_decode_buffer_length`、`encode`、`decode` 和 `compare` 是必需的。`hash` 和 `default` 是可选的，但建议使用。

```rust theme={null}
villagesql::custom_type!(
    type_name: "sql_type_name",
    persisted_length: N,
    max_decode_buffer_length: M,
    encode: encode_fn,
    decode: decode_fn,
    compare: compare_fn,
    hash: hash_fn,
    default: "default_string",
)
```

| 字段                         | 类型                                       | 描述                                                                       |
| -------------------------- | ---------------------------------------- | ------------------------------------------------------------------------ |
| `type_name`                | `&str` 字面量                               | SQL 中该类型的名称。在 SQL 中不区分大小写。必须是所有已安装扩展中唯一的。                                |
| `persisted_length`         | `usize`                                  | 磁盘上存储的固定字节长度。所有编码的值都必须产生恰好该长度的字节。                                        |
| `max_decode_buffer_length` | `usize`                                  | 解码字符串的最大字节长度。用于在调用 `decode` 之前确定输出缓冲区的大小。                                |
| `encode`                   | `fn(&str) -> Result<Vec<u8>, String>`    | 在 `INSERT` 时调用。将 SQL 字符串字面量转换为二进制。返回 `Err(msg)` 以拒绝输入。                   |
| `decode`                   | `fn(&[u8]) -> Result<String, String>`    | 在显示值时调用。将二进制转换为字符串。                                                      |
| `compare`                  | `fn(&[u8], &[u8]) -> std::cmp::Ordering` | 在 `ORDER BY`、`MIN`、`MAX` 中调用。返回 `Less`、`Equal` 或 `Greater`。              |
| `hash`                     | `fn(&[u8]) -> usize`                     | 可选。在 `COUNT(DISTINCT)` 和集合操作中调用。比较结果为 `Equal` 的值必须哈希到相同的值。建议用于索引列。       |
| `default`                  | `&str` 字面量                               | 可选。一个有效的字符串，服务器在类型初始化时对其进行编码，以验证回调是否正常工作。必须编码为恰好 `persisted_length` 个字节。 |

`default` 字段不是列的默认值——它是一个启动探测。服务器在加载扩展时调用 `encode(default)` 以验证回调是否正常工作。如果 `encode` 对该默认值返回 `Err`，则扩展将无法加载。

## custom! 宏

`villagesql::custom!("type_name")` 在 `func!` 声明中按名称引用一个自定义类型：

```rust theme={null}
villagesql::func!(
    my_fn,
    "my_sql_func",
    [villagesql::custom!("mytype")] -> villagesql::custom!("mytype"),
    deterministic: true
)
```

在任何可以使用 `villagesql::Type::*` 的参数列表或返回类型位置中使用它。该字符串必须与相应的 `custom_type!` 中声明的 `type_name` 匹配。

## manifest.json 字段

每个扩展都需要一个与 `Cargo.toml` 位于同一位置的 `manifest.json` 文件：

```json theme={null}
{
  "name": "vsql_my_extension",
  "version": "0.1.0",
  "description": "Brief description of what the extension does",
  "author": "Your Name",
  "license": "GPL-2.0"
}
```

| 字段            | 必需 | 格式                | 描述                                                             |
| ------------- | -- | ----------------- | -------------------------------------------------------------- |
| `name`        | 是  | 小写字母、数字、`_`、`-`   | 扩展标识符。必须与 `INSTALL EXTENSION` 名称匹配。使用下划线——连字符需要在 SQL 中使用反引号引用。 |
| `version`     | 是  | MAJOR.MINOR.PATCH | 语义版本。                                                          |
| `description` | 否  | 字符串               | 在 `INFORMATION_SCHEMA.EXTENSIONS` 中显示。                         |
| `author`      | 否  | 字符串               | 作者姓名或组织。                                                       |
| `license`     | 否  | 字符串               | 许可证标识符。建议使用 `GPL-2.0` 作为开源扩展。                                  |

`name` 验证规则：必须以字母开头，以字母或数字结尾，最大长度为 64 个字符。无效的清单会导致 `INSTALL EXTENSION` 失败。
