> ## 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 はアルファ版です。リリース間で破壊的な API 変更が発生する可能性があります。関数のみの拡張機能、集約関数、可変長引数関数、およびカスタム型（encode、decode、compare、hash）がサポートされており、`sys_var`、`status_var`、`thread_worker`、`keyring` の各プレビュー機能もサポートされています。列ストレージ ABI は現在 C++ のみです。必要な場合は [C++ SDK](/docs/ja/mysql-9.7/stable/create) を使用してください。
</Warning>

このページは、`villagesql` クレート API のリファレンスです。 入門チュートリアルについては、[Rust で拡張機能を作成する](/docs/ja/mysql-9.7/stable/rust-sdk) を参照してください。 カスタム型については、[Rust のカスタム型](/docs/ja/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/ja/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 エントリポイントを生成します。 これは、クレート内に正確に 1 つ存在する必要があります。

```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/ja/mysql-9.7/stable/rust-preview-capabilities) を、`static` な機能オブジェクトへの参照として宣言します。 これは最後に、`funcs:` セクションの後に記述する必要があります。拡張機能が関数を登録しない場合は `funcs: []` を含めてください。

## func! マクロ

`func!` は、SQL で呼び出すことができる関数を宣言します。 6 つの形式があります。ステートメントごとの状態を持たない 4 つの形式 (パラメータなし、`buffer_size` のみ、`deterministic` のみ、両方) と、`prerun` 関数を通じてステートメントごとの状態を付加する 2 つの形式です。

```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` クレートの **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`      | オプション。 文字列/バイナリの戻り値に対する結果バッファーのバイト単位のサイズ。 サーバーのデフォルト (256 バイト) を使用するには `0` を指定します。 関数が `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 関数は最初の行の前に 1 回実行され、その後、行関数がその状態への `&mut` アクセスを伴って行ごとに 1 回実行されます。

prerun 関数のシグネチャは `fn(PrerunArgs, PrerunResult<T>)` であり、それが状態を渡す行関数は、状態を最初に受け取ります: `fn(state: &mut T, args: &[InValue]) -> VdfReturn`。 `T` は `state:` で指定された型であり、コンパイラーは prerun 関数と行関数がその型について一致していることをチェックします。

| `PrerunResult<T>` のメソッド                    | 効果                                                                                |
| ------------------------------------------ | --------------------------------------------------------------------------------- |
| `set_state(self, state: T)`                | ステートメントごとの状態を割り当て、サーバーに渡します。 これは `self` を借用するのではなく消費するため、コンパイラーは 2 回目の呼び出しを拒否します。 |
| `request_buffer_size(&mut self, n: usize)` | 特定の結果バッファーのサイズをサーバーに要求します。                                                        |
| `error(self, msg: &str)`                   | メッセージとともにステートメント全体を失敗させます。                                                        |

`PrerunArgs::len()` は各行が受け取る引数の数であり、`PrerunArgs::is_empty()` は関数が引数なしで呼び出された場合に true になります。

状態を自分で解放してはいけません。 `func!` は、ステートメントが終了したときに状態を破棄する postrun を生成します。 これは、postrun が `delete_state<T>()` を呼び出す必要がある C++ SDK とは逆です。[ステートメントごとの状態](/docs/ja/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/ja/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
```

このテーブルには 3 行があるため、`call_index()` は 3 回実行され、`1`、`2`、`3` を順に返します。行ごとに 1 つの値です。 `SUM` はこれら 3 つの値を加算し、`6` になります。

2 番目の `SELECT` は、より大きな値ではなく、最初と同じ合計を返します。カウンターは 1 つのステートメントに対して割り当てられ、それが終了したときに破棄されるためです。

## agg\_func! マクロ

`agg_func!` は集約 SQL 関数を宣言します。SUM/COUNT のように、行ごとに 1 回ではなく、各グループの行全体に対して呼び出される関数です。2 つの形式があります。

```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`。完成したアキュムレーターから 1 つのグループの出力を生成します。グループごとに 1 回、そのグループの最後の行が畳み込まれた後に実行されます。`&mut` ではなく `&State` を受け取ります。結果関数はアキュムレーターを読み取るだけで、リセットはしません。 |
| `"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` を derive するか、手動で実装してください。        |
| `clear: clear_fn`                        | `fn(&mut State)`。各グループの開始時にアキュムレーターをリセットします。                                                                                                                    |
| `accumulate: accumulate_fn`              | `fn(&mut State, &[InValue])`。1 行をアキュムレーターに畳み込みます。行ごとに 1 回実行されます。何も返しません。値は `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>

アキュムレーターはステートメントごとに 1 回割り当てられ、ステートメントの終了時に破棄されます。`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 のグループと空のグループは `0` ではなく NULL を返します。

```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/ja/mysql-9.7/stable/development#varargs-vdfs)。
</Warning>

6 つの形式があります。3 つの形があり、それぞれに省略形と、`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()` が true の場合にのみ `Some`。スカラー型では `None`。 |

`is_custom()` と `custom_name()` を組み合わせることで、特定の 1 つのカスタム型だけを受け入れられます。`is_custom()` だけでは、サーバー内のすべてのカスタム型を受け入れてしまいます。

<Note>
  `varargs_func!` と `PrerunArgs::type_at` は、まだ公開されたリリースには
  含まれていません。現在の [crates.io](https://crates.io/crates/villagesql)
  リリース (`0.0.1`) ではこれらを公開していません。
</Note>

SDK リポジトリの `vsql_varargs` の例は、形式ごとに 1 つの関数を宣言しています。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` は失敗します。
