Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

ユーザー定義関数 (UDFs)

ClickHouse は、いくつかの種類のユーザー定義関数 (UDFs) をサポートしています。

  • 実行可能 UDF は、外部プログラムやスクリプト (Python、Bash など) を起動し、STDIN / STDOUT を介してデータのブロックをストリーミングします。ClickHouse を再コンパイルすることなく、既存のコードやツールを統合するために使用できます。インプロセスの選択肢と比べると呼び出しごとのオーバーヘッドは大きく、より重いロジックや別のランタイムが必要な場合に適しています。
  • SQL UDFs は、CREATE FUNCTION を使って SQL のみで定義されます。これらはクエリプランにインライン展開されるため (プロセス境界はありません) 、軽量で、式ロジックの再利用や複雑な計算カラムの簡略化に適しています。
  • Experimental WebAssembly UDFs は、WebAssembly にコンパイルされたコードを、サーバープロセス内のサンドボックスで実行します。外部の実行可能ファイルよりも呼び出しごとのオーバーヘッドが低く、ネイティブ拡張機能よりも優れた分離性を備えているため、WASM をターゲットにできる言語 (例: C/C++/Rust) で記述されたカスタムアルゴリズムに適しています。
  • Experimental ドライバーベースの実行可能 UDF では、オペレーターが提供する "ドライバー" により、CREATE FUNCTION ... ENGINE = DriverName(...) AS '...' で指定されたコードスニペットを、関数作成時に実行可能 UDF に変換できます (たとえばコンパイルして)。これは実行可能 UDF をベースとしており、サーバー側のドライバー設定が必要です。

実行可能ユーザー定義関数

ベータ機能

ClickHouse は、データ処理のために任意の外部実行可能プログラムまたはスクリプトを呼び出すことができます。

実行可能ユーザー定義関数の設定は、1 つ以上の XML ファイルに配置できます。 設定へのパスは、user_defined_executable_functions_config パラメータで指定します。

関数の設定には、次の項目が含まれます:

Parameter Description Required Default Value
name 関数名 はい -
command 実行するスクリプト名、または execute_direct が false の場合は実行コマンド はい -
argument 引数の type と、必要に応じて引数の name を指定します。各引数は個別の設定として記述します。NativeJSONEachRow など、ユーザー定義関数のフォーマットで引数名がシリアライゼーションの一部となる場合は、name の指定が必要です はい c + argument_number
format 引数をコマンドに渡す際の フォーマット。コマンドの出力でも同じフォーマットを使用する必要があります はい -
return_type 戻り値の型 はい -
return_name 戻り値の名前。NativeJSONEachRow など、ユーザー定義関数のフォーマットで戻り値の名前がシリアライゼーションの一部となる場合は、return_name の指定が必要です 任意 result
type 実行可能の種類。typeexecutable に設定されている場合は単一のコマンドが起動され、executable_pool に設定されている場合はコマンドのプールが作成されます はい -
max_command_execution_time データの block を処理する際の最大実行時間 (秒) 。この設定は executable_pool コマンドでのみ有効です 任意 10
command_termination_timeout パイプが閉じられたあと、コマンドの終了を待機する時間 (秒) 。この時間を過ぎると、コマンドを実行しているプロセスに SIGTERM が送信されます 任意 10
command_read_timeout コマンドの stdout からデータを読み取る際のタイムアウト (ミリ秒) 任意 10000
command_write_timeout コマンドの stdin にデータを書き込む際のタイムアウト (ミリ秒) 任意 10000
pool_size コマンドプールのサイズ 任意 16
send_chunk_header process にデータの chunk を送信する前に、行数を送信するかどうかを制御します 任意 false
execute_direct execute_direct = 1 の場合、commanduser_scripts_path で指定された user_scripts フォルダー内から検索されます。追加のスクリプト引数は空白区切りで指定できます。例: script_name arg1 arg2execute_direct = 0 の場合、commandbin/sh -c の引数として渡されます 任意 1
lifetime 関数の再読み込み間隔 (秒) 。0 に設定すると、関数は再読み込みされません 任意 0
deterministic 関数が決定論的かどうか (同じ入力に対して常に同じ結果を返すか) 任意 false
stderr_reaction コマンドの stderr 出力の扱い方。値: none (無視) 、log (stderr をすべて即座に記録) 、log_first (終了後に先頭 4 KiB を記録) 、log_last (終了後に末尾 4 KiB を記録) 、throw (stderr に出力があった時点で直ちに例外を送出) 。log_first または log_last をゼロ以外の終了コードとともに使用した場合、stderr の内容が例外メッセージに含まれます 任意 log_last
check_exit_code true の場合、ClickHouse はコマンドの終了コードを確認します。終了コードがゼロ以外の場合は例外が発生します 任意 true

コマンドは STDIN から引数を読み取り、結果を STDOUT に出力する必要があります。また、引数は反復的に処理する必要があります。つまり、1 つの chunk の引数を処理したら、次の chunk を待機しなければなりません。

実行可能ユーザー定義関数

インラインスクリプトからの UDF

XML または YAML の設定で execute_direct0 に明示的に指定し、test_function_sum を手動で作成します。

test_function.xml ファイル (デフォルトのパス設定では /etc/clickhouse-server/test_function.xml) 。

/etc/clickhouse-server/test_function.xmlxml
<functions>
    <function>
        <type>executable</type>
        <name>test_function_sum</name>
        <return_type>UInt64</return_type>
        <argument>
            <type>UInt64</type>
            <name>lhs</name>
        </argument>
        <argument>
            <type>UInt64</type>
            <name>rhs</name>
        </argument>
        <format>TabSeparated</format>
        <command>cd /; clickhouse-local --input-format TabSeparated --output-format TabSeparated --structure 'x UInt64, y UInt64' --query "SELECT x + y FROM table"</command>
        <execute_direct>0</execute_direct>
        <deterministic>true</deterministic>
    </function>
</functions>

Querysql
SELECT test_function_sum(2, 2);
Resulttext
┌─test_function_sum(2, 2)─┐
│                       4 │
└─────────────────────────┘

Python スクリプトからの UDF

この例では、STDIN から値を読み取り、それを文字列として返す UDF を作成します。

XML または YAML の設定を使用して test_function を作成します。

ファイル test_function.xml (デフォルトのパス設定では /etc/clickhouse-server/test_function.xml) 。

/etc/clickhouse-server/test_function.xmlxml
<functions>
    <function>
        <type>executable</type>
        <name>test_function_python</name>
        <return_type>String</return_type>
        <argument>
            <type>UInt64</type>
            <name>value</name>
        </argument>
        <format>TabSeparated</format>
        <command>test_function.py</command>
    </function>
</functions>

user_scripts フォルダー内にスクリプトファイル test_function.py を作成します (デフォルトのパス設定では /var/lib/clickhouse/user_scripts/test_function.py) 。

#!/usr/bin/python3

import sys

if __name__ == '__main__':
    for line in sys.stdin:
        print("Value " + line, end='')
        sys.stdout.flush()
Querysql
SELECT test_function_python(toUInt64(2));
Resulttext
┌─test_function_python(2)─┐
│ Value 2                 │
└─────────────────────────┘

STDIN から 2 つの値を読み取り、その合計を JSON オブジェクトとして返す

名前付き引数と JSONEachRow フォーマットを使用して、XML または YAML の設定で test_function_sum_json を作成します。

ファイル test_function.xml (デフォルトのパス設定では /etc/clickhouse-server/test_function.xml) 。

/etc/clickhouse-server/test_function.xmlxml
<functions>
    <function>
        <type>executable</type>
        <name>test_function_sum_json</name>
        <return_type>UInt64</return_type>
        <return_name>result_name</return_name>
        <argument>
            <type>UInt64</type>
            <name>argument_1</name>
        </argument>
        <argument>
            <type>UInt64</type>
            <name>argument_2</name>
        </argument>
        <format>JSONEachRow</format>
        <command>test_function_sum_json.py</command>
    </function>
</functions>

user_scripts フォルダー内にスクリプトファイル test_function_sum_json.py を作成します (デフォルトのパス設定では /var/lib/clickhouse/user_scripts/test_function_sum_json.py) 。

#!/usr/bin/python3

import sys
import json

if __name__ == '__main__':
    for line in sys.stdin:
        value = json.loads(line)
        first_arg = int(value['argument_1'])
        second_arg = int(value['argument_2'])
        result = {'result_name': first_arg + second_arg}
        print(json.dumps(result), end='\n')
        sys.stdout.flush()
Querysql
SELECT test_function_sum_json(2, 2);
Resulttext
┌─test_function_sum_json(2, 2)─┐
│                            4 │
└──────────────────────────────┘

command 設定でパラメータを使用する

実行可能なユーザー定義関数では、command 設定で構成した定数パラメータを受け取れます (これは executable 型のユーザー定義関数でのみ機能します) 。 また、シェルの引数展開による脆弱性を防ぐため、execute_direct オプションも必要です。

