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

# タイプ操作

> C++タイプ操作ビルダーの詳細なリファレンス — エンコード、デコード、比較、ハッシュ、組み込みデフォルト、およびパラメータ化されたタイプ。

このページは、C++タイプ操作ビルダーのリファレンスです。カスタムタイプのチュートリアルレベルの導入については、[C++でのカスタム型](/docs/ja/mysql-8.4/0.0.5/custom-types)を参照してください。

カスタムタイプには、エンジンが内部的に呼び出す3つの操作が必要です。エンコード（文字列からバイナリ）、デコード（バイナリから文字列）、および比較です。ハッシュはオプションです。これらのC++シグネチャに対して実装します（すべて`<villagesql/vsql.h>`から利用可能）。

## 固定長タイプ

```cpp theme={null}
// Encode: string -> binary. Write the encoded bytes via out.buffer() and
// out.set_length(n); call out.set_null() for SQL NULL, out.warning(msg) for
// recoverable bad input, or out.error(msg) to abort the statement. Returning
// without calling any of these surfaces a default warning.
using TypeEncodeFunc = void (*)(std::string_view from, vsql::CustomResult out);

// Decode: binary -> string. Report the outcome by calling
// out.set_length(n), out.set(sv), out.set_null(), out.warning(msg), or
// out.error(msg). If none is called the SDK falls back to a default
// "failed to decode value" ERROR.
using TypeDecodeFunc = void (*)(vsql::CustomArg in, vsql::StringResult out);

// Compare: returns -1, 0, or 1 (used for ORDER BY and indexes).
using TypeCompareFunc = int (*)(vsql::CustomArg a, vsql::CustomArg b);

// Hash: returns hash code (used for hash joins).
using TypeHashFunc = size_t (*)(vsql::CustomArg in);
```

これらの操作は、`vsql::make_type<kTypeName>()`を使用して登録します。タイプ名は、非型テンプレートパラメータ（NTTP）として渡されます。`static constexpr const char[]`配列です。ビルダーは、このNTTPから`TYPE::method`形式（例：`"MYTYPE::from_string"`）のVDF名を自動生成するため、手動での文字列マッチングは不要です。ビルドされたタイプオブジェクトを、拡張ビルダーの`.type()`に渡します。タイプ操作用の個別の`.func()`呼び出しは必要ありません。

<Warning>
  タイプ名は`static constexpr const char[]`変数でなければなりません。文字列リテラルは、非型テンプレートパラメータとして使用できません。`"MYTYPE"`を直接渡すと、次のようなコンパイルエラーが発生します。

  ```
  error: '"MYTYPE"' is not a valid template argument for type 'const char*'
  ```

  以下に示すように、名前を名前付き配列として宣言します。
</Warning>

```cpp theme={null}
#include <villagesql/vsql.h>

using namespace vsql;

static constexpr const char kMyTypeName[] = "MYTYPE";

constexpr auto MYTYPE =
    vsql::make_type<kMyTypeName>()
        .persisted_length(8)
        .max_decode_buffer_length(64)
        .from_string<&my_encode>()   // auto: "MYTYPE::from_string"
        .to_string<&my_decode>()     // auto: "MYTYPE::to_string"
        .compare<&my_compare>()      // auto: "MYTYPE::compare"
        .hash<&my_hash>()            // optional, auto: "MYTYPE::hash"
        .intrinsic_default_str("0")  // string-literal intrinsic default
        .build();

VEF_GENERATE_ENTRY_POINTS(
    make_extension()
        .type(MYTYPE))
```

`from_string`、`to_string`、または`compare`が欠落している場合、`build()`はコンパイル時に失敗します。各テンプレートメソッドは、`static_assert`を使用して関数ポインタのシグネチャを検証します。

<h2 id="intrinsic-default">
  組み込みデフォルト
</h2>

`NOT NULL`のカスタムタイプカラムが`IGNORE`モード（例：`INSERT IGNORE`または`UPDATE IGNORE`）で`NULL`を受け取った場合、サーバーはエラーを発生させる代わりに、組み込みデフォルトを呼び出してフォールバック値を生成します。組み込みデフォルトは文字列表現を提供し、サーバーはタイプの`from_string`関数を使用してそれをバイナリに変換します。

<Note>
  `.intrinsic_default_str()`と`.intrinsic_default_vdf()`の両方を省略すると、サーバーはフォールバックとして`from_string("")`を呼び出します。これは、タイプが**最初に使用される**とき（テーブル作成時）に発生し、`INSTALL EXTENSION`時ではありません。エンコード関数が空の文字列を拒否する場合、または間違ったバイト数にエンコードする場合、タイプの初期化はSQLクライアントに表示されるエラーで失敗します。

  ```
  Type 'MYTYPE' failed to initialize: from_string VDF encoded intrinsic
  default input '' to N bytes, expected persisted_length=M
  ```

  固定長タイプの場合、デフォルト文字列は正確に`persisted_length`バイトにエンコードされる必要があります。空の文字列が有効な入力ではないタイプには、明示的なデフォルトを設定してください。
</Note>

**文字列リテラル：`.intrinsic_default_str()`**

定数のデフォルトの場合は、タイプビルダーで文字列を直接渡します（上記の固定長の例で`.intrinsic_default_str("0")`として示されています）。

**VDFベース：`.intrinsic_default_vdf()` + `make_intrinsic_default`**

デフォルト値がタイプパラメータに依存する場合は、次のいずれかのシグネチャに対して関数を実装します（`<villagesql/vsql.h>`から利用可能）。

<Warning>
  **破壊的変更**：`IntrinsicDefaultFunc`と
  `IntrinsicDefaultWithParamsFunc`は、`const char*`ではなく`std::string`を返します。
  既存の組み込みデフォルトの実装を更新して、直接`std::string`を返すようにしてください。
</Warning>

```cpp theme={null}
// Fixed (no type parameters):
using IntrinsicDefaultFunc = std::string (*)(char *error_msg);

// Parameterized (receives cached parsed params):
template <typename P>
using IntrinsicDefaultWithParamsFunc = std::string (*)(const P &,
                                                       char *error_msg);
```

デフォルト値の`std::string`表現を返します。エラーが発生した場合は、`error_msg`にメッセージを書き込み、任意の値を返します（SDKはエラーを検出するために`error_msg[0] != '\0'`をチェックします）。`make_intrinsic_default<&fn>("vdf_name")`（引数1つ：VDF名）で登録し、その名前をタイプビルダーの`.intrinsic_default_vdf()`で参照します。以下のパラメータ化されたタイプの例に、完全な登録パターンを示します。

```cpp theme={null}
std::string mytype_default(const MyTypeParams &p, char * /*error_msg*/) {
  return /* build string representation based on p */;
}
```

<h2 id="parameterized-types">
  パラメータ化されたタイプ
</h2>

パラメータ化されたタイプは、割り当てサイズとレイアウトを決定するために、エンコード、デコード、比較、およびハッシュの時点でカラムの宣言されたパラメータを必要とします。パース関数と逆の`to_strings`関数を持つパラメータ構造体を定義し、`.params<P, &ParseFunc, &ToStringsFunc>()`でタイプビルダーに両方を登録し、タイプ操作関数の最初の引数として`const P&`を使用します。SDKは一意のパラメータの組み合わせごとにパース結果をキャッシュするため、パース関数はタイプのインスタンス化ごとに最大1回だけ実行されます。`to_strings`関数は`parse`の逆です。型`P`のインスタンスを正規のキー/値の文字列形式に書き戻すため、サーバーは`parse`が消費するのと同じ形状で推論されたパラメータを公開できます。

```cpp theme={null}
struct MyTypeParams {
  int64_t dimension;
  static MyTypeParams parse(const std::map<std::string, std::string> &p) {
    return {.dimension = stoll(p.at("dimension"))};
  }
  static void to_strings(const MyTypeParams &p,
                         std::map<std::string, std::string> &out) {
    out["dimension"] = std::to_string(p.dimension);
  }
};

void mytype_encode(vsql::MaybeParams<MyTypeParams> &params,
                   std::string_view from, vsql::CustomResult out) {
  const MyTypeParams &p = params.value();  // is_known() is always true at runtime
  size_t bytes = (size_t)p.dimension * 4;
  auto buf = out.buffer();
  if (buf.size() < bytes) { out.error("MYTYPE: buffer too small"); return; }
  // ... parse from, write to buf ...
  out.set_length(bytes);
}

void mytype_decode(vsql::CustomArgWith<MyTypeParams> in,
                   vsql::StringResult out) {
  const MyTypeParams &p = in.params();
  // ... read p.dimension floats from in.value(), write to out.buffer() ...
  out.set_length(bytes_written);
}

int mytype_compare(vsql::CustomArgWith<MyTypeParams> a,
                   vsql::CustomArgWith<MyTypeParams> b) {
  // Returns -1, 0, or 1.
}

size_t mytype_hash(vsql::CustomArgWith<MyTypeParams> in) {
  // Returns hash code.
}

// Converts MYTYPE(N) integer syntax to a parameter map.
// Signature: IntToTypeParamsFunc from <villagesql/vsql.h>.
bool mytype_int_to_params_fn(int64_t value,
                             std::map<std::string, std::string> &params,
                             char *error_msg) {
  if (value <= 0) {
    snprintf(error_msg, VEF_MAX_ERROR_LEN,
             "MYTYPE: dimension must be a positive integer");
    return true;
  }
  params["dimension"] = std::to_string(value);
  return false;  // success
}

// Validates parameters and computes storage sizes.
// Signature: ResolveTypeParamsFunc from <villagesql/vsql.h>.
bool mytype_resolve_params_fn(const std::map<std::string, std::string> &params,
                              vsql::ResolvedTypeParams *result,
                              char *error_msg) {
  int64_t dim = std::stoll(params.at("dimension"));
  result->persisted_length = dim * 4;
  result->max_decode_buffer_length = 64;
  return false;  // success
}
```

タイプビルダーに`.params<>()`を登録します。`MYTYPE(N)`整数構文を処理するには`.int_to_params<&mytype_int_to_params_fn>()`を、パラメータを検証してストレージサイズを計算するには`.resolve_params<&mytype_resolve_params_fn>()`を使用します。すべての有効なパラメータ化にわたる永続化されるバイトサイズの上限を指定して`.max_persisted_length(N)`を呼び出します。サーバーはこれをタイプパラメータ推論のパスでのみ使用します。そこではまだパラメータを推論しておらず、エンコードバッファーのサイズを決定するために`resolve_params`を参照できないためです。VDFベースの組み込みデフォルトの場合は、VDF名を指定して`.intrinsic_default_vdf()`を使用し、`make_intrinsic_default<&mytype_default>()`を介してVDFを個別に登録します。

<Warning>
  `.max_persisted_length()`にはVEFプロトコル3以上が必要です。これを使用するタイプは、プロトコル3より前のサーバーではロードできません。
</Warning>

```cpp theme={null}
static constexpr const char kMyTypeName[] = "MYTYPE";

// Maximum valid dimension for MYTYPE.
constexpr int64_t kMyTypeMaxDimension = 1024;  // your max valid dimension
// Upper bound on MYTYPE's persisted byte size across all valid params.
constexpr int64_t kMyTypeMaxPersistedLength = kMyTypeMaxDimension * 4;

constexpr auto MYTYPE =
    vsql::make_type<kMyTypeName>()
        .variable_length_type()  // Protocol 4; use instead of persisted_length(-1)
        .max_decode_buffer_length(16)
        .max_persisted_length(kMyTypeMaxPersistedLength)
        .params<MyTypeParams, &MyTypeParams::parse, &MyTypeParams::to_strings>()
        .int_to_params<&mytype_int_to_params_fn>()
        .resolve_params<&mytype_resolve_params_fn>()
        .from_string<&mytype_encode>()
        .to_string<&mytype_decode>()
        .compare<&mytype_compare>()
        .intrinsic_default_vdf("mytype_intrinsic_default")
        .build();

using namespace vsql;

VEF_GENERATE_ENTRY_POINTS(
    make_extension()
        .type(MYTYPE)
        .func(make_intrinsic_default<&mytype_default>(
            "mytype_intrinsic_default")))
```

パラメータ化されたバリアント — `TypeEncodeWithParamsFunc<P>`、
`TypeDecodeWithParamsFunc<P>`、`TypeCompareWithParamsFunc<P>`、および
`TypeHashWithParamsFunc<P>` — は、`ParamsToStringsFunc<P>`
（`void fn(const P&, std::map<std::string,std::string>&)`）とともに、
`<villagesql/vsql.h>`から利用可能です。
`vsql::make_type`のテンプレートメソッドは、パラメータ引数を検出し、
パラメータキャッシュを自動的に経由します。エンコード関数は、
最初の引数として`vsql::MaybeParams<P> &`を取ります。`is_known()`は実行時に常に真であり、
`value()`は`const P&`を返します。デコード、比較、およびハッシュの
バリアントは、`params()`アクセサが`const P&`を返す`vsql::CustomArgWith<P>`を取ります。

**SQLでのパラメータの指定。** 2つの構文が`resolve_params`に到達します。

* **整数** — `MYTYPE(N)`。サーバーは`N`を`int_to_params`を通してルーティングし、
  パラメータマップを構築します。`.int_to_params<>()`が必要です。
* **文字列** — `MYTYPE('key=value,...')`。サーバーは文字列を正規化し、
  `resolve_params`を直接呼び出します。`int_to_params`は関与しません。
  `.resolve_params<>()`が登録されていれば常に利用可能です — 追加のビルダー呼び出しは不要です。

`.resolve_params<>()`のみを登録するタイプは、文字列形式を受け入れ、
`MYTYPE(N)`を拒否します。`SHOW CREATE TABLE`は、書き込まれた形式を保持します。

```sql theme={null}
CREATE TABLE t (v ext.MYTYPE(8));              -- integer form (.int_to_params)
CREATE TABLE t2 (v ext.MYTYPE('dimension=8')); -- string form (.resolve_params only)
```

<Note>
  `int_to_params`が生成し、`resolve_params`が消費する
  シリアライズされた`key=value,...`パラメータ文字列は、
  `VEF_MAX_TYPE_PARAMS_STRING_LEN`（1024バイト）で上限が設定されています。
  正規の文字列がその制限を超えるパラメータ化は、
  黙って切り詰められるのではなく、定義されたエラーで拒否されます —
  単一のタイプのパラメータ名と値を合わせて1024バイト以内に収めてください。
</Note>

### パラメータの書き換えとデフォルトの指定

`resolve_params`には、2つ目の変更用オーバーロードがあります。パラメータマップを
非const参照で受け取るため、タイプがそれを書き換えることができます — 通常は、
作者が省略したデフォルトを埋めるためです。同じ方法で登録します
（`.resolve_params<&fn>()`はどちらの形式も受け入れます。1つだけ登録してください）。

```cpp theme={null}
bool mytype_resolve_params_fn(std::map<std::string, std::string> &params,
                              vsql::ResolvedTypeParams *result, char *error_msg) {
  if (params.find("dimension") == params.end())
    params["dimension"] = "128";                 // supply a default
  int64_t dim = std::stoll(params.at("dimension"));
  result->persisted_length = dim * 4;
  result->max_decode_buffer_length = 64;
  return false;                                  // success
}
```

書き換えられたマップは、サーバーが永続化し、`SHOW CREATE TABLE`が出力する
正規のパラメータ文字列になるため、書き換えは冪等でなければなりません。**素の**
宣言（`MYTYPE`、長さやパラメータなし）は、それをスキップするのではなく、
空のマップで`resolve_params`を呼び出すようになったため、デフォルトを提供するタイプは、
すべてのカラムに明示的なパラメータを与えます — `vsql_bitfield_test`の`BITFIELD`は、
素のカラムを`max_number_of_bits=4096`に解決します。

```sql theme={null}
INSTALL EXTENSION vsql_bitfield_test;
CREATE TABLE bits (id INT PRIMARY KEY, b vsql_bitfield_test.BITFIELD);
SHOW CREATE TABLE bits;   -- b persists as BITFIELD('max_number_of_bits=4096')
```

## 可変長タイプ

可変長カスタムタイプは、単一の固定フットプリントを使用するのではなく、
値ごとに永続化されるサイズを決定します。タイプビルダーで`.variable_length_type()`を
呼び出して宣言します。これはタイプの`variable_length`フラグを設定します。

<Warning>
  `.variable_length_type()`は、タイプの必須プロトコルをVEFプロトコル4に引き上げます。
  サーバーは、プロトコル4以上でのみ`variable_length`フラグを読み取ります。
  オプトインの開発ABIヘッダー（`-DVSQL_USE_DEV_ABI=ON`）に対してビルドします。古いサーバーは
  フラグを読み取りません。
</Warning>

可変長タイプは、`.max_persisted_length(N)`も呼び出す必要があります。これが省略されると、
`build()`はコンパイル時に失敗します — サーバーは、バッキングフィールドのバッファーを
割り当てるために上限を必要とします。

`.variable_length_type()`は一方向で、一度引き上げられたプロトコル要件は下がりません。プロトコル3のセッター
（`max_persisted_length()`、`params()`、`int_to_params()`）の前後に呼び出しても、
プロトコル要件がプロトコル4より下に戻ることはありません。

```cpp theme={null}
#include <villagesql/vsql.h>
using namespace vsql;

static constexpr const char kMyTypeName[] = "MYTYPE";
constexpr int64_t kMyTypeMaxPersistedLength = 4096;

constexpr auto MYTYPE =
    vsql::make_type<kMyTypeName>()
        .variable_length_type()  // per-value sizing; requires Protocol 4
        .max_persisted_length(kMyTypeMaxPersistedLength)
        .max_decode_buffer_length(64)
        .from_string<&my_encode>()
        .to_string<&my_decode>()
        .compare<&my_compare>()
        .build();

VEF_GENERATE_ENTRY_POINTS(
    make_extension()
        .type(MYTYPE))
```

<Note>
  すべてのカスタムタイプと同様に、可変長タイプは使用可能な
  [組み込みデフォルト](#intrinsic-default)を生成する必要があります。デフォルトは
  フィールドの最大容量にエンコードされ、1から`max_persisted_length`バイトまでの
  空でない結果はすべて受け入れられます。空の文字列のエンコードが**ゼロ**バイトを生成する
  タイプ — 空の配列やビットセットなど — には使用可能なデフォルトがないため、
  空でない値にエンコードされる明示的なデフォルトを宣言してください。

  ```cpp theme={null}
          .max_persisted_length(kMyTypeMaxPersistedLength)
          .intrinsic_default_str("[0]")  // empty "[]" would encode to zero bytes
  ```

  そうしないと、`NOT NULL`カラムが最初にそれを参照するとき、`CREATE TABLE`時に
  タイプの初期化に失敗します — `from_string("")`をエンコードできない固定長タイプと
  同じです。
</Note>

## ストアドプロシージャでのカスタムタイプ

カスタム拡張タイプは、ストアドプロシージャのパラメータタイプとして、および
`DECLARE`変数宣言で使用できます。サーバーは、インストールされた拡張機能の
タイプメタデータを使用して、ルーチンの実行時にカスタムタイプを解決します。

```sql theme={null}
DELIMITER //
CREATE PROCEDURE insert_complex(IN val COMPLEX)
BEGIN
  DECLARE tmp COMPLEX;
  SET tmp = val;
  INSERT INTO t1 VALUES (tmp);
END //
DELIMITER ;
```

## 関連項目

* [C++でのカスタム型](/docs/ja/mysql-8.4/0.0.5/custom-types) — カスタムタイプのチュートリアル導入
* [C++ APIリファレンス](/docs/ja/mysql-8.4/0.0.5/extension-api-reference) — VDFコントラクト、null処理、およびバッファーサイズ
* [C++開発](/docs/ja/mysql-8.4/0.0.5/development) — VDFの作成の詳細、引数と結果のタイプ、集計、可変長引数
