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

# Cloud 中的用户自定义函数

> 在 Cloud 中添加自定义的可执行 Python 函数

export const BetaBadge = ({link, galaxyTrack, galaxyEvent}) => {
  if (link) {
    return <a href={link} target="_blank" rel="noopener noreferrer" className="betaBadge" onClick={galaxyTrack && galaxyEvent ? galaxyOnClick(galaxyEvent) : undefined}>
                <span>Beta</span>
            </a>;
  }
  return <a href="https://clickhouse.com/docs/reference/settings/beta-and-experimental-features#beta-features" className="betaBadge">
            <span>Beta 版功能</span>
        </a>;
};

用户自定义函数 (UDF) 允许用户将 ClickHouse 的功能扩展到其内置的一千多种[函数](/zh/reference/functions/regular-functions/overview)之外。

在 ClickHouse Cloud 中，有多种方式可以创建和管理用户自定义函数：

1. 使用 SQL
2. 使用 UI 和你自己的代码 (Public Beta)
3. 使用 [Cloud API](#manage-udfs-with-the-cloud-api) (Beta)
4. 使用 [Terraform](#manage-udfs-with-terraform) (alpha)

<div id="sql-udfs">
  ## SQL 用户自定义函数
</div>

可以使用 [`CREATE FUNCTION`](/zh/reference/statements/create/function) 语句，基于 lambda 表达式创建 SQL UDF。

在此示例中，我们将创建一个简单的可执行用户自定义函数 `isBusinessHours`。
该函数会检查某个时间戳是否处于正常营业时间内；如果是则返回 true，否则返回 false。

1. 登录 Cloud Console 并打开 SQL 控制台
2. 编写以下 SQL 查询以创建 `isBusinessHours` 函数：

```sql theme={null}
CREATE FUNCTION isBusinessHours AS (ts) ->
toDayOfWeek(ts) BETWEEN 1 AND 5
AND toHour(ts) BETWEEN 9 AND 17;
```

3. 运行以下内容，测试你刚创建的 UDF：

```sql theme={null}
SELECT isBusinessHours('2026-03-20 10:00:00'::DateTime), isBusinessHours('2026-03-20 23:00:00'::DateTime);
```

你应该会看到如下结果：

```response theme={null}
1   0
```

4. 您可以使用 `DROP FUNCTION` 命令删除刚刚创建的 UDF：

```sql theme={null}
DROP FUNCTION isBusinessHours
```

<Warning>
  **重要**

  ClickHouse Cloud 中的 UDFs **不会继承用户级设置**，而是按默认系统设置执行。
</Warning>

这意味着：

* 会话级设置 (通过 `SET` 语句设置) 不会传递到 UDF 的执行上下文中
* 用户 profile 中的设置不会被 UDFs 继承
* 查询级设置在 UDF 执行期间不生效

<div id="ui-udfs">
  ## 通过 UI 创建的用户自定义函数
</div>

<BetaBadge />

ClickHouse Cloud 提供了可通过 UI 创建用户自定义函数的配置界面。

在本示例中，我们将创建与前文相同的简单可执行用户自定义函数 `isBusinessHours`，用于检查某个时间戳是否落在正常工作时间内。
此前我们是通过 SQL 创建它的，这次则改用 Python 创建，并通过 UI 进行配置。

<Steps>
  <Step title="创建 Python 文件" id="create-python-file">
    在本地新建一个 `main.py` 文件：

    ```python theme={null}
    cat > main.py << 'EOF'
    import sys
    from datetime import datetime

    for line in sys.stdin:
        ts = datetime.fromisoformat(line.strip())
        result = 1 if (0 <= ts.weekday() <= 4 and 9 <= ts.hour <= 17) else 0
        print(result)
        sys.stdout.flush()
    EOF
    ```

    如果你的 Python 脚本导入了第三方包，请在 `requirements.txt` 文件中列出这些依赖项，ClickHouse Cloud 会为你安装。你也可以选择直接将依赖项打包到 ZIP 中，但这样就必须同时包含适用于两种 CPU 架构的缓存包，因此使用 `requirements.txt` 更简单。例如：

    ```text theme={null}
    requests>=2.28.0
    numpy>=1.23.0
    ```

    <Note>
      ClickHouse Cloud 会在你下一步通过 UI 上传的 zip 文件中查找 `main.py`。
      如果将该文件命名为其他名称，就会报错。
    </Note>
  </Step>

  <Step title="打包依赖项和本地文件" id="bundle-dependencies">
    要包含依赖包以及其他本地文件 (例如 wheel 文件、配置文件或数据文件) ，请将它们放在与您的 `main.py` 和 `requirements.txt` 相同的目录中。创建 ZIP 归档时，请包含所有文件：

    ```bash theme={null}
    zip is_business_hours.zip main.py requirements.txt
    ```

    你可以在 Python 代码中使用 `os.path.dirname(os.path.abspath(__file__))` 引用本地打包路径的基目录。它会返回 ZIP 归档中 `main.py` 所在目录的绝对路径，以便你访问其他一并打包的文件：

    ```python theme={null}
    import os

    # Get the base directory of the bundled files
    base_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(base_dir, 'config.json')
    ```

    在你需要执行以下操作时，这会很有用：

    * 访问随 UDF 一起打包的配置文件
    * 为自定义依赖加载 wheel 包
    * 引用其他脚本或数据文件

    现在将该文件压缩为 ZIP 压缩包：

    ```bash theme={null}
    zip is_business_hours.zip main.py
    ```

    <Warning>
      **不允许使用符号链接**

      ClickHouse Cloud 会拒绝包含符号链接的 UDF 归档文件。请确保 ZIP 压缩包中只包含普通文件和目录——包含符号链接的上传内容将无法通过验证。
    </Warning>
  </Step>

  <Step title="通过 UI 创建 UDF" id="create-udf-via-ui">
    1. 在 Cloud 控制台主页上，点击左下角菜单中的组织名称。
    2. 从菜单中选择**用户自定义函数**。
    3. 在用户自定义函数页面中，点击**设置 UDF**。屏幕右侧会打开一个配置面板。
    4. 输入函数名称。本示例使用 `isBusinessHours`。
    5. 选择函数类型，可选 **Executable pool** 或 **Executable**：
       * **Executable pool**：系统会维护一个持久进程池，并从池中取出进程来处理读取请求。
       * **Executable**：脚本会在每次查询时运行。
    6. 本示例使用默认设置。有关完整的配置参数列表，请参见[可执行用户自定义函数](/zh/reference/functions/regular-functions/udf#executable-user-defined-functions)。
    7. 点击**浏览文件**，上传在本教程开头创建的 `.zip` 文件。
    8. 添加一个新参数。本示例中，添加一个类型为 `DateTime` 的参数 `timestamp`。
    9. 选择返回类型。本示例中，选择 `Bool`。
    10. 点击**创建 UDF**。此时会显示一个对话框，展示当前构建状态。
        * 如果出现任何问题，状态将变为**错误**。
        * 否则，状态会从**构建中**变为**预配中**。你的服务必须处于唤醒状态才能完成预配。如果服务处于空闲状态，请在服务名称旁的 **UDF 详细信息** 面板中点击**唤醒服务**。
        * 完成后，状态将变为**已部署**。
  </Step>

  <Step title="测试你的 UDF" id="test-your-udf">
    1. 点击页面左上角的 **Settings - 返回到服务视图**，返回 SQL 控制台主页
    2. 点击左侧菜单中的 **SQL 控制台**
    3. 输入以下查询：

    ```sql theme={null}
    SELECT isBusinessHours('2026-03-20 10:00:00'::DateTime), isBusinessHours('2026-03-20 23:00:00'::DateTime);
    ```

    你应该会看到如下结果：

    ```response theme={null}
    true    false
    ```
  </Step>

  <Step title="创建新版本" id="create-new-version">
    如需更改 UDF 的代码，请创建一个新版本。**Edit** 面板仅用于管理 UDF 分配给哪些服务；在此处上传文件不会替换已部署的代码。

    1. 在 Cloud Console 首页，点击左下角菜单中的组织名称。
    2. 在菜单中选择 **用户自定义函数**。
    3. 在 `isBusinessHours` UDF 的 **操作** 一栏中点击三点图标，然后点击 **创建新版本**
    4. 上传包含修改后代码的 ZIP 压缩包，或修改设置，然后点击 **创建新版本**

    你已成功通过 UI 添加了第一个用户自定义函数，确认其可正常运行，并了解了在需要时如何创建新版本。
  </Step>
</Steps>

<div id="manage-udfs-with-the-cloud-api">
  ## 使用 Cloud API 管理 UDF
</div>

<BetaBadge />

UI 中提供的所有功能也可通过 [ClickHouse Cloud API](/zh/products/cloud/features/admin-features/api/api-overview) 以编程方式使用。
UDF 端点可让您通过脚本管理 UDF 的完整生命周期：上传源代码归档文件、创建函数和版本、将其附加到服务，以及进行清理。

<Note>
  这些端点目前处于 Beta 阶段，API 合约可能会变更。
</Note>

通过 API 创建和部署 UDF 的典型工作流如下：

1. [创建上传 URL](/zh/products/cloud/api-reference/udf/udf-upload-session-create) 以获取预签名的 `application/zip` 上传 URL，然后将 ZIP 归档文件上传至该 URL。每个上传 ID 只能用于一次创建 UDF 或创建版本的尝试；重试时请申请新的上传 URL。
2. 从上传的归档文件[创建 UDF](/zh/products/cloud/api-reference/udf/udf-create)，并指定函数名称、运行时、参数和返回类型。
3. [将 UDF 附加到服务](/zh/products/cloud/api-reference/udf/udf-attach)。如果省略版本，则会附加最新的就绪版本。服务必须处于运行状态；可先唤醒空闲服务。

完整端点列表如下：

| 端点                                                                              | 描述                                  |
| ------------------------------------------------------------------------------- | ----------------------------------- |
| [创建 UDF 上传 URL](/zh/products/cloud/api-reference/udf/udf-upload-session-create) | 创建组织范围的预签名 `application/zip` 上传 URL |
| [创建 UDF](/zh/products/cloud/api-reference/udf/udf-create)                       | 从已上传的归档文件创建新的 UDF                   |
| [列出 UDF](/zh/products/cloud/api-reference/udf/udf-list)                         | 返回组织中每个 UDF 的最新版本                   |
| [获取 UDF](/zh/products/cloud/api-reference/udf/udf-get)                          | 返回某个 UDF 的最新版本                      |
| [删除 UDF](/zh/products/cloud/api-reference/udf/udf-delete)                       | 删除 UDF 的所有版本，并将其从所有服务中分离            |
| [创建 UDF 版本](/zh/products/cloud/api-reference/udf/udf-version-create)            | 使用源代码归档文件创建版本，并启动 UDF 构建            |
| [列出 UDF 版本](/zh/products/cloud/api-reference/udf/udf-version-list)              | 返回 UDF 的所有版本                        |
| [删除 UDF 版本](/zh/products/cloud/api-reference/udf/udf-version-delete)            | 删除未附加到任何服务的 UDF 版本                  |
| [将 UDF 附加到服务](/zh/products/cloud/api-reference/udf/udf-attach)                  | 将一个 UDF 版本附加到服务，必要时替换当前版本           |
| [列出 UDF 附加关系](/zh/products/cloud/api-reference/udf/udf-attachment-list)         | 返回 UDF 当前附加到各服务的情况                  |
| [获取 UDF 附加关系](/zh/products/cloud/api-reference/udf/udf-attachment-get)          | 返回 UDF 当前附加到某个服务的情况                 |
| [从服务中分离 UDF](/zh/products/cloud/api-reference/udf/udf-detach)                   | 将 UDF 从服务中分离                        |

有关请求和响应 schema，请参阅 [UDF API 参考文档](/zh/products/cloud/api-reference/udf/udf-create)。

<div id="manage-udfs-with-terraform">
  ## 使用 Terraform 管理 UDF
</div>

官方 [ClickHouse Terraform provider](https://registry.terraform.io/providers/ClickHouse/clickhouse/latest/docs) 提供了两个资源，可用于以基础设施即代码的方式管理 UDF：

* [`clickhouse_udf`](https://github.com/ClickHouse/terraform-provider-clickhouse/blob/main/docs/resources/udf.md) 用于管理函数本身。它接收包含函数源代码的 ZIP 归档文件，并在归档文件的哈希发生变化时发布新版本，同时等待构建完成。
* [`clickhouse_udf_attachment`](https://github.com/ClickHouse/terraform-provider-clickhouse/blob/main/docs/resources/udf_attachment.md) 用于将 UDF 版本关联到服务。一个服务在同一时间最多只能使用一个函数版本。您可以固定指定版本号，也可以引用 `clickhouse_udf.<name>.version`，以自动将服务升级到最新版本。

<Note>
  这些资源适用于 provider 3.24.0 及更高版本。它们目前处于 alpha 阶段，行为可能会在未来的 provider 版本中发生变化。
</Note>

例如，要使用 Terraform 部署前文示例中的 `isBusinessHours` UDF：

```terraform theme={null}
resource "clickhouse_udf" "is_business_hours" {
  function_name = "isBusinessHours"
  runtime       = "python3.11"
  type          = "executable_pool"
  return_type   = "Bool"

  arguments = [
    { name = "timestamp", type = "DateTime" },
  ]

  source_archive_path = "${path.module}/is_business_hours.zip"
  source_archive_hash = filebase64sha256("${path.module}/is_business_hours.zip")
}

resource "clickhouse_udf_attachment" "production" {
  function_name = clickhouse_udf.is_business_hours.function_name
  service_id    = var.service_id
  version       = clickhouse_udf.is_business_hours.version
}
```

仅当版本处于就绪状态时，附加操作才能成功，且可能需要数分钟；空闲服务会自动唤醒。删除 `clickhouse_udf` 资源会移除该函数的所有版本，并将其从所有服务中分离。