ファイル test_function_parameter_python.xml (デフォルトのパス設定では /etc/clickhouse-server/test_function_parameter_python.xml) 。

/etc/clickhouse-server/test_function_parameter_python.xmlxml
<functions>
    <function>
        <type>executable</type>
        <execute_direct>true</execute_direct>
        <name>test_function_parameter_python</name>
        <return_type>String</return_type>
        <argument>
            <type>UInt64</type>
        </argument>
        <format>TabSeparated</format>
        <command>test_function_parameter_python.py {test_parameter:UInt64}</command>
    </function>
</functions>

user_scripts フォルダ内にスクリプトファイル test_function_parameter_python.py を作成します (デフォルトのパス設定では /var/lib/clickhouse/user_scripts/test_function_parameter_python.py) 。

#!/usr/bin/python3

import sys

if __name__ == "__main__":
    for line in sys.stdin:
        print("Parameter " + str(sys.argv[1]) + " value " + str(line), end="")
        sys.stdout.flush()
Querysql
SELECT test_function_parameter_python(1)(2);
Resulttext
┌─test_function_parameter_python(1)(2)─┐
│ Parameter 1 value 2                  │
└──────────────────────────────────────┘

シェルスクリプトによるUDF

この例では、各値を 2 倍するシェルスクリプトを作成します。

ファイル test_function_shell.xml (デフォルトのパス設定の場合は /etc/clickhouse-server/test_function_shell.xml) 。

/etc/clickhouse-server/test_function_shell.xmlxml
<functions>
    <function>
        <type>executable</type>
        <name>test_shell</name>
        <return_type>String</return_type>
        <argument>
            <type>UInt8</type>
            <name>value</name>
        </argument>
        <format>TabSeparated</format>
        <command>test_shell.sh</command>
    </function>
</functions>

user_scripts フォルダー内に、スクリプトファイル test_shell.sh を作成します (デフォルトのパス設定の場合は /var/lib/clickhouse/user_scripts/test_shell.sh) 。

/var/lib/clickhouse/user_scripts/test_shell.shbash
#!/bin/bash

while read read_data;
    do printf "$(expr $read_data \* 2)\n";
done
Querysql
SELECT test_shell(number) FROM numbers(10);
Resulttext
    ┌─test_shell(number)─┐
 1. │ 0                  │
 2. │ 2                  │
 3. │ 4                  │
 4. │ 6                  │
 5. │ 8                  │
 6. │ 10                 │
 7. │ 12                 │
 8. │ 14                 │
 9. │ 16                 │
10. │ 18                 │
    └────────────────────┘

エラー処理

一部の関数では、データが無効な場合に例外がスローされることがあります。 この場合、クエリはキャンセルされ、エラーメッセージがクライアントに返されます。 分散処理では、いずれかのサーバーで例外が発生すると、他のサーバーでもクエリの中止が試みられます。

引数式の評価

ほとんどすべてのプログラミング言語では、特定の演算子では引数の1つが評価されないことがあります。 通常は、&&||?: といった演算子です。 ClickHouse では、関数 (演算子) の引数は常に評価されます。 これは、各行を個別に計算するのではなく、カラムのパーツ全体が一度に評価されるためです。

分散クエリ処理における関数の実行

分散クエリ処理では、クエリ処理の各段階のうち、可能な限り多くの段階がリモートサーバー上で実行され、残りの段階 (中間結果のマージとそれ以降のすべて) はリクエスト元のサーバーで実行されます。

つまり、関数が異なるサーバーで実行されることがあります。 たとえば、クエリ SELECT f(sum(g(x))) FROM distributed_table GROUP BY h(y), では、

  • distributed_table に少なくとも2つの分片がある場合、関数 'g' と 'h' はリモートサーバーで実行され、関数 'f' はリクエスト元のサーバーで実行されます。
  • distributed_table に分片が1つしかない場合、'f'、'g'、'h' のすべての関数はこの分片のサーバーで実行されます。

通常、関数の結果は、どのサーバーで実行されるかに依存しません。ただし、これが重要になる場合もあります。 たとえば、Dictionary を扱う関数は、その関数が実行されているサーバー上にある Dictionary を使用します。 別の例として、hostName 関数は、自身が実行されているサーバーの名前を返します。これは、SELECT クエリでサーバーごとに GROUP BY できるようにするためです。

クエリ内の関数がリクエスト元のサーバーで実行されるものの、リモートサーバーで実行する必要がある場合は、その関数を 'any' 集約関数でラップするか、GROUP BY のキーに追加できます。

SQL ユーザー定義関数

ラムダ式からカスタム関数を作成するには、CREATE FUNCTION ステートメントを使用します。これらの関数を削除するには、DROP FUNCTION ステートメントを使用します。

WebAssembly ユーザー定義関数

ClickHouse Cloud ではサポートされていません
実験的な機能

WebAssembly ユーザー定義関数 (WASM UDFs) を使用すると、WebAssembly にコンパイルしたカスタムコードを ClickHouse サーバープロセス内で実行できます。

クイックスタート

ClickHouse の設定で、実験的な WebAssembly サポートを有効にします。

<clickhouse>
    <allow_experimental_webassembly_udf>true</allow_experimental_webassembly_udf>
</clickhouse>

コンパイル済みのWASMモジュールをシステムテーブルに挿入します:

INSERT INTO system.webassembly_modules (name, code)
SELECT 'my_module', base64Decode('AGFzbQEAAAA...');

WASM モジュールを使って関数を作成します:

CREATE FUNCTION my_function
LANGUAGE WASM
ABI ROW_DIRECT
FROM 'my_module'
ARGUMENTS (x UInt32, y UInt32)
RETURNS UInt32;

クエリではこの関数を使用します:

SELECT my_function(10, 20);

詳細情報

詳細については、WebAssembly ユーザー定義関数 のドキュメントを参照してください。

ドライバーベースの実行可能なユーザー定義関数

ClickHouse Cloud ではサポートされていません
実験的な機能

ドライバー は、ユーザーのコードスニペットを実行可能な 実行可能 UDF に変換する、オペレーター提供のアダプターです。関数を ENGINE = DriverName(...) で作成すると、ClickHouse はドライバーの create_command を実行し、関数シグネチャとコードのボディを渡します。ドライバーはボディをコンパイルするか、別の方法で処理し、実行可能 UDF の設定を出力します。その後、ClickHouse はその設定を保存して読み込みます。

これにより管理者は、サーバーの設定ファイルやファイルシステムへのアクセスを与えることなく、任意の言語 (たとえば、サンドボックス化されたコンテナー内でコンパイルされる C) で関数を定義できる、安全かつ限定的な手段をユーザーに提供できます。利用可能なドライバーのセットは、オペレーターが完全に制御します。

ドライバーの有効化

