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

> CREATE HANDLER 参考文档

# CREATE HANDLER

创建由 SQL 定义的自定义 HTTP 处理程序，无需编辑服务器配置文件。SQL 定义的处理程序可替代基于配置的 [HTTP 接口处理程序](/docs/zh/concepts/features/interfaces/http)。

<div id="syntax">
  ## 语法
</div>

```sql theme={null}
CREATE HANDLER [IF NOT EXISTS] name [ON CLUSTER cluster]
[PROTOCOL protocol_name|ANY]
URL [PREFIX|REGEXP] '/path'
[METHODS (GET, POST)]
[TYPE query]
AS [SELECT|INSERT|...] ...
```

使用指定的 `name` 创建处理程序。该名称用于通过 SQL 查询管理处理程序、生成诊断消息以及确定处理程序的顺序。

<div id="clauses">
  ## 子句
</div>

* `PROTOCOL` — 可选。如果指定了协议名称，处理程序仅对指定的[可组合协议](/docs/zh/concepts/features/configuration/server-config/composable-protocols)生效。否则，处理程序会在所有 HTTP 端点上生效：内置的 `http`/`https` 端口，以及每个 HTTP 类型的[可组合协议](/docs/zh/concepts/features/configuration/server-config/composable-protocols)侦听器。`PROTOCOL ANY` 显式指定后一种默认行为；在 `ALTER HANDLER` 中，它会移除此前设置的协议限制。名称恰为 `any` 的协议可使用反引号引用：``PROTOCOL `any` ``。
* `URL` — 必填。可以是精确 URL、`URL PREFIX` 或 `URL REGEXP`。对于精确 URL 和前缀，会在创建或修改时检查是否存在歧义；如有歧义，则抛出异常。对于 regexp，无法检查歧义。匹配 URL 时会忽略 `?` 查询字符串和 `#` 片段标识符。`URL PREFIX` 会作为基路径在路径段边界处匹配——其语义与[通过配置定义的处理程序](/docs/zh/concepts/features/interfaces/http)中的 `url_prefix` 规则相同：`URL PREFIX '/api/v1'` 匹配 `/api/v1`、`/api/v1/` 和 `/api/v1/write`，但不匹配 `/api/v1beta`。前缀末尾的 `/` 会被忽略，因此 `'/api/v1/'` 和 `'/api/v1'` 的行为相同。
* `METHODS` — 可选。允许的 HTTP 方法列表。默认为仅允许 `GET`。支持的方法包括 `GET`、`POST`、`PUT` 和 `DELETE`。会修改数据的方法 `POST`、`PUT` 和 `DELETE` 可执行修改数据的查询；`GET` 和 `HEAD` 等安全方法始终以 `readonly` 模式执行。因此，执行修改数据查询 (例如 `INSERT` 或 DDL) 的处理程序必须允许至少一种修改方法——若仅使用只读方法 (例如默认的 `GET`) 创建此类处理程序，将抛出异常。副作用可在 `readonly` 模式下保留的查询属于特殊情况：`BACKUP` 和 `RESTORE` 具有持久性副作用；使用 `session_id` 时，修改会话的语句 `SET`、`SET ROLE`、`USE`、`BEGIN TRANSACTION`、`COMMIT`、`ROLLBACK` 和 `SET TRANSACTION SNAPSHOT` 会更改跨请求持续保留的会话或事务状态；`CREATE TEMPORARY TABLE` / `CREATE TEMPORARY VIEW` 会创建仅存在于会话中的对象——但安全方法的 `readonly` 模式不会阻止其中任何一种。`readonly` 模式同样不会阻止对*现有*临时表的修改，因此可能以临时表为目标的查询也按相同方式处理：目标表未使用数据库限定的 `INSERT` (未限定名称可能解析为会话临时表) 、`DROP TEMPORARY TABLE`、对未使用数据库限定的表执行的 `DROP TABLE` / `TRUNCATE TABLE`，以及对未使用数据库限定的表执行的 `ALTER` (`ALTER TEMPORARY TABLE` 是相同的语句) 。使用数据库限定的目标不可能是临时表，因此此类查询不受此规则约束。HTTP 要求安全方法不得产生副作用 (声明为 `GET` 的处理程序也会处理 `HEAD` 请求，此时响应正文会被抑制，副作用将不可见) 。因此，执行此类查询的处理程序必须*仅*列出修改方法——创建或修改该处理程序时如包含安全方法，将抛出异常。会深入检查复合语句：对于 `statement1 PARALLEL WITH statement2 ...` 和 `EXECUTE AS <user> <statement>`，上述规则适用于其包装的语句，因为实际执行的是这些语句 (每个语句都在处理程序上下文的副本中运行，该副本会保留 `readonly` 模式) 。不带语句的 `EXECUTE AS <user>` 会使整个会话以另一用户身份运行，因此它本身会修改会话。此外，任何 `EXECUTE AS` 处理程序——无论是不带语句还是包装语句——都必须允许至少一种修改方法：身份模拟需要 `IMPERSONATE` 权限，而安全方法的 `readonly` 模式会拒绝该权限。
* `TYPE` — 可选。目前唯一支持的类型是 `query`。
* `AS` — 此处理程序将调用的 SQL 查询。该查询可以参数化。在创建或修改处理程序时，会对查询进行语法解析以检查其正确性，但不会进行语义分析——例如，创建处理程序时，查询所引用的表可能尚不存在。`FORMAT` 及类似子句属于查询本身，而不属于整个 `CREATE`/`ALTER` 语句。为消除歧义，可将查询置于括号中。`INSERT` 查询的 `VALUES` 或 `FORMAT` 子句后不得包含内联数据——创建或修改此类处理程序会引发异常，因为内联载荷无法保留在处理程序定义中；数据应通过 HTTP 请求正文提供 (或由 `INSERT ... SELECT` 计算) 。对于处理程序查询会读取请求正文的请求——即从正文获取数据的 `INSERT`，或使用 `_request_body` 参数的查询——必须声明正文长度：不含 `Content-Length` 请求头的非分块请求将收到 `411 Length Required` 响应，因为否则正文会一直读取到 stream 结束，连接中断也会被视为完整请求。此类处理程序的每种方法也必须是可携带请求正文的方法 (`POST`、`PUT` 或 `DELETE`) ——如果在 `METHODS` 子句中使用安全方法创建处理程序 (例如默认的 `GET`) ，将引发异常，因为安全方法绝不会提供请求正文，查询会在不知不觉中读取空正文；声明 `GET` 后也会为 `HEAD` 提供服务，因此混用安全方法和可携带请求正文的方法会使这些调用仍然可达。`INSERT ... SELECT` 不读取请求正文 (其数据来自 `SELECT`) ，因此不受这些要求约束——除非其 `SELECT` 从由请求正文提供数据的 `input` 表函数读取。读取请求正文的 `INSERT` 必须是处理程序自身的查询：`EXECUTE AS` 和 `PARALLEL WITH` 在运行其封装的语句时不会使用请求正文，因此在创建时将此类查询封装在其中会被拒绝，以避免悄然丢弃每次上传的数据。读取请求正文的查询也不得使用 `_request_body` 参数：请求正文只有一个，绑定 `_request_body` 会在查询读取输入数据前将其消耗，因此此类处理程序会在创建时被拒绝，以避免悄然丢失每次上传的数据——请使用查询自身的正文输入或 `_request_body`，不要同时使用两者。不读取请求正文的处理程序没有此类要求；对此类处理程序的请求正文会被忽略，绝不会追加到处理程序的查询中。每次重新加载或调用处理程序时，服务器都会以无限制的解析器深度和回溯次数重新解析存储的查询文本，因此，在提高 `max_parser_depth` / `max_parser_backtracks` 的会话中创建的处理程序，在普通会话限制下仍可加载和调用。

<div id="priority">
  ## 优先级
</div>

服务器配置中定义的处理程序优先于通过 SQL 定义的处理程序。通过 SQL 定义的处理程序会按名称的词典序进行匹配。

<div id="parameters">
  ## 参数
</div>

与配置定义的 处理程序 一样，参数化查询的查询参数可通过以下方式提供：

* 查询字符串中的 HTTP URL 参数，使用 `param_<name>` 命名约定 (例如，`?param_id=42` 绑定 `{id:Type}`) ；
* `URL REGEXP` 中的命名捕获组 (例如，`URL REGEXP '/users/(?P<id>\d+)'` 绑定 `{id:Type}`) ；
* 对于查询中声明了参数的 处理程序，请求正文中的表单字段：在携带正文的 `POST`、`PUT` 和 `DELETE` 方法中，`application/x-www-form-urlencoded` 正文 (例如，`curl -d 'param_id=42'`) 和 `multipart/form-data` 正文中的字段均会像 URL 参数一样绑定 `{name:Type}` 参数。若某个参数同时出现在 URL 和正文中，则以 URL 中的值为准。被解析为表单的正文会由 处理程序 层消费，不会作为 `INSERT` 数据传递给查询。如果 处理程序 对正文的唯一用途是 `_request_body`，则会获得原始正文，而不会进行表单解析；如果 处理程序 除了其他参数外还声明了 `_request_body`，则两者都会获得——在将正文解析为表单前，原始未解析正文的副本会保存在 `_request_body` 中 (受 `http_max_request_param_data_size` 限制) 。

调用 处理程序 时，标准 ClickHouse HTTP 请求头 (如 `X-ClickHouse-Database`、`X-ClickHouse-User`、`X-ClickHouse-Key`) 仍会照常生效。

函数 [`currentHandler`](/docs/zh/reference/functions/regular-functions/other-functions#currentHandler) 和 [`currentRequestURL`](/docs/zh/reference/functions/regular-functions/other-functions#currentRequestURL) 可用于根据调用的 处理程序 和请求 URL 自定义查询行为。

<div id="access-control">
  ## 访问控制
</div>

`CREATE HANDLER`、`DROP HANDLER` 和 `ALTER HANDLER` 分别需要 `CREATE HANDLER`、`DROP HANDLER` 和 `ALTER HANDLER` 授权。

读取 [`system.handlers`](/docs/zh/reference/system-tables/handlers) 表需要 `SHOW HANDLERS` 授权。处理程序查询中可能嵌入的 secret 会在该表中被掩码，除非用户还获准查看 secret (请参阅 [`system.handlers`](/docs/zh/reference/system-tables/handlers)) 。

调用处理程序无需额外授权，但在执行查询时仍会照常检查授权，并按常规进行身份验证。若要封装对特定查询的访问，请创建一个使用 `SQL SECURITY DEFINER` 的 [`VIEW`](/docs/zh/reference/statements/create/view#sql_security)，并定义一个从该视图中查询的处理程序。

<div id="storage">
  ## 存储
</div>

处理程序保存在存储中。与[命名集合](/docs/zh/concepts/features/configuration/server-config/named-collections)类似，存储可以是本地存储或 Keeper 存储，并在配置文件的 `query_rules_storage` 部分中进行配置：

```xml theme={null}
<query_rules_storage>
    <type>local</type> <!-- or zookeeper -->
    <path>/var/lib/clickhouse/handlers/</path>
</query_rules_storage>
```

使用 Keeper 存储时，处理程序 会自动在所有副本之间保持同步，因此显式指定 `ON CLUSTER` 子句不仅多余，还会导致每个副本都尝试创建同一个 处理程序。启用 `ignore_on_cluster_for_replicated_handler_queries` 设置后，当存储已启用复制时，`CREATE`、`ALTER` 和 `DROP HANDLER` 会忽略 `ON CLUSTER`，其行为与 `ignore_on_cluster_for_replicated_named_collections_queries` 一致。

<div id="alter-handler">
  ## ALTER HANDLER
</div>

```sql theme={null}
ALTER HANDLER name
[PROTOCOL protocol_name|ANY]
[URL [PREFIX|REGEXP] '/path']
[METHODS (GET, POST)]
[TYPE query]
[AS SELECT ...]
```

将 处理程序 替换为新的 处理程序。`ALTER` 查询只能包含部分子句；例如，可仅更改 URL 或查询。未指定的子句将保留原有值。`PROTOCOL ANY` 会移除现有的协议限制，使 处理程序 再次在所有 HTTP 端点上生效。

<div id="drop-handler">
  ## DROP HANDLER
</div>

```sql theme={null}
DROP HANDLER [IF EXISTS] name
```

删除指定名称的处理程序。

<div id="introspection">
  ## 内部信息
</div>

[`system.handlers`](/docs/zh/reference/system-tables/handlers) 表列出所有通过 SQL 定义的处理程序。[`system.query_log`](/docs/zh/reference/system-tables/query_log) 表在 `http_handler_name` 和 `http_request_url` 列中记录每个查询对应的处理程序名称及 HTTP 请求路径 (不含查询字符串) 。

<div id="example">
  ## 示例
</div>

```sql theme={null}
CREATE HANDLER my_handler URL '/my_handler' AS SELECT version();
```

```bash theme={null}
$ curl 'http://localhost:8123/my_handler'
```

带有 Regexp URL 的参数化处理程序：

```sql theme={null}
CREATE HANDLER get_user URL REGEXP '/users/(?P<id>\d+)' AS SELECT * FROM users WHERE id = {id:UInt64};
```

```bash theme={null}
$ curl 'http://localhost:8123/users/42'
```

<div id="related-statements">
  ## 相关语句
</div>

`CREATE HANDLER` 是 `CREATE` 语句家族的一部分，与 `ALTER` 和 `DROP` 相关。
