> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Python ユーザー定義関数（UDF）

> 型付き引数、NULL の処理、例外制御を備えたネイティブ Python UDF を chDB で作成します。

chDB では、Python 関数を SQL から呼び出せる UDF として登録できます。これらはネイティブにインプロセスで実行されるため、サブプロセスの起動やシリアライゼーションのオーバーヘッドは発生しません。関数は型安全で、Python アノテーションに基づく自動型推論をサポートし、NULL と例外の処理を設定できます。

<div id="quick-start">
  ## クイックスタート
</div>

```python theme={null}
from chdb import query, func
from chdb.sqltypes import INT64

@func([INT64, INT64], INT64)
def add(a, b):
    return a + b

result = query("SELECT add(2, 3)")
print(result)  # 5
```

<Note>
  このガイドの例では、デフォルトのCSV出力フォーマットで `query()` を実行します。インラインコメントには論理的な結果の値を示します。生データ出力では `NULL` は `\N` として出力され、文字列および日付の値にはCSVの引用符付けが適用されます (例: `"Hello, world!"`) 。
</Note>

<div id="registration-methods">
  ## 登録方法
</div>

<div id="func-decorator">
  ### `@func` デコレータ
</div>

UDF を登録する最も簡単な方法です。関数の `__name__` が SQL 関数名として使用されます。

```python theme={null}
from chdb import func
from chdb.sqltypes import INT64, STRING

# Explicit types
@func([INT64, INT64], INT64)
def add(a, b):
    return a + b

# Types inferred from annotations
@func()
def multiply(a: int, b: int) -> int:
    return a * b

# Explicit return_type, arg_types inferred from annotations
@func(return_type=STRING)
def greet(name: str):
    return f"Hello, {name}!"
```

デコレートされた関数は、通常の Python 関数と同様に引き続き呼び出せます。

```python theme={null}
add(2, 3)       # 5 (Python call)
query("SELECT add(2, 3)")  # 5 (SQL call)
```

<div id="create-function">
  ### `create_function`
</div>

任意の呼び出し可能なオブジェクト (lambda、関数、method) を明示的な名前で登録します。

```python theme={null}
from chdb import create_function, query
from chdb.sqltypes import INT64, STRING

create_function("strlen", len, arg_types=[STRING], return_type=INT64)
query("SELECT strlen('hello')")  # 5

create_function("double", lambda x: x * 2, arg_types=[INT64], return_type=INT64)
query("SELECT double(21)")  # 42
```

<div id="drop-function">
  ### `drop_function`
</div>

登録済みの UDF を削除します。登録されていない名前を削除しようとしても何も起こらないため、無条件で安全に呼び出せます。

```python theme={null}
from chdb import drop_function

drop_function("strlen")
# query("SELECT strlen('hello')")  # Error: function not found
```

<Note>
  すでに登録されている名前を登録しようとするとエラーになります。UDFは自動的に置き換えられません。たとえばノートブックのセルを再実行する場合など、関数を再登録するには、まず`drop_function(name)`を呼び出してください。
</Note>

<div id="type-system">
  ## 型システム
</div>

<div id="available-types">
  ### 利用可能な型
</div>

すべての型は `chdb.sqltypes` からインポートできます。

```python theme={null}
from chdb.sqltypes import (
    # Boolean
    BOOL,
    # Signed integers
    INT8, INT16, INT32, INT64, INT128, INT256,
    # Unsigned integers
    UINT8, UINT16, UINT32, UINT64, UINT128, UINT256,
    # Floating point
    FLOAT32, FLOAT64,
    # String
    STRING,
    # Date and time
    DATE, DATE32, DATETIME, DATETIME64,
)
```

<div id="specifying-types">
  ### 型の指定
</div>

型は次の 4 つの方法で指定できます。

| 方法              | 例                                      | 説明                                                     |
| --------------- | -------------------------------------- | ------------------------------------------------------ |
| `ChdbType` 定数   | `INT64`, `STRING`                      | `chdb.sqltypes` からインポート                                |
| ClickHouse 型文字列 | `"Int64"`, `"String"`                  | 標準の ClickHouse 型名                                      |
| パラメーター付き文字列     | `"DateTime('UTC')"`, `"DateTime64(6)"` | パラメーターを持つ型に使用                                          |
| Python 型        | `int`, `str`, `float`                  | `arg_types`/`return_type` に直接渡すか、関数シグネチャの型アノテーションとして使用 |

```python theme={null}
from chdb import create_function, func
from chdb.sqltypes import INT64

# All equivalent:
create_function("f1", lambda x: x * 2, arg_types=[INT64], return_type=INT64)
create_function("f2", lambda x: x * 2, arg_types=["Int64"], return_type="Int64")
create_function("f3", lambda x: x * 2, arg_types=[int], return_type=int)

@func()
def f4(x: int) -> int:
    return x * 2
```

<div id="automatic-type-inference">
  ### 自動型推論
</div>

`arg_types` または `return_type` を省略すると、chDB は Python の型アノテーションから型を推論します。

| Python 型            | ClickHouse 型    |
| ------------------- | --------------- |
| `bool`              | `Bool`          |
| `int`               | `Int64`         |
| `float`             | `Float64`       |
| `str`               | `String`        |
| `bytes`             | `String`        |
| `bytearray`         | `String`        |
| `datetime.date`     | `Date`          |
| `datetime.datetime` | `DateTime64(6)` |

```python theme={null}
@func()
def process(name: str, age: int) -> str:
    return f"{name} is {age} years old"

# Equivalent to:
# @func([STRING, INT64], STRING)
```

<Note>
  `arg_types` を明示的に指定する場合は、**すべての**パラメータを指定する必要があります。一部を明示的に指定し、残りを推論させることはサポートされていません。これは `create_function` と `@func` デコレータの両方に適用されます。すべてのパラメータに型を指定するか、型指定をすべて省略して chDB にアノテーションから推論させてください。
</Note>

戻り値の型は常に必要です。`return_type` を省略し、関数に戻り値のアノテーションもない場合、登録に失敗します。一方、引数の型は任意です。明示的な型指定もアノテーションもないパラメータは、サポートされている任意の入力型を動的に受け入れます。

<div id="null-handling">
  ## NULL の処理
</div>

`on_null` パラメータは、入力引数のいずれかが NULL の場合の動作を制御します。

| 値                | 動作                                         |
| ---------------- | ------------------------------------------ |
| `"skip"` (デフォルト) | 関数を呼び出さず、直ちに NULL を返します                    |
| `"pass"`         | NULL を Python の `None` に変換し、通常どおり関数を呼び出します |

enum も使用できます: `chdb.NullHandling.SKIP` / `chdb.NullHandling.PASS`。

<div id="null-skip">
  ### 例: default (スキップ)
</div>

```python theme={null}
@func(return_type="Int64")
def increment(x: int) -> int:
    return x + 1

query("SELECT increment(NULL)")  # NULL
query("SELECT increment(5)")     # 6
```

<div id="null-pass">
  ### 例: NULL を `None` として渡す
</div>

```python theme={null}
@func(return_type="Int64", on_null="pass")
def null_to_zero(x):
    return 0 if x is None else x + 1

query("SELECT null_to_zero(NULL)")  # 0
query("SELECT null_to_zero(5)")     # 6
```

<div id="null-multiple-args">
  ### 例: 複数の引数
</div>

```python theme={null}
@func(arg_types=["Int64", "Int64"], return_type="Int64", on_null="pass")
def add_or_zero(a, b):
    return (a or 0) + (b or 0)

query("SELECT add_or_zero(NULL, 5)")    # 5
query("SELECT add_or_zero(NULL, NULL)") # 0
query("SELECT add_or_zero(3, 7)")       # 10
```

<div id="exception-handling">
  ## 例外処理
</div>

`on_error` パラメータは、Python 関数で例外が発生した場合の動作を制御します。

| 値                     | 動作                      |
| --------------------- | ----------------------- |
| `"propagate"` (デフォルト) | 例外を SQL エラーとして送出        |
| `"ignore"`            | 例外を捕捉し、その行に対して NULL を返す |

enum も使用できます: `chdb.ExceptionHandling.PROPAGATE` / `chdb.ExceptionHandling.IGNORE`。

<div id="exception-propagate">
  ### 例: default (伝播)
</div>