ドライバーベースの実行可能 UDF は、デフォルトで無効になっています。有効にするには、次の手順を実行します。

  1. server configuration で Experimental のゲートを設定します。

    <clickhouse>
        <allow_experimental_executable_udf_drivers>true</allow_experimental_executable_udf_drivers>
    </clickhouse>
  2. user_defined_executable_function_drivers_config に 1 つ以上の ドライバー設定 file を指定します (glob を使用可能) 。必要に応じて、生成された実行可能 UDF の configuration が保存されるディレクトリである dynamic_user_defined_executable_functions_path も設定します。

    <clickhouse>
        <user_defined_executable_function_drivers_config>user_defined_executable_function_drivers_config.d/*_driver.xml</user_defined_executable_function_drivers_config>
        <dynamic_user_defined_executable_functions_path>/var/lib/clickhouse/dynamic_user_defined_executable_functions/</dynamic_user_defined_executable_functions_path>
    </clickhouse>

ドライバー registry は server の起動時に読み込まれ、SYSTEM RELOAD CONFIG で更新されるため、server を再起動せずにドライバーを追加、変更、削除できます。

ドライバー設定

ドライバーは、最上位要素として <driver> を持つ XML (または YAML) ファイルで記述します。サポートされるフィールドは次のとおりです。

Field Description Required
name CREATE FUNCTION ... ENGINE = <name>(...) で使用するドライバー名です。 はい
create_command コードスニペットから UDF を作成する際に呼び出されるプログラムのパスです。相対パスは、ドライバー設定ファイルを基準に解決されます。 はい
drop_command このドライバーに基づく関数が削除されたときに呼び出されるプログラムのパスです。 いいえ
engine_arguments ENGINE = DriverName(...) 内で使用できる引数を宣言します。各子要素は引数名で、<required>true</required> という子要素を追加すると、その引数は必須になります。 いいえ
env ドライバーコマンドの呼び出し時にエクスポートされる環境変数です。 いいえ

ドライバー設定の例:

<clickhouse>
    <driver>
        <name>DockerC</name>
        <create_command>../user_defined_executable_function_drivers/docker_c_create.sh</create_command>
        <drop_command>../user_defined_executable_function_drivers/docker_c_drop.sh</drop_command>
        <engine_arguments>
            <opt_level><required>false</required></opt_level>
        </engine_arguments>
        <env>
            <CLICKHOUSE_C_DRIVER_MEMORY>256m</CLICKHOUSE_C_DRIVER_MEMORY>
            <CLICKHOUSE_C_DRIVER_CPUS>1.0</CLICKHOUSE_C_DRIVER_CPUS>
        </env>
    </driver>
</clickhouse>

ドライバー呼び出し契約

CREATE FUNCTION を実行すると、設定された env 変数がセットされた状態で create_command が呼び出され、次の引数が渡されます。

  • --name <function_name>
  • --return <return_type> (RETURNS 句がある場合)
  • --args <signature> (ARGUMENTS 句がある場合) 。ここで、signature は宣言された引数の一覧で、たとえば x UInt8, y DateTime です
  • ENGINE = DriverName(key = value) で指定された、宣言済みの各 engine 引数について --<key> <value>

ユーザー code のボディ (AS の後のテキスト) は、コマンドの標準入力に送られます。コマンドは、実行可能 UDF の configuration を標準出力に出力する必要があります。フォーマットは自動検出され、< で始まる出力は XML、それ以外は YAML として扱われます。生成された configuration 内で定義される関数名は、作成する関数名と一致している必要があります。create_command がゼロ以外の status で終了した場合、ステートメントは終了コードとドライバーの標準エラーを含む例外とともに失敗します。

drop_command は、存在する場合、関数が削除されるときに同じ方法で呼び出されます (stdin に code のボディは渡されません) 。

function の作成

CREATE [OR REPLACE] FUNCTION [IF NOT EXISTS] name [ON CLUSTER cluster]
    ARGUMENTS (a UInt8, b String) RETURNS UInt64
    ENGINE = DriverName(key1 = 'value1', key2 = 42)
    AS '...code body...'

ClickHouse はドライバーの create_command を実行し、生成された設定を dynamic_user_defined_executable_functions_path に書き込みます。すると、既存の実行可能 UDF ローダーがそれを検出します。その後、その関数は他の関数と同様に呼び出せます。

関数の削除

DROP FUNCTION [IF EXISTS] name [ON CLUSTER cluster]

DROP FUNCTION は、ドライバーの drop_command (存在する場合) を呼び出し、生成された動的設定と関数ごとの作業ディレクトリを削除し、実行可能 UDF ローダーを再読み込みして、永続化されたクエリを削除します。

永続化と再起動

元のクエリは、ユーザー定義 SQL オブジェクトディレクトリに ATTACH FUNCTION ... ステートメントとして永続化されるため、サーバーを再起動しても関数は保持されます。起動時には、dynamic_user_defined_executable_functions_path 内の生成済み構成が、ドライバーを再実行することなく直接読み込まれます。永続化された ATTACH FUNCTION に対応する生成済み構成がない場合 (たとえば動的ディレクトリが失われた場合) 、それを再作成するためにドライバーが再実行されます。

制限事項

  • この機能は実験的で、allow_experimental_executable_udf_drivers を有効にした場合にのみ利用できます。
  • ドライバーベースの関数は、レプリケーション対応のユーザー定義関数ストレージ (ON CLUSTER および <user_defined_zookeeper_path>) ではサポートされていません。レプリケートされるのは生成されたアーティファクトではなく、元のクエリだけであるためです。
  • バックアップされたドライバーベースの関数を RESTORE すると、クエリ自体は保持されますが、ドライバーは再実行されません。生成された設定は、その後の再起動時の復旧によって実体化されます。

例: C ドライバー

ソースツリーには、C の関数本体をコンパイルして実行する概念実証用ドライバーが programs/server/user_defined_executable_function_drivers_config.d/ 配下に含まれています。これらはあくまで例であり、パッケージには含まれず、インストールもされません

  • DockerC - サンドボックス化された Docker コンテナー内でコードをコンパイルして実行し (--network=none --read-only --cap-drop=ALL --security-opt=no-new-privileges に加えて、メモリ/CPU/PID 制限も適用) 、executable_pool UDF を生成します。
  • GVisorC - コンパイル済みバイナリーを gVisorrunsc ランタイム上で実行するバリアントです。
  • UnsafeC - サンドボックスを使用せず、ホスト上でコードを直接コンパイルして実行します。名前のとおり分離は行われないため、信頼できる環境とテスト用途でのみ使用することを想定しています。

これらのサンプルドライバーは出発点として用意されています。信頼できないユーザーに公開する前に、利用環境に合わせてサンドボックス化の内容を確認し、強化してください。

Navigation