> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-trino-dialect.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> WebAssembly 사용자 정의 함수 문서

# WebAssembly 사용자 정의 함수

export const ExperimentalBadge = () => {
  return <a href="https://clickhouse.com/docs/reference/settings/beta-and-experimental-features#experimental-features" className="experimentalBadge">
            <div className="experimentalIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.25" d="M5.5 2H10.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M9.50015 2V6.19625L13.4283 12.7425C13.4738 12.8183 13.4985 12.9049 13.4996 12.9934C13.5008 13.0818 13.4785 13.169 13.435 13.246C13.3914 13.323 13.3283 13.3871 13.2519 13.4317C13.1755 13.4764 13.0886 13.4999 13.0002 13.5H3.00015C2.91164 13.5 2.8247 13.4766 2.74822 13.432C2.67174 13.3874 2.60847 13.3233 2.56487 13.2463C2.52126 13.1693 2.49889 13.082 2.50004 12.9935C2.50119 12.905 2.52582 12.8184 2.5714 12.7425L6.50015 6.19625V2" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M4.47656 9.56754C5.30344 9.41254 6.47656 9.47942 7.99969 10.25C10.0153 11.2707 11.4216 11.0569 12.2184 10.7282" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            실험 기능
        </a>;
};

export const CloudNotSupportedBadge = () => {
  return <a href="https://clickhouse.com/docs/products/cloud/guides/cloud-compatibility#list-of-unsupported-features" className="cloudNotSupportedBadge">
            <div className="cloudNotSupportedIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.5" d="M6.33366 12.6666L12.3739 12.6667C13.6593 12.6667 14.7073 11.6187 14.7073 10.3334C14.7073 9.04804 13.6593 8.00003 12.3739 8.00003C12.3739 8.00003 12.3337 7.66659 12.0003 7.33325M10.667 5.33322C8.00033 2.33325 4.45395 4.78537 4.14195 6.68203C2.55728 6.7627 1.29395 8.06203 1.29395 9.6667C1.29395 11.3234 2.66699 12.6666 4.00033 12.6666" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.5" d="M2.66699 14L12.0003 4.66663" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>

        </div>
            ClickHouse Cloud에서 지원되지 않음
        </a>;
};

ClickHouse는 WebAssembly로 작성된 사용자 정의 함수(UDF)를 생성할 수 있도록 지원합니다. 이를 통해 Rust, C, C++ 등의 언어로 작성한 사용자 정의 로직을 WebAssembly 모듈로 컴파일해 실행할 수 있습니다.

<CloudNotSupportedBadge />

<ExperimentalBadge />

<div id="overview">
  ## 개요
</div>

WebAssembly 모듈은 ClickHouse에서 호출할 수 있는 하나 이상의 함수를 포함하는 컴파일된 바이너리 파일입니다.
모듈은 한 번 로드해 여러 번 재사용하는 라이브러리나 공유 객체로 생각하면 됩니다.

UDF를 포함하는 WebAssembly 모듈은 Rust, C, C++처럼 WebAssembly로 컴파일할 수 있는 모든 언어로 작성할 수 있습니다.

WebAssembly로 컴파일된 코드("guest" 코드)와 ClickHouse에서 실행되는 코드("host")는 전용 메모리 공간에만 접근할 수 있는 샌드박스 환경에서 실행됩니다.

게스트 코드는 ClickHouse가 호출할 수 있는 함수를 export합니다. 여기에는 사용자 지정 로직을 구현하는 함수(UDF 정의에 사용됨)뿐 아니라 메모리 관리와 ClickHouse와 WebAssembly 코드 간 데이터 교환에 필요한 지원 함수도 포함됩니다.

코드는 운영 체제나 표준 라이브러리에 의존하지 않는 "freestanding" WebAssembly(즉, `wasm32-unknown-unknown`)로 컴파일해야 합니다. 또한 기본 32비트 WebAssembly 대상만 지원됩니다(`wasm64` 확장은 지원되지 않음).
모듈은 ClickHouse와 상호작용하기 위해 지원되는 통신 프로토콜(ABI) 중 하나를 따라야 합니다.

컴파일이 완료되면 모듈의 바이너리 코드는 `system.webassembly_modules` 테이블에 삽입되어 ClickHouse에 로드됩니다.
그 후 `CREATE FUNCTION ... LANGUAGE WASM` 구문을 사용해 모듈이 내보낸 함수를 참조하는 UDF를 생성할 수 있습니다.

<div id="prerequisites">
  ## 사전 요구 사항
</div>

ClickHouse 구성에서 WebAssembly 지원을 사용 설정하세요:

```xml theme={null}
<clickhouse>
    <allow_experimental_webassembly_udf>true</allow_experimental_webassembly_udf>
    <webassembly_udf_engine>wasmtime</webassembly_udf_engine>
</clickhouse>
```

사용 가능한 Engine 구현:

* `wasmtime` (기본값, 권장) — [WasmTime](https://github.com/bytecodealliance/wasmtime)을 사용합니다
* `wasmedge` — [WasmEdge](https://github.com/WasmEdge/WasmEdge)를 사용합니다

<div id="quick-start">
  ## 빠른 시작
</div>

이 예시는 [콜라츠 추측](https://en.wikipedia.org/wiki/Collatz_conjecture) 계산기를 구현하여 WebAssembly UDF를 만드는 전체 워크플로를 보여줍니다.

이 단계에서는 프로그래밍 언어가 필요하지 않으므로, WebAssembly를 사람이 읽을 수 있는 형태로 표현한 WebAssembly Text 포맷(WAT)으로 코드를 작성합니다.
ClickHouse는 모듈이 바이너리 형식이어야 하므로, 트랜스파일러를 사용해 WAT를 WASM으로 변환합니다.
이 변환을 수행하려면 [WebAssembly Binary Toolkit (WABT)](https://github.com/WebAssembly/wabt)의 `wat2wasm` 또는 [wasm-tools](https://github.com/bytecodealliance/wasm-tools)의 `parse` 명령을 사용할 수 있습니다.

```bash theme={null}
cat << 'EOF' | wasm-tools parse | clickhouse client -q "INSERT INTO system.webassembly_modules (name, code) SELECT 'collatz', code FROM input('code String') FORMAT RawBlob"
(module
  (func $next (param $n i32) (result i32)
    local.get $n i32.const 1 i32.and
    (if (result i32)
      (then local.get $n i32.const 3 i32.mul i32.const 1 i32.add)
      (else local.get $n i32.const 2 i32.div_u)))
  (func $steps (export "steps") (param $n i32) (result i32)
    (local $count i32)
    local.get $n i32.const 1 i32.lt_u
    (if (then i32.const 0 return))
    (block $done (loop $loop
      local.get $n i32.const 1 i32.eq br_if $done
      local.get $n call $next local.set $n
      local.get $count i32.const 1 i32.add local.set $count
      br $loop))
    local.get $count)
)
EOF
```

위 스니펫에서는 바이너리 WASM 코드를 `FORMAT RawBlob`을 사용해 ClickHouse client로 직접 파이프하여 `system.webassembly_modules` 테이블에 삽입합니다.

그런 다음 모듈이 내보내는 `steps` 함수를 참조하는 UDF를 정의합니다:

```sql theme={null}
CREATE FUNCTION collatz_steps LANGUAGE WASM ARGUMENTS (n UInt32) RETURNS UInt32 FROM 'collatz' :: 'steps';
```

UDF 이름과 다르므로 `::` 뒤에는 모듈의 함수 이름을 지정한다는 점에 유의하십시오.

이제 쿼리에서 `collatz_steps` 함수를 사용할 수 있습니다:

```sql theme={null}
SELECT groupArray(collatz_steps(number :: UInt32))
FROM numbers(1, 100)
FORMAT TSV
```

`number` 컬럼은 `UInt32`로 명시적으로 CAST됩니다. 이는 WebAssembly 함수가 `CREATE FUNCTION` 문에 지정된 시그니처와 정확히 일치하는 타입을 요구하기 때문입니다.

그 결과, 1부터 100까지의 수에 대한 Collatz 단계 수열을 얻을 수 있으며, 이는 [OEIS의 A006577 수열](https://oeis.org/A006577)에 해당합니다.

```text theme={null}
[0,1,7,2,5,8,16,3,19,6,14,9,9,17,17,4,12,20,20,7,7,15,15,10,23,10,111,18,18,18,106,5,26,13,13,21,21,21,34,8,109,8,29,16,16,16,104,11,24,24,24,11,11,112,112,19,32,19,32,19,19,107,107,6,27,27,27,14,14,14,102,22,115,22,14,22,22,35,35,9,22,110,110,9,9,30,30,17,30,17,92,17,17,105,105,12,118,25,25,25]
```

<div id="manage-wasm-modules-via-system-table">
  ## 시스템 테이블을 통해 WASM 모듈 관리
</div>

WebAssembly 모듈은 다음 구조를 가진 `system.webassembly_modules` 테이블에 저장됩니다:

* **컬럼**
  * `name` String — 모듈 이름입니다. 비어 있을 수 없으며, 영문자, 숫자, 밑줄 문자만 사용할 수 있습니다.
  * `code` String — 원시 바이너리 WASM 코드입니다. 쓰기 전용이며, 읽기 시 빈 문자열이 반환됩니다.
  * `hash` UInt256 — 모듈 바이너리의 SHA256입니다(디스크에는 존재하지만 아직 로드되지 않은 경우 0).

모듈 관리는 이 테이블에 대한 표준 SQL 작업을 통해 수행됩니다:

<div id="insert-a-module">
  ### 모듈 삽입
</div>

```sql theme={null}
INSERT INTO system.webassembly_modules (name, code)
SELECT 'my_module', base64Decode('AGFzbQEAAAA...');
```

필요한 경우 무결성 해시를 제공할 수 있습니다:

```sql theme={null}
INSERT INTO system.webassembly_modules (name, code, hash)
SELECT 'my_module', base64Decode('...'), reinterpretAsUInt256(unhex('369f...c57d'));
```

제공된 해시가 모듈 코드에서 계산된 SHA256과 일치하지 않으면 삽입이 실패합니다. S3 또는 HTTP 같은 외부 소스에서 모듈을 로드할 때 유용합니다.

<div id="distribute-a-module-across-a-cluster">
  ### 클러스터 전체에 모듈 배포
</div>

`system.webassembly_modules`는 인스턴스별 table이므로 `INSERT`는 connection을 처리하는 레플리카에만 적용됩니다. `INSERT` 문에는 `ON CLUSTER` 형식이 없으므로, 이어서 `CREATE FUNCTION ... ON CLUSTER`를 실행하면 모듈이 없는 레플리카에서는 실패합니다:

```text theme={null}
Code: 674. DB::Exception: WebAssembly module 'collatz' not found:
while adding user defined function `collatz_steps`. (RESOURCE_NOT_FOUND)
```

삽입이 모든 노드로 전파되도록 하려면 로컬 `system.webassembly_modules` 테이블 대신 `cluster` 테이블 함수에 쓰세요:

```bash theme={null}
cat collatz.wasm | clickhouse client -q "
  INSERT INTO FUNCTION cluster('default', 'system', 'webassembly_modules') (name, code)
  SELECT 'collatz', code FROM input('code String') FORMAT RawBlob"
```

<Note>
  이 패턴은 기본 분산 쓰기 경로가 각 세그먼트 내의 모든 레플리카를 거치는 방식에 의존하며, 이는 클러스터가 `internal_replication=false`로 구성된 경우에만 가능합니다. `internal_replication=true`인 경우(`ReplicatedMergeTree`를 사용해 자체적으로 복제를 수행하는 클러스터의 기본 설정), 삽입은 세그먼트당 정상인 단일 레플리카에만 전달되고 `system.webassembly_modules`는 이 경로로 복제되지 않으므로 일부 레플리카에는 여전히 모듈이 없을 수 있습니다. 이 구성에서는 각 레플리카에 대해 개별적으로 삽입해야 합니다. 예를 들어 `system.clusters`를 순회하면서 각 호스트별로 `remote(...)`를 통해 기록하거나, 모든 호스트의 `user_scripts/wasm/`에 바이너리를 복사할 수 있습니다.

  클러스터의 `internal_replication` 설정은 `SELECT cluster, shard_num, internal_replication FROM system.clusters`로 확인할 수 있습니다.
</Note>

이처럼 분산된 삽입이 완료되면 모든 레플리카에 모듈이 존재하게 되며, `CREATE FUNCTION ... ON CLUSTER`가 성공합니다:

```sql theme={null}
CREATE FUNCTION collatz_steps ON CLUSTER 'default'
LANGUAGE WASM FROM 'collatz' :: 'steps'
ARGUMENTS (n UInt32) RETURNS UInt32;
```

`clusterAllReplicas`를 사용하면 모든 레플리카에 모듈이 로드되었는지 확인할 수 있습니다:

```sql theme={null}
SELECT hostName(), name FROM clusterAllReplicas('default', system.webassembly_modules) WHERE name = 'collatz';
```

`system.webassembly_modules`에 대한 삽입은 동일한 `(name, hash)` 쌍에 대해 멱등적이므로, 팬아웃된 삽입을 다시 실행해도 안전하며 레플리카 교체 후 상태를 복구하는 합리적인 방법입니다. 새로 추가된 서버에는 기존 모듈이 자동으로 전달되지 않는다는 점에 유의하십시오. 업데이트된 클러스터를 대상으로 삽입을 다시 실행하거나, 새 호스트의 `user_scripts/wasm/` 디렉터리에 바이너리를 배치해야 합니다.

<div id="list-modules">
  ### 모듈 목록 보기
</div>

```sql theme={null}
SELECT name, lower(hex(reinterpretAsFixedString(hash))) AS sha256 FROM system.webassembly_modules

   ┌─name────┬─sha256───────────────────────────────────────────────────────────┐
1. │ collatz │ a084a10b7b5cb07db198bc93bf1f3c1f8cb8ef279df7a4f6b66b1cdd55d79c48 │
   └─────────┴──────────────────────────────────────────────────────────────────┘
```

<div id="delete-a-module">
  ### 모듈 삭제
</div>

삭제는 `DELETE FROM system.webassembly_modules WHERE name = '...'` 문을 사용해 수행합니다.
프레디케이트는 정확한 일치에는 `name = 'literal'`, 패턴과 일치하는 모든 모듈을 삭제할 때는 `name LIKE 'pattern'`여야 하며, 그 밖의 형태는 허용되지 않습니다.

```sql theme={null}
DELETE FROM system.webassembly_modules WHERE name = 'collatz';

-- 이름이 `tmp_`로 시작하는 모든 모듈을 일괄 삭제합니다 (리터럴 밑줄은 `\_`로 이스케이프 처리됩니다):
DELETE FROM system.webassembly_modules WHERE name LIKE 'tmp\_%';
```

기존 UDF 중 일치하는 모듈을 참조하는 UDF가 있으면 삭제에 실패하므로, 먼저 해당 UDF를 삭제해야 합니다.

<div id="create-a-webassembly-udf">
  ## WebAssembly UDF 생성하기
</div>

**구문**:

```sql theme={null}
CREATE [OR REPLACE] FUNCTION function_name
LANGUAGE WASM
FROM 'module_name' [:: 'source_function_name']
ARGUMENTS ( [name type[, ...]] | [type[, ...]] )
RETURNS return_type
[ABI ROW_DIRECT | ABI BUFFERED_V1 | ABI ASSEMBLYSCRIPT]
[DETERMINISTIC]
[SHA256_HASH 'hex']
[SETTINGS key = value[, ...]];
```

**매개변수**:

* `function_name`: ClickHouse에서의 함수 이름입니다. 모듈의 내보낸 함수 이름과 다를 수 있습니다.
* `FROM 'module_name' :: 'source_function_name'`: 사용할 로드된 WASM 모듈 이름과 WASM 모듈 내 함수 이름입니다(기본값은 function\_name).
* `ARGUMENTS`: 인수 이름과 타입 목록입니다(이름은 선택 사항이며, 이름 있는 필드를 지원하는 직렬화 포맷에서 사용됩니다).
* `ABI`: Application Binary Interface 버전입니다.
  * `ROW_DIRECT`: 직접 타입 매핑, 행 단위 처리
  * `BUFFERED_V1`: 직렬화를 사용하는 블록 기반 처리
  * `ASSEMBLYSCRIPT`: [AssemblyScript](https://www.assemblyscript.org) 컴파일러로 생성된 모듈을 위한 행 단위 처리입니다. 숫자 타입은 AssemblyScript 기본 타입에 매핑되며, ClickHouse `String`은 AssemblyScript `string`에 매핑됩니다.
* `DETERMINISTIC`: 함수를 결정적으로 선언합니다 — 동일한 입력에 대해 항상 동일한 출력을 반환합니다. 지정하면 모든 인수가 상수인 호출에 대해 ClickHouse가 상수 폴딩을 수행할 수 있습니다. 함수는 쿼리 분석 시점에 한 번 평가되며, 결과는 모든 행에 재사용됩니다.
* `SHA256_HASH`: 검증에 사용할 예상 모듈 해시입니다(생략하면 자동으로 채워짐). 서로 다른 레플리카에서 올바른 WASM 모듈이 로드되었는지 확인하는 데 사용할 수 있습니다.
* `SETTINGS`: 함수별 설정
  * `serialization_format` String — 모듈에 전달되는 인수 블록을 직렬화하고 반환된 결과를 파싱하는 데 사용하는 포맷입니다. `ABI BUFFERED_V1`에서만 사용됩니다. 지원되는 값: `MsgPack`, `JSONEachRow`, `CSV`, `TSV`, `TSVRaw`, `RowBinary`, `Buffers`. 기본값: `MsgPack`. `Buffers`와 같은 블록 기반 포맷은 선언된 함수 시그니처와 타입이 일치하는 단일 컬럼 하나를 반환해야 합니다.
  * `webassembly_udf_enable_fuel` Bool — 함수에 대한 제한된 연료 예산을 활성화합니다. 기본값: `true`. `false`이면 이 함수에서는 쿼리 수준 설정 `webassembly_udf_max_fuel`이 무시됩니다. `wasmtime` 엔진을 사용할 때 연료 제한을 비활성화하면 성능이 향상될 수 있습니다. 하지만 신뢰할 수 없거나 버그가 있는 게스트 코드의 경우, 제어되지 않는 실행 위험이 커질 수 있습니다.

<div id="abis-versions">
  ## ABI 버전
</div>

ClickHouse와 상호작용하려면 WebAssembly 모듈이 지원되는 ABI(Application Binary Interface) 중 하나를 따라야 합니다.

* `ROW_DIRECT`: 직접 타입 매핑(기본 타입 `Int32`, `UInt32`, `Int64`, `UInt64`, `Float32`, `Float64`만 지원)
* `BUFFERED_V1`: 직렬화를 사용하는 복합 타입
* `ASSEMBLYSCRIPT`: [AssemblyScript](https://www.assemblyscript.org) 모듈과의 행 단위 상호 운용; 숫자 타입과 `String`을 지원

<div id="abi-row_direct">
  ### ABI ROW\_DIRECT
</div>

내보낸 WASM 함수를 각 행에 대해 직접 호출합니다.

* 인수와 반환 타입은 숫자 타입 `Int32/UInt32/Int64/UInt64/Float32/Float64/Int128/UInt128`입니다.
* 이 ABI에서는 문자열을 지원하지 않습니다.
* 시그니처는 WASM 내보내기(`i32/i64/f32/f64/v128`)와 일치해야 합니다.
* 모듈에서 내보내야 하는 지원 함수는 필요하지 않습니다.

예를 들어, 다음과 같은 시그니처를 가진 함수가 있습니다:

```
(func (param i32 i64 f32) (result f64) ...)
```

다음과 같이 만들 수 있습니다:

```sql theme={null}
CREATE FUNCTION my_func ARGUMENTS (Int32, UInt64, Float32) RETURNS Float64 ...
```

WebAssembly는 부호 있는 인수와 부호 없는 인수를 구분하지 않으며, 대신 값을 해석할 때 서로 다른 명령어를 사용합니다. 따라서 인수의 크기는 정확히 일치해야 하고, 부호 여부는 함수 내부에서 수행하는 연산에 따라 결정됩니다.

<div id="abi-buffered_v1">
  ### ABI BUFFERED\_V1
</div>

<Note>
  이 ABI는 실험적 기능이며 향후 릴리스에서 변경될 수 있습니다.
</Note>

WASM 메모리를 통해 (역)직렬화하여 전체 블록을 한 번에 처리합니다. 모든 인수 및 반환 타입을 지원합니다.

데이터는 WASM 메모리의 버퍼를 통해 교환됩니다. 버퍼는 데이터에 대한 포인터와 데이터 크기를 저장하는 8바이트 구조체이며, 리틀 엔디언 `u32` 값 2개로 구성됩니다. 버퍼는 데이터 자체가 아니라 이 구조체를 가리키는 포인터인 handle로 전달됩니다. 게스트 코드는 이러한 버퍼를 생성하고 제거하기 위한 2개의 함수를 내보내야 합니다.

각 입력 블록에 대해 ClickHouse는 다음을 수행합니다.

1. 함수의 `serialization_format`(기본값은 `MsgPack`)을 사용하여 인수 컬럼을 직렬화합니다. 행 기반 포맷은 `ARGUMENTS`에 선언된 순서대로 인수 값을 행별로 작성합니다. 이름이 지정된 필드가 있는 포맷은 인수 이름을 사용하므로, 이러한 포맷을 사용할 때는 `ARGUMENTS`에 인수 이름을 선언하십시오.
2. 모듈에서 내보낸 `clickhouse_create_buffer`를 호출하고, 반환된 버퍼가 가리키는 메모리에 직렬화된 데이터를 복사합니다.
3. 입력 버퍼 handle(함수에 인수가 없으면 `0`)과 행 수라는 2개의 `i32` 인수로 사용자 정의 함수를 호출합니다. 함수는 게스트 코드가 직접 할당한 결과 버퍼의 handle인 단일 `i32`를 반환합니다. `0`을 반환하면 쿼리가 오류와 함께 실패합니다.
4. 결과 버퍼를 읽습니다. 이 버퍼에는 동일한 포맷으로 직렬화된, 정확히 같은 수의 행을 포함하는 컬럼이 정확히 하나 있어야 합니다. `JSONEachRow`처럼 이름이 지정된 필드가 있는 포맷에서는 결과 컬럼의 이름이 `result`여야 합니다.
5. 입력 버퍼(있는 경우)와 결과 버퍼 handle 모두에 대해 `clickhouse_destroy_buffer`를 호출합니다. 게스트 코드는 결과 버퍼를 직접 해제해서는 안 되며, 입력 handle을 결과로 반환해서도 안 됩니다. 그렇게 하면 버퍼가 두 번 제거됩니다.

함수는 입력 블록마다 한 번 호출됩니다. 대규모 쿼리는 쿼리 파이프라인에 의해 여러 블록으로 분할되므로(`webassembly_udf_max_input_block_size` 설정으로 호출당 최대 행 수를 추가로 제한할 수 있음), 각 호출에서 전달되는 행 수는 전체 쿼리가 아니라 해당 블록의 크기입니다.

<Note>
  버퍼 내부의 정확한 바이트는 선택한 포맷에 따라 결정되며, 게스트 코드는 이를 직접 디코딩하고 인코딩해야 합니다. 특히 `String` 값의 경우:

  * `MsgPack`: ClickHouse는 `String` 값을 `str` 계열이 아닌 `bin` 타입 계열(`0xc4`/`0xc5`/`0xc6`)로 작성합니다. 결과를 파싱할 때는 두 계열 모두 허용됩니다.
  * `RowBinary`: 각 `String` 앞에는 고정 폭 정수가 아닌 부호 없는 varint([LEB128](https://en.wikipedia.org/wiki/LEB128)) 형식의 바이트 길이가 접두사로 추가됩니다.
  * `JSONEachRow`: 각 결과 행은 예를 들어 `{"result":"value"}`와 같이 `result` 키를 가진 객체여야 합니다.
</Note>

```
(module
  ;; Allocate a new buffer of specified size
  ;; Returns: handle to Buffer structure (not direct data pointer!) with pointer to data and size
  (func (export "clickhouse_create_buffer")
    (param $size i32)    ;; Size of data to allocate
    (result i32))        ;; Returns buffer handle with enough space

  ;; Free a buffer by its handle
  (func (export "clickhouse_destroy_buffer")
    (param $handle i32)  ;; Buffer handle to free
    (result))            ;; No return value

    ;; User-defined function
    (func (export "user_defined_function1")
      (param $input_buffer_handle i32)  ;; Input buffer handle
      (param $n i32)                    ;; Number of rows in input
      (result i32))                     ;; Returns output buffer handle
)
```

freestanding C로 작성한 완전한 예시입니다. `str_reverse`는 `serialization_format = 'RowBinary'`를 사용해 각 입력 문자열의 바이트 순서를 뒤집습니다. 모듈 인스턴스는 여러 블록에서 재사용되므로 `clickhouse_destroy_buffer`가 실제로 메모리를 회수해야 합니다. 여기서는 모든 버퍼가 소멸되면 할당자가 재설정됩니다.

```c theme={null}
#include <stdint.h>

typedef struct {
    uint8_t * data;
    uint32_t size;
} ClickHouseBuffer;

#define HEAP_SIZE (1 << 24)
static _Alignas(16) uint8_t heap[HEAP_SIZE];
static uint32_t heap_pos = 0;
static uint32_t live_buffers = 0;

__attribute__((export_name("clickhouse_create_buffer")))
ClickHouseBuffer * clickhouse_create_buffer(uint32_t size)
{
    uint32_t total = (sizeof(ClickHouseBuffer) + size + 15u) & ~15u;
    if (heap_pos + total > HEAP_SIZE)
        return 0; /* a zero handle makes the host fail the query with an error */
    ClickHouseBuffer * buf = (ClickHouseBuffer *) &heap[heap_pos];
    buf->data = &heap[heap_pos + sizeof(ClickHouseBuffer)];
    buf->size = size;
    heap_pos += total;
    ++live_buffers;
    return buf;
}

__attribute__((export_name("clickhouse_destroy_buffer")))
void clickhouse_destroy_buffer(ClickHouseBuffer * buf)
{
    if (--live_buffers == 0)
        heap_pos = 0;
}

/* RowBinary prefixes each String with its byte length as an unsigned varint (LEB128) */
static uint64_t read_varint(const uint8_t ** pp)
{
    uint64_t value = 0;
    for (int shift = 0;; shift += 7)
    {
        uint8_t b = *(*pp)++;
        value |= (uint64_t)(b & 0x7f) << shift;
        if (!(b & 0x80))
            return value;
    }
}

static void write_varint(uint8_t ** pp, uint64_t value)
{
    while (value >= 0x80)
    {
        *(*pp)++ = (uint8_t)(value | 0x80);
        value >>= 7;
    }
    *(*pp)++ = (uint8_t)value;
}

/* Reverses the bytes of each input string */
__attribute__((export_name("str_reverse")))
ClickHouseBuffer * str_reverse(ClickHouseBuffer * input, uint32_t num_rows)
{
    ClickHouseBuffer * out = clickhouse_create_buffer(input->size);
    if (!out)
        return 0;
    const uint8_t * in = input->data;
    uint8_t * o = out->data;
    for (uint32_t row = 0; row < num_rows; ++row)
    {
        uint64_t len = read_varint(&in);
        write_varint(&o, len);
        for (uint64_t i = 0; i < len; ++i)
            o[i] = in[len - 1 - i];
        in += len;
        o += len;
    }
    out->size = (uint32_t)(o - out->data);
    return out;
}
```

clang 및 `wasm-ld`(LLVM/lld에 포함됨)를 사용하여 빌드합니다:

```bash theme={null}
clang --target=wasm32 -ffreestanding -nostdlib -fno-builtin -c str_reverse.c
wasm-ld --no-entry str_reverse.o -o str_reverse.wasm
```

함수는 모듈에서 내보내야 합니다. 위와 같이 `__attribute__((export_name("...")))`로 표시하거나 `wasm-ld --export-all`로 링크하십시오. `-fno-builtin`은 clang이 단순한 바이트 루프를 표준 라이브러리 없이는 사용할 수 없는 `memcpy`/`memset` 호출로 변환하는 것을 방지합니다.

모듈을 로드하고 함수를 생성합니다:

```bash theme={null}
cat str_reverse.wasm | clickhouse client -q "INSERT INTO system.webassembly_modules (name, code) SELECT 'str_reverse', code FROM input('code String') FORMAT RawBlob"
```

```sql theme={null}
CREATE FUNCTION str_reverse LANGUAGE WASM ABI BUFFERED_V1
FROM 'str_reverse' :: 'str_reverse'
ARGUMENTS (s String) RETURNS String
SETTINGS serialization_format = 'RowBinary';

SELECT str_reverse(toString(number + 100)) FROM numbers(3);
```

```text theme={null}
001
101
201
```

<div id="abi-assemblyscript">
  ### ABI ASSEMBLYSCRIPT
</div>

[AssemblyScript](https://www.assemblyscript.org) 컴파일러로 생성된 모듈을 대상으로 합니다. 각 행마다 내보낸 함수가 한 번 호출되며, ClickHouse 값은 AssemblyScript 기본 타입과 문자열 객체로 매핑됩니다.

**지원되는 타입**:

* 숫자형: `Int8`/`UInt8`, `Int16`/`UInt16` (경계에서 `i32`로 확장), `Int32`/`UInt32`, `Int64`/`UInt64`, `Float32`, `Float64`

* `String` — AssemblyScript `string`에 매핑됩니다(WASM 메모리에서는 UTF-16). ClickHouse가 UTF-8 ↔ UTF-16 변환을 자동으로 처리합니다.

* 사용자 정의 AssemblyScript 클래스는 인수 또는 반환 타입으로 지원되지 않습니다. 런타임 클래스 id가 컴파일할 때마다 안정적이지 않기 때문입니다([AssemblyScript#2982](https://github.com/AssemblyScript/assemblyscript/issues/2982) 참조).

**모듈 요구 사항**:

모듈은 `__new`, `__pin`, `__unpin`이 내보내지도록 AssemblyScript 관리 런타임으로 컴파일되어야 합니다. 표준 문자열 입출력 처리는 이를 전제로 합니다. 권장 호출 방식은 다음과 같습니다.

```bash theme={null}
asc src.ts --runtime incremental --exportRuntime -o src.wasm
```

AssemblyScript는 런타임 트랩(메모리 부족, 경계 검사 등)을 처리하기 위해 `env.abort`도 가져옵니다. ClickHouse는 이 import를 자동으로 제공합니다. `abort`가 트리거되면 현재 실행 중인 쿼리가 실패하며, 디코딩된 AssemblyScript 메시지와 소스 위치가 포함된 `WASM_ERROR` 예외가 반환됩니다.

**예시**:

```typescript theme={null}
// src.ts
export function add(a: u32, b: u32): u32 {
  return a + b;
}

export function greet(name: string): string {
  return "Hello, " + name + "!";
}
```

`asc`로 컴파일하고 생성된 `.wasm`을 `system.webassembly_modules`에 로드한 후, UDF를 다음과 같이 선언합니다:

```sql theme={null}
CREATE FUNCTION as_add
    LANGUAGE WASM ABI ASSEMBLYSCRIPT
    FROM 'as_example' :: 'add'
    ARGUMENTS (a UInt32, b UInt32) RETURNS UInt32;

CREATE FUNCTION as_greet
    LANGUAGE WASM ABI ASSEMBLYSCRIPT
    FROM 'as_example' :: 'greet'
    ARGUMENTS (name String) RETURNS String;
```

<div id="note-for-developing-udfs-in-rust">
  ### Rust로 UDF를 개발할 때 참고할 사항
</div>

Rust 프로그램용으로는 ClickHouse의 WebAssembly UDF 개발을 간소화할 수 있도록 헬퍼 크레이트 [clickhouse-wasm-udf](https://crates.io/crates/clickhouse-wasm-udf)를 제공합니다. 이 크레이트는 메모리 관리 함수를 제공하므로 `clickhouse_create_buffer`와 `clickhouse_destroy_buffer` 함수를 직접 구현할 필요 없이 크레이트를 의존성으로 추가하면 됩니다. 또한 일반적인 Rust 함수를 필요한 ABI 형식으로 감싸는 매크로 `#[clickhouse_wasm_udf]`도 제공합니다.

이 크레이트를 사용하면 다음과 같이 UDF를 작성할 수 있습니다:

```rust theme={null}

use clickhouse_wasm_udf_bindgen::clickhouse_udf;

#[clickhouse_udf]
pub fn some_udf(data: String) -> HashMap<String, String> {
    // 여기에 구현 내용을 작성하세요
}

```

매크로는 버퍼 구조체를 인자로 받아 버퍼 구조체를 반환하는 래퍼 함수를 생성하고, `serde`를 사용해 직렬화/역직렬화를 자동으로 처리합니다.

<div id="host-api-available-to-modules">
  ## 모듈에서 사용할 수 있는 호스트 API
</div>

다음 호스트 함수는 import하여 모듈에서 사용할 수 있습니다:

* `clickhouse_server_version() -> i64` — ClickHouse 서버 버전을 정수로 반환합니다(예: v25.11.1.1은 25011001).
* `clickhouse_throw(ptr: i32, size: i32)` — 지정된 메시지로 오류를 발생시킵니다. 오류 메시지 문자열이 저장된 메모리 위치의 포인터와 문자열 크기를 인수로 받습니다.
* `clickhouse_log(ptr: i32, size: i32)` — 메시지를 ClickHouse 서버 텍스트 로그에 기록합니다.
* `clickhouse_random(ptr: i32, size: i32)` — 메모리를 무작위 바이트로 채웁니다.
* `env.abort(message: i32, fileName: i32, line: i32, column: i32)` — AssemblyScript 호환 모듈을 위해 제공됩니다. 이를 호출하거나(또는 이를 호출하는 AssemblyScript 런타임 트랩이 트리거되면) 디코딩된 메시지와 소스 위치가 포함된 `WASM_ERROR` 예외와 함께 UDF가 종료됩니다. `env.abort`를 import하지 않는 모듈은 영향을 받지 않습니다.

<div id="settings">
  ## 설정
</div>

다음 쿼리 수준 설정은 WebAssembly UDF 실행을 제어합니다:

* `webassembly_udf_max_fuel` — WebAssembly UDF 인스턴스 실행당 연료 한도입니다. 각 WebAssembly 명령어는 일정량의 연료를 소비합니다. 이 값은 런타임에 전달되기 전에 1024배로 스케일되므로, `webassembly_udf_max_fuel = 1`은 약 1024 연료 단위에 해당합니다. 유한한 제한을 두지 않으려면 0으로 설정합니다. 이는 함수별 설정인 `webassembly_udf_enable_fuel`이 true인 함수에만 적용되며, 기본값은 true입니다.

* `webassembly_udf_max_memory` — WebAssembly UDF 인스턴스당 바이트 단위의 메모리 한도입니다.

* `webassembly_udf_max_input_block_size` — 단일 블록에서 WebAssembly UDF에 전달되는 최대 행 수입니다. 모든 행을 한 번에 처리하려면 0으로 설정합니다.

* `webassembly_udf_max_instances` — 함수별로 병렬 실행할 수 있는 WebAssembly UDF 인스턴스의 최대 개수입니다.

예시 사용법:

```sql theme={null}
SET webassembly_udf_max_fuel = 200000;
SELECT my_wasm_udf(column) FROM table;
```

<div id="see-also">
  ## 관련 항목
</div>

* [ClickHouse UDF 개요](/ko/reference/functions/regular-functions/udf)
