> ## 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++ SDK 学习 vsql_complex 参考实现

`vsql_complex` 扩展是 VillageSQL 使用 C++ SDK 的自定义类型的参考实现，展示了生产就绪的扩展模式。

\*\*源代码：\*\*位于 [VillageSQL 仓库](https://github.com/villagesql/villagesql-server/tree/main/villagesql/examples/vsql-complex) 的 `villagesql/examples/vsql-complex/` 目录中。

***

## vsql\_complex 提供的功能

COMPLEX 类型用于表示复数（a + bi），并提供算术运算、工具函数和聚合功能。

**用法示例：**

```sql theme={null}
INSTALL EXTENSION vsql_complex;

CREATE TABLE signals (
    id INT PRIMARY KEY,
    impedance COMPLEX
);

INSERT INTO signals VALUES (1, '(50.0,10.0)');

SELECT
    complex_real(impedance) as resistance,
    complex_imag(impedance) as reactance,
    complex_abs(impedance) as magnitude
FROM signals;
```

***

## 目录结构

```
vsql_complex/
├── CMakeLists.txt       # Build config with VEF_CREATE_VEB
├── manifest.json        # Extension metadata
├── src/
│   └── complex.cc       # Complete implementation: types, functions, and VEF registration
└── test/
    ├── t/*.test         # Test cases
    └── r/*.result       # Expected results
```

***

## VEF 注册模式

**文件：`src/complex.cc`**

vsql\_complex 使用 C++ SDK，并使用 `VEF_GENERATE_ENTRY_POINTS()`：

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

// Type name constants as char arrays — required as non-type template parameters
// (NTTPs) so that make_type can auto-generate VDF names like "COMPLEX::from_string".
static constexpr const char kComplexTypeName[] = "COMPLEX";
static constexpr const char kComplex2TypeName[] = "COMPLEX2";

// Type objects: encode/decode/compare VDFs are embedded via template parameters.
// No separate .func() registration is needed for type operations.
constexpr auto COMPLEX =
    vsql::make_type<kComplexTypeName>()
        .persisted_length(kComplexSize)
        .max_decode_buffer_length(64)
        .from_string<&complex_from_string>()
        .to_string<&complex_to_string>()
        .compare<&complex_compare>()
        .intrinsic_default_str("(0,0)")
        .build();

constexpr auto COMPLEX2 =
    vsql::make_type<kComplex2TypeName>()
        .persisted_length(kComplexSize)
        .max_decode_buffer_length(64)
        .from_string<&complex2_from_string>()
        .to_string<&complex_to_string>()
        .compare<&complex_compare>()
        .hash<&complex2_hash>()
        .intrinsic_default_str("(0,0)")
        .build();

using namespace vsql;

VEF_GENERATE_ENTRY_POINTS(
    make_extension()
        .type(COMPLEX)
        .type(COMPLEX2)
        // Arithmetic operations
        .func(make_func<&complex_add_impl>("complex_add")
                  .returns(COMPLEX)
                  .param(COMPLEX)
                  .param(COMPLEX)
                  .deterministic()
                  .build())
        .func(make_func<&complex_subtract_impl>("complex_subtract")
                  .returns(COMPLEX)
                  .param(COMPLEX)
                  .param(COMPLEX)
                  .deterministic()
                  .build())
        .func(make_func<&complex_multiply_impl>("complex_multiply")
                  .returns(COMPLEX)
                  .param(COMPLEX)
                  .param(COMPLEX)
                  .deterministic()
                  .build())
        .func(make_func<&complex_divide_impl>("complex_divide")
                  .returns(COMPLEX)
                  .param(COMPLEX)
                  .param(COMPLEX)
                  .deterministic()
                  .build())
        // Utility functions
        .func(make_func<&complex_real_impl>("complex_real")
                  .returns(REAL)
                  .param(COMPLEX)
                  .build())
        .func(make_func<&complex_imag_impl>("complex_imag")
                  .returns(REAL)
                  .param(COMPLEX)
                  .build())
        .func(make_func<&complex_abs_impl>("complex_abs")
                  .returns(REAL)
                  .param(COMPLEX)
                  .build())
        .func(make_func<&complex_conjugate_impl>("complex_conjugate")
                  .returns(COMPLEX)
                  .param(COMPLEX)
                  .build())
        // Aggregate functions
        .func(make_aggregate_func<ComplexSumState, &complex_sum_result>(
                  "complex_sum")
                  .returns(COMPLEX)
                  .param(COMPLEX)
                  .clear<&complex_sum_clear>()
                  .accumulate<&complex_sum_accumulate>()
                  .build()))
```

**关键模式：**

* 单个宏调用注册所有内容，无需手动编写 SQL
* 类型对象是使用 `vsql::make_type<kName>()` 构建的 `constexpr` 变量，其中 `kName` 是用作非类型模板参数的 `static constexpr const char[]`
* 类型操作（`.from_string<>()`、`.to_string<>()`、`.compare<>()`、`.hash<>()`）嵌入在类型对象中，无需为它们进行单独的 `.func()` 调用
* `.compare()` 启用 ORDER BY 和索引；`.hash()` 是可选的
* `.intrinsic_default_str("(0,0)")` 设置了当 INSERT IGNORE 或 UPDATE IGNORE 将 NULL 赋值给此类型的 NOT NULL 列时写入的默认值
* 函数注册使用 `make_func<&impl>("name")`，并使用 `.build()`
* 聚合注册使用 `make_aggregate_func<State, &result_fn>("name")`，并使用 `.clear<&fn>()` 和 `.accumulate<&fn>()`；结果函数具有签名 `void(const State&, ResultType)`

***

## 二进制存储格式

COMPLEX 存储 **16 字节**（小端字节序）：

* 字节 0-7：实部（double）
* 字节 8-15：虚部（double）

**编码/解码（complex.cc）：**

```cpp theme={null}
void complex_from_string(std::string_view from, vsql::CustomResult out);
void complex_to_string(vsql::CustomArg in, vsql::StringResult out);
```

使用与平台无关的字节序函数，以实现跨平台兼容性。

***

## 参数和结果类型

实现使用类型化的参数和结果类型，而不是原始协议结构：

**算术示例（来自 complex.cc）：**

```cpp theme={null}
void complex_add_impl(CustomArg in_l, CustomArg in_r, CustomResult out) {
  // Handle NULL inputs and validate arguments
  // ...
  store_complex(out.buffer().data(), Complex{lhs.re + rhs.re, lhs.im + rhs.im});
  out.set_length(kComplexSize);
}
```

**工具函数（来自 complex.cc）：**

```cpp theme={null}
void complex_real_impl(CustomArg in, RealResult out) {
  // Handle NULL and validate
  // ...
  out.set(cx.re);
}

void complex_abs_impl(CustomArg in, RealResult out) {
  // Handle NULL and validate
  // ...
  out.set(sqrt(cx.re * cx.re + cx.im * cx.im));
}
```

***

## 测试策略

**测试文件（test/t/complex\_create.test）：**

```sql theme={null}
INSTALL EXTENSION vsql_complex;

CREATE TABLE t1 (id INT, val COMPLEX);
INSERT INTO t1 VALUES (1, '(1.0,2.0)');
SELECT * FROM t1;

DROP TABLE t1;
UNINSTALL EXTENSION vsql_complex;
```

**生成结果：**

```bash theme={null}
cd /path/to/villagesql/build/mysql-test
./mysql-test-run.pl --suite=vsql_complex --record
```

**运行测试：**

```bash theme={null}
./mysql-test-run.pl --suite=vsql_complex
```

***

## 关键实现模式

| 模式          | 用途                                              |
| ----------- | ----------------------------------------------- |
| **固定长度存储**  | 在类型构建器中设置 `.persisted_length()`                 |
| **平台无关性**   | 使用自定义的字节序函数处理 double 类型                         |
| **NULL 处理** | 在参数类型上调用 `in.is_null()`                         |
| **错误处理**    | 在结果类型上调用 `out.error("message")`                 |
| **结果输出**    | 在结果类型上调用 `out.set(value)` / `out.set_length(n)` |

***

## 清单文件

**文件：`manifest.json`**

```json theme={null}
{
  "name": "vsql_complex",
  "version": "0.0.1",
  "description": "Complex number data type for VillageSQL",
  "author": "VillageSQL Contributors",
  "license": "GPL-2.0"
}
```

***

## 使用自定义类型导出和导入数据

VillageSQL 支持针对自定义类型的 `SELECT INTO OUTFILE` 和 `LOAD DATA INFILE` 操作，允许您导出和导入数据，同时保留自定义类型的值。

### SELECT INTO OUTFILE

自定义类型在导出时序列化为它们的字符串表示形式：

```sql theme={null}
INSTALL EXTENSION vsql_complex;

-- Create table with custom type
CREATE TABLE signals (
    id INT PRIMARY KEY,
    reading COMPLEX
);

INSERT INTO signals VALUES
    (1, '(3.0,4.0)'),
    (2, '(5.0,12.0)'),
    (3, '(-1.0,2.0)');

-- Export to file
SELECT * FROM signals INTO OUTFILE '/tmp/signals_export.txt';
```

**文件内容（`/tmp/signals_export.txt`）：**

```
1	(3.00,4.00)
2	(5.00,12.00)
3	(-1.00,2.00)
```

### LOAD DATA INFILE

将导出的数据加载回表中：

```sql theme={null}
-- Create new table with same schema
CREATE TABLE signals_imported (
    id INT PRIMARY KEY,
    reading COMPLEX
);

-- Import data
LOAD DATA INFILE '/tmp/signals_export.txt' INTO TABLE signals_imported;

-- Verify
SELECT * FROM signals_imported;
```

### 使用自定义分隔符导出

您可以使用自定义字段和行终止符：

```sql theme={null}
SELECT * FROM signals
INTO OUTFILE '/tmp/signals_csv.txt'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
```

**输出：**

```
"1","(3.00,4.00)"
"2","(5.00,12.00)"
"3","(-1.00,2.00)"
```

### 使用 VDF 函数导出

使用扩展函数导出计算值：

```sql theme={null}
SELECT
    id,
    reading,
    complex_abs(reading) AS magnitude,
    complex_real(reading) AS real_part,
    complex_imag(reading) AS imag_part
INTO OUTFILE '/tmp/signals_computed.txt'
FROM signals;
```

<Note>
  自定义类型以其字符串表示形式导出。目前不支持使用 `SELECT INTO DUMPFILE` 进行自定义类型的二进制导出。
</Note>

***

## 后续步骤

<CardGroup cols={2}>
  <Card title="创建扩展" icon="code" href="/docs/zh/mysql-8.4/0.0.5/create">
    构建您自己的扩展
  </Card>

  <Card title="扩展架构" icon="sitemap" href="/docs/zh/mysql-8.4/0.0.5/architecture">
    了解内部结构
  </Card>

  <Card title="vsql_complex 源代码" icon="github" href="https://github.com/villagesql/villagesql-server/tree/main/villagesql/examples/vsql-complex">
    查看完整的源代码
  </Card>

  <Card title="可用扩展" icon="list" href="/docs/zh/mysql-8.4/0.0.5/extensions">
    浏览扩展目录
  </Card>
</CardGroup>