```python theme={null}
@func(arg_types=["Int64", "Int64"], return_type="Int64")
def divide(a, b):
    return a // b

query("SELECT divide(10, 2)")  # 5
query("SELECT divide(1, 0)")   # Error: ZeroDivisionError
```

<div id="exception-ignore">
  ### 例: エラーを無視する
</div>

```python theme={null}
@func(arg_types=["Int64", "Int64"], return_type="Int64", on_error="ignore")
def safe_divide(a, b):
    return a // b

query("SELECT safe_divide(10, 2)")  # 5
query("SELECT safe_divide(1, 0)")   # NULL
```

<div id="combining-null-and-exception">
  ## NULL と例外処理の組み合わせ
</div>

`on_null` オプションと `on_error` オプションは組み合わせて使用できます。

| on\_null | on\_error     | NULL 入力          | 例外        |
| -------- | ------------- | ---------------- | --------- |
| `"skip"` | `"propagate"` | NULL を返す         | エラーを発生させる |
| `"skip"` | `"ignore"`    | NULL を返す         | NULL を返す  |
| `"pass"` | `"propagate"` | `None` を指定して呼び出す | エラーを発生させる |
| `"pass"` | `"ignore"`    | `None` を指定して呼び出す | NULL を返す  |

```python theme={null}
@func(
    arg_types=["Int64", "Int64"],
    return_type="Int64",
    on_null="pass",
    on_error="ignore",
)
def robust_divide(a, b):
    if a is None or b is None:
        return -1
    return a // b

query("SELECT robust_divide(10, 2)")     # 5
query("SELECT robust_divide(NULL, 2)")   # -1
query("SELECT robust_divide(1, 0)")      # NULL (exception caught)
```

<div id="datetime-and-timezone">
  ## DateTime と タイムゾーン のサポート
</div>

UDF は、タイムゾーン を認識する日付と時刻の型を完全にサポートしています。

<div id="date-types">
  ### Date 型
</div>

```python theme={null}
from datetime import date, timedelta

@func()
def next_day(d: date) -> date:
    return d + timedelta(days=1)

@func()
def get_year(d: date) -> int:
    return d.year

query("SELECT next_day(toDate('2024-06-15'))")  # 2024-06-16
query("SELECT get_year(toDate('2024-06-15'))")  # 2024
```

<div id="datetime-with-timezones">
  ### タイムゾーン付きDateTime
</div>

```python theme={null}
from datetime import timedelta

@func(arg_types=["DateTime('UTC')"], return_type="DateTime('UTC')")
def add_one_hour(dt):
    return dt + timedelta(hours=1)

query("SELECT add_one_hour(toDateTime('2024-01-01 12:00:00', 'UTC'))")  # 2024-01-01 13:00:00
```

<div id="datetime64">
  ### DateTime64 (高精度)
</div>

`DATETIME64` のデフォルトのスケールは6 (マイクロ秒) です。

```python theme={null}
from datetime import timedelta

@func(arg_types=["DateTime64(6, 'UTC')"], return_type="DateTime64(6, 'UTC')")
def add_microsecond(dt):
    return dt + timedelta(microseconds=1)

query("SELECT add_microsecond(toDateTime64('2024-01-01 12:00:00.000000', 6, 'UTC'))")  # 2024-01-01 12:00:00.000001
```

<Note>
  * 入力の `DateTime`/`DateTime64` 値には ClickHouse のタイムゾーン情報が含まれます
  * 出力される `datetime` オブジェクトにはタイムゾーン情報が保持されます
  * タイムゾーン変換は自動的に行われます
</Note>

<div id="using-udfs-with-sessions">
  ## sessions で UDF を使用する
</div>

UDF はグローバルに登録され、同じプロセス内のすべての sessions で利用できます。

```python theme={null}
from chdb import session as chs, func
from chdb.sqltypes import INT64

@func([INT64], INT64)
def double(x):
    return x * 2

sess = chs.Session()
sess.query("CREATE TABLE t (x Int64) ENGINE = Memory")
sess.query("INSERT INTO t VALUES (1), (2), (3)")
result = sess.query("SELECT double(x) FROM t ORDER BY x", "CSV")
print(result)
# 2
# 4
# 6
```
