Cria um handler HTTP personalizado definido em SQL, sem editar o arquivo de configuração do servidor. Os handlers definidos em SQL são uma alternativa aos handlers de interface HTTP.
Sintaxe
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|...] ...Cria um handler com o name especificado. O nome é usado para gerenciar handlers por meio de consultas SQL, em mensagens de diagnóstico e para ordená-los.
Cláusulas
PROTOCOL— opcional. Se um nome de protocolo for especificado, o handler ficará ativo apenas para o protocolo componível indicado. Caso contrário, ficará ativo em todos os endpoints HTTP: as portas integradashttp/httpse todos os listeners de protocolo componível do tipo HTTP.PROTOCOL ANYseleciona explicitamente esse comportamento padrão; emALTER HANDLER, remove uma restrição de protocolo definida anteriormente. Um protocolo chamado literalmenteanypode ser referenciado com crases:PROTOCOL `any`.URL— obrigatória. Pode ser uma URL exata, umURL PREFIXou umURL REGEXP. Para URLs exatas e prefixos, a ambiguidade é verificada no momento da criação ou alteração, e uma exceção é lançada se houver ambiguidade. Para regexp, a ambiguidade não pode ser verificada. A URL é comparada sem a string de consulta?nem o identificador de fragmento#. UmURL PREFIXé comparado como um caminho base, no limite de um segmento de caminho — com a mesma semântica da regraurl_prefixdos handlers definidos na configuração:URL PREFIX '/api/v1'corresponde a/api/v1,/api/v1/e/api/v1/write, mas não a/api/v1beta. A/final do prefixo é ignorada, portanto'/api/v1/'e'/api/v1'têm o mesmo comportamento.METHODS— opcional. A lista de métodos HTTP permitidos. Por padrão, é apenasGET. Os métodos compatíveis sãoGET,POST,PUTeDELETE. Os métodos que modificam dados,POST,PUTeDELETE, podem executar consultas modificadoras; os métodos seguros, comoGETeHEAD, são sempre executados no modoreadonly. Consequentemente, um handler cuja consulta modifica dados (por exemplo,INSERTou DDL) deve permitir pelo menos um método que modifica dados — criar esse handler apenas com métodos somente leitura (por exemplo, oGETpadrão) lança uma exceção. Consultas cujos efeitos colaterais persistem no modoreadonlysão um caso especial:BACKUPeRESTOREtêm efeitos colaterais duradouros; as instruções que alteram a sessãoSET,SET ROLE,USE,BEGIN TRANSACTION,COMMIT,ROLLBACKeSET TRANSACTION SNAPSHOTalteram o estado da sessão ou da transação, que persiste entre solicitações quandosession_idestá em uso; eCREATE TEMPORARY TABLE/CREATE TEMPORARY VIEWcriam um objeto que existe durante a sessão — ainda assim, o modoreadonlydos métodos seguros não bloqueia nenhum deles. As mutações de uma tabela temporária existente também não são bloqueadas pelo modoreadonly; por isso, consultas que possam ter uma tabela temporária como destino são tratadas da mesma forma: umINSERTcuja tabela de destino não seja qualificada com um banco de dados (um nome não qualificado pode ser resolvido como uma tabela temporária da sessão), umDROP TEMPORARY TABLE, umDROP TABLE/TRUNCATE TABLEde uma tabela não qualificada com um banco de dados e umALTERde uma tabela não qualificada com um banco de dados (ALTER TEMPORARY TABLEé a mesma instrução). Um destino qualificado com banco de dados nunca pode ser uma tabela temporária; portanto, essas consultas não estão sujeitas a essa regra. O HTTP exige que os métodos seguros não tenham efeitos colaterais (um handler declarado paraGETtambém atende aHEAD, no qual o corpo da resposta é suprimido e o efeito seria invisível). Portanto, um handler que execute esse tipo de consulta deve listar apenas métodos que modificam dados — criá-lo ou alterá-lo para incluir um método seguro lança uma exceção. As instruções compostas são analisadas internamente: parastatement1 PARALLEL WITH statement2 ...eEXECUTE AS <user> <statement>, as regras acima se aplicam às instruções encapsuladas, pois são elas que são executadas (cada uma em uma cópia do contexto do handler, que mantém o modoreadonly). UmEXECUTE AS <user>sem instrução faz com que toda a sessão seja executada como outro usuário; portanto, conta como uma alteração de sessão por si só. Além disso, qualquer handlerEXECUTE AS— sem instrução ou encapsulando uma instrução — deve permitir pelo menos um método que modifica dados: a personificação exige o privilégioIMPERSONATE, que o modoreadonlydos métodos seguros nega.TYPE— opcional. Por enquanto, o único tipo compatível équery.AS— a consulta SQL que será invocada por este manipulador. A consulta pode ser parametrizada. Sua correção sintática é verificada durante a criação ou alteração do manipulador, mas ela não é analisada semanticamente — por exemplo, as tabelas às quais a consulta faz referência podem não existir no momento da criação do manipulador. As cláusulasFORMATe semelhantes pertencem à consulta, não à instruçãoCREATE/ALTERcomo um todo. A consulta pode ser colocada entre parênteses para eliminar ambiguidades. Uma consultaINSERTnão pode conter dados inline após a cláusulaVALUESouFORMAT— a criação ou alteração desse manipulador lança uma exceção, pois o payload inline não pode ser preservado na definição do manipulador; espera-se que os dados sejam fornecidos no corpo da requisição HTTP (ou calculados por umINSERT ... SELECT). Uma requisição para um manipulador cuja consulta lê o corpo — umINSERTque recebe seus dados do corpo ou uma consulta que usa o parâmetro_request_body— deve declarar seu tamanho: uma requisição não fragmentada sem o cabeçalhoContent-Lengthrecebe a resposta411 Length Required, pois, caso contrário, o corpo seria lido até o fim do fluxo, e uma conexão interrompida seria aceita como uma requisição completa. Todos os métodos desse manipulador também devem permitir corpo (POST,PUTouDELETE) — criá-lo com um método seguro na cláusulaMETHODS(por exemplo, oGETpadrão) lança uma exceção, pois um método seguro nunca fornece um corpo de requisição e a consulta leria silenciosamente um corpo vazio; umGETdeclarado também atende aHEAD, portanto misturar métodos seguros com métodos que permitem corpo manteria essas invocações acessíveis. UmINSERT ... SELECTnão lê o corpo (seus dados vêm doSELECT), portanto não está sujeito a esses requisitos — exceto se oSELECTler da função de tabelainput, que é alimentada pelo corpo da requisição. UmINSERTque lê o corpo deve ser a própria consulta do manipulador:EXECUTE ASePARALLEL WITHexecutam as instruções que encapsulam sem o corpo da requisição; portanto, encapsulá-lo em um deles é rejeitado na criação, em vez de descartar silenciosamente cada upload. Uma consulta que lê o corpo também não pode usar o parâmetro_request_body: há um único corpo de requisição, e a vinculação de_request_bodyo consome antes que a consulta leia seus dados de entrada; portanto, esse manipulador é rejeitado na criação, em vez de perder silenciosamente cada upload — use a entrada de corpo da própria consulta ou_request_body, mas não ambos. Manipuladores que não leem o corpo não têm esse requisito; o corpo de uma requisição para esse tipo de manipulador é ignorado e nunca é anexado à consulta do manipulador. O texto da consulta armazenada é analisado novamente pelo servidor com profundidade do analisador e número de retrocessos ilimitados sempre que o manipulador é recarregado ou invocado; assim, um manipulador criado em uma sessão commax_parser_depth/max_parser_backtrackselevados continua podendo ser carregado e invocado sob limites normais de sessão.
Priority
Os handlers definidos na configuração do servidor têm prioridade sobre os handlers definidos em SQL. Os handlers definidos em SQL são correspondidos em ordem lexicográfica pelos respectivos nomes.
Parâmetros
Os parâmetros de consulta para consultas parametrizadas são fornecidos, assim como para handlers definidos na configuração, a partir de:
- parâmetros de URL HTTP na string de consulta, usando a convenção
param_<name>(por exemplo,?param_id=42associa{id:Type}); - grupos de captura nomeados em uma
URL REGEXP(por exemplo,URL REGEXP '/users/(?P<id>\d+)'associa{id:Type}); - campos de formulário do corpo da requisição, para um handler cuja consulta declara parâmetros: um corpo
application/x-www-form-urlencoded(por exemplo,curl -d 'param_id=42') e os campos de um corpomultipart/form-dataassociam parâmetros{name:Type}da mesma forma que os parâmetros de URL, nos métodos que aceitam corpoPOST,PUTeDELETE. Um parâmetro presente tanto na URL quanto no corpo assume o valor da URL. Um corpo analisado como formulário é consumido pela camada do handler: ele não é enviado à consulta como dados deINSERT. Um handler cujo único uso do corpo é_request_bodyrecebe o corpo bruto em vez de analisá-lo como formulário; um handler que declara_request_bodyjunto com outros parâmetros recebe ambos — uma cópia do corpo bruto, não analisado, é preservada em_request_body(sujeita ahttp_max_request_param_data_size) antes que o corpo seja analisado como formulário.
Os cabeçalhos HTTP padrão do ClickHouse (como X-ClickHouse-Database, X-ClickHouse-User e X-ClickHouse-Key) são tratados normalmente ao invocar um handler.
As funções currentHandler e currentRequestURL podem ser usadas para personalizar o comportamento da consulta de acordo com o handler invocado e a URL da requisição.
Controle de acesso
CREATE HANDLER, DROP HANDLER e ALTER HANDLER exigem as permissões CREATE HANDLER, DROP HANDLER e ALTER HANDLER, respectivamente.
A leitura da tabela system.handlers exige a permissão SHOW HANDLERS. Os Secrets que podem estar embutidos na consulta de um handler são mascarados nessa tabela, a menos que o usuário também tenha permissão para visualizar Secrets (consulte system.handlers).
A invocação de um handler não exige uma permissão específica, mas as permissões são verificadas normalmente durante a execução da consulta, e a autenticação funciona como de costume. Para encapsular o acesso a determinadas consultas, crie uma VIEW com SQL SECURITY DEFINER e defina um handler que execute uma instrução SELECT nessa view.
Armazenamento
Os handlers são armazenados em um armazenamento local ou no Keeper, de forma semelhante às named collections, configurado na seção query_rules_storage do arquivo de configuração:
<query_rules_storage>
<type>local</type> <!-- or zookeeper -->
<path>/var/lib/clickhouse/handlers/</path>
</query_rules_storage>Com o armazenamento Keeper, os handlers são automaticamente mantidos em sincronia em todas as réplicas. Portanto, uma cláusula ON CLUSTER explícita é redundante e faria com que cada réplica tentasse criar o mesmo handler. Habilite a configuração ignore_on_cluster_for_replicated_handler_queries para que CREATE, ALTER e DROP HANDLER ignorem ON CLUSTER quando o armazenamento for replicado, espelhando 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 ...]Substitui o handler por outro. A consulta ALTER pode incluir apenas um subconjunto de cláusulas; por exemplo, pode ser usada para alterar somente a URL ou a consulta. As cláusulas não especificadas mantêm seus valores anteriores. PROTOCOL ANY remove uma restrição de protocolo existente, reativando o handler em todos os endpoints HTTP.
DROP HANDLER
DROP HANDLER [IF EXISTS] nameRemove o handler com o nome especificado.
Introspecção
A tabela system.handlers lista todos os handlers definidos em SQL. A tabela system.query_log registra, nas colunas http_handler_name e http_request_url, o nome do handler e o caminho da requisição HTTP (sem a string de consulta) de cada consulta.
Exemplo
CREATE HANDLER my_handler URL '/my_handler' AS SELECT version();$ curl 'http://localhost:8123/my_handler'Um handler parametrizado com uma URL Regexp:
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 faz parte da família de instruções CREATE e está relacionado às instruções ALTER e DROP.