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

Создаёт пользовательский HTTP-обработчик, определённый в SQL, без изменения файла конфигурации сервера. Обработчики, определённые в SQL, служат альтернативой [обработчикам HTTP-интерфейса, настраиваемым в конфигурации](/docs/ru/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/ru/concepts/features/configuration/server-config/composable-protocols). В противном случае обработчик активен на всех HTTP-конечных точках: встроенных портах `http`/`https` и всех слушателях [компонуемых протоколов](/docs/ru/concepts/features/configuration/server-config/composable-protocols) HTTP-типа. `PROTOCOL ANY` явно выбирает это поведение по умолчанию; в `ALTER HANDLER` он снимает ранее установленное ограничение протокола. На протокол, буквально называемый `any`, можно сослаться с помощью обратных кавычек: ``PROTOCOL `any` ``.
* `URL` — обязательный. Может иметь вид точного URL, `URL PREFIX` или `URL REGEXP`. Для точных URL и префиксов неоднозначность проверяется при создании или изменении, и при её наличии генерируется исключение. Для регулярных выражений проверить неоднозначность невозможно. URL сопоставляется без строки запроса после `?` и идентификатора фрагмента после `#`. `URL PREFIX` сопоставляется как базовый путь на границе сегмента пути — с той же семантикой, что и правило `url_prefix` у [обработчиков, заданных конфигурацией](/docs/ru/concepts/features/interfaces/http): `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` имеют долговременные побочные эффекты, изменяющие сеанс операторы `SET`, `SET ROLE`, `USE`, `BEGIN TRANSACTION`, `COMMIT`, `ROLLBACK` и `SET TRANSACTION SNAPSHOT` изменяют состояние сеанса или транзакции, сохраняющееся между запросами при использовании `session_id`, а `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`, поскольку иначе тело считывалось бы до конца потока, а разорванное соединение было бы принято за полный запрос. Каждый метод такого обработчика также должен передавать тело (`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>

Параметры параметризованных запросов передаются, как и в обработчиках, определённых конфигурацией, из следующих источников:

* URL-параметров HTTP в строке запроса с использованием соглашения `param_<name>` (например, `?param_id=42` связывает `{id:Type}`);
* именованных групп захвата в `URL REGEXP` (например, `URL REGEXP '/users/(?P<id>\d+)'` связывает `{id:Type}`);
* полей формы в теле запроса для обработчика, в запросе которого объявлены параметры: тело `application/x-www-form-urlencoded` (например, `curl -d 'param_id=42'`) и поля тела `multipart/form-data` связывают параметры `{name:Type}` так же, как URL-параметры, для методов с телом `POST`, `PUT` и `DELETE`. Если параметр присутствует и в URL, и в теле запроса, используется значение из URL. Тело, разобранное как форма, потребляется на уровне обработчика: оно не передаётся в запрос как данные `INSERT`. Если тело используется обработчиком только через `_request_body`, обработчик получает исходное тело без разбора формы; если обработчик объявляет `_request_body` наряду с другими параметрами, он получает и то и другое: до разбора тела как формы в `_request_body` сохраняется копия исходного неразобранного тела (с учётом `http_max_request_param_data_size`).

Стандартные HTTP-заголовки ClickHouse (например, `X-ClickHouse-Database`, `X-ClickHouse-User`, `X-ClickHouse-Key`) обрабатываются обычным образом при вызове обработчика.

Функции [`currentHandler`](/docs/ru/reference/functions/regular-functions/other-functions#currentHandler) и [`currentRequestURL`](/docs/ru/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/ru/reference/system-tables/handlers) требуется привилегия `SHOW HANDLERS`. Секреты, которые могут быть встроены в запрос обработчика, маскируются, если пользователю не разрешено просматривать секреты (см. [`system.handlers`](/docs/ru/reference/system-tables/handlers)).

Для вызова обработчика отдельная привилегия не требуется, однако при выполнении запроса привилегии проверяются обычным образом, как и выполняется аутентификация. Чтобы ограничить доступ к определённым запросам, создайте [`VIEW` с `SQL SECURITY DEFINER`](/docs/ru/reference/statements/create/view#sql_security) и определите обработчик, выполняющий выборку из этого представления.

<div id="storage">
  ## Хранилище
</div>

Обработчики сохраняются в локальном хранилище или хранилище Keeper, аналогично [именованным коллекциям](/docs/ru/concepts/features/configuration/server-config/named-collections). Хранилище настраивается в разделе `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/ru/reference/system-tables/handlers) перечислены все обработчики, определённые в SQL. В таблице [`system.query_log`](/docs/ru/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'
```

Параметризованный обработчик с 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`.
