Создаёт пользовательский HTTP-обработчик, определённый в SQL, без изменения файла конфигурации сервера. Обработчики, определённые в SQL, служат альтернативой обработчикам HTTP-интерфейса, настраиваемым в конфигурации.
Синтаксис
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-запросов, в диагностических сообщениях и для определения порядка обработчиков.
Секции
PROTOCOL— необязательный. Если указано имя протокола, обработчик активен только для указанного компонуемого протокола. В противном случае обработчик активен на всех HTTP-конечных точках: встроенных портахhttp/httpsи всех слушателях компонуемых протоколов HTTP-типа.PROTOCOL ANYявно выбирает это поведение по умолчанию; вALTER HANDLERон снимает ранее установленное ограничение протокола. На протокол, буквально называемыйany, можно сослаться с помощью обратных кавычек:PROTOCOL `any`.URL— обязательный. Может иметь вид точного URL,URL PREFIXилиURL REGEXP. Для точных URL и префиксов неоднозначность проверяется при создании или изменении, и при её наличии генерируется исключение. Для регулярных выражений проверить неоднозначность невозможно. URL сопоставляется без строки запроса после?и идентификатора фрагмента после#.URL PREFIXсопоставляется как базовый путь на границе сегмента пути — с той же семантикой, что и правило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имеют долговременные побочные эффекты, изменяющие сеанс операторы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, остаётся доступным для загрузки и вызова при обычных ограничениях сеанса.
Приоритет
Обработчики, определённые в конфигурации сервера, имеют приоритет над обработчиками, определёнными в SQL. Обработчики, определённые в SQL, сопоставляются в лексикографическом порядке имён.
Параметры
Параметры параметризованных запросов передаются, как и в обработчиках, определённых конфигурацией, из следующих источников:
- 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 и currentRequestURL можно использовать для настройки поведения запроса в зависимости от вызванного обработчика и URL запроса.
Управление доступом
Для выполнения CREATE HANDLER, DROP HANDLER и ALTER HANDLER требуются привилегии CREATE HANDLER, DROP HANDLER и ALTER HANDLER соответственно.
Для чтения таблицы system.handlers требуется привилегия SHOW HANDLERS. Секреты, которые могут быть встроены в запрос обработчика, маскируются, если пользователю не разрешено просматривать секреты (см. system.handlers).
Для вызова обработчика отдельная привилегия не требуется, однако при выполнении запроса привилегии проверяются обычным образом, как и выполняется аутентификация. Чтобы ограничить доступ к определённым запросам, создайте VIEW с SQL SECURITY DEFINER и определите обработчик, выполняющий выборку из этого представления.
Хранилище
Обработчики сохраняются в локальном хранилище или хранилище Keeper, аналогично именованным коллекциям. Хранилище настраивается в разделе query_rules_storage файла конфигурации:
<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.
ALTER HANDLER
ALTER HANDLER name
[PROTOCOL protocol_name|ANY]
[URL [PREFIX|REGEXP] '/path']
[METHODS (GET, POST)]
[TYPE query]
[AS SELECT ...]Заменяет обработчик новым. Запрос ALTER может включать только часть секций; например, его можно использовать для изменения только URL или запроса. Неуказанные секции сохраняют прежние значения. PROTOCOL ANY снимает существующее ограничение протокола, снова активируя обработчик на всех конечных точках HTTP.
DROP HANDLER
DROP HANDLER [IF EXISTS] nameУдаляет обработчик с указанным именем.
Интроспекция
В таблице system.handlers перечислены все обработчики, определённые в SQL. В таблице system.query_log в столбцах http_handler_name и http_request_url записываются имя обработчика и путь HTTP-запроса (без строки запроса) для каждого запроса.
Пример
CREATE HANDLER my_handler URL '/my_handler' AS SELECT version();$ curl 'http://localhost:8123/my_handler'Параметризованный обработчик с URL на основе регулярного выражения:
CREATE HANDLER get_user URL REGEXP '/users/(?P<id>\d+)' AS SELECT * FROM users WHERE id = {id:UInt64};$ curl 'http://localhost:8123/users/42'CREATE HANDLER относится к семейству операторов CREATE и связан с ALTER и DROP.