Crée un gestionnaire HTTP personnalisé défini en SQL, sans modifier le fichier de configuration du serveur. Les gestionnaires définis en SQL constituent une alternative aux gestionnaires de l'interface HTTP configurés via la configuration.
Syntaxe
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|...] ...Crée un gestionnaire avec un nom spécifié. Le nom est utilisé pour gérer les gestionnaires à l’aide de requêtes SQL, pour les messages de diagnostic et pour définir leur ordre.
Clauses
PROTOCOL— facultatif. Si un nom de protocole est spécifié, le gestionnaire est actif uniquement pour le protocole composable indiqué. Sinon, le gestionnaire est actif sur tous les endpoints HTTP : les portshttp/httpsintégrés et chaque écouteur de protocole composable de type HTTP.PROTOCOL ANYsélectionne explicitement ce comportement par défaut ; dansALTER HANDLER, il supprime une restriction de protocole définie précédemment. Un protocole littéralement nomméanypeut être référencé à l’aide d’accents graves :PROTOCOL `any`.URL— obligatoire. Peut prendre la forme d’une URL exacte, d’unURL PREFIXou d’unURL REGEXP. Pour les URL exactes et les préfixes, l’ambiguïté est vérifiée lors de la création ou de la modification, et une exception est levée en cas d’ambiguïté. Pour les expressions régulières, l’ambiguïté ne peut pas être vérifiée. L’URL est mise en correspondance sans la chaîne de requête?ni l’identifiant de fragment#. UnURL PREFIXest mis en correspondance comme un chemin de base, à la limite d’un segment de chemin — avec la même sémantique que la règleurl_prefixdes gestionnaires définis par la configuration :URL PREFIX '/api/v1'correspond à/api/v1,/api/v1/et/api/v1/write, mais pas à/api/v1beta. Un/final dans le préfixe est ignoré ;'/api/v1/'et'/api/v1'se comportent donc de la même manière.METHODS— facultatif. Liste des méthodes HTTP autorisées. Par défaut, seuleGETest autorisée. Les méthodes prises en charge sontGET,POST,PUTetDELETE. Les méthodes de modificationPOST,PUTetDELETEpeuvent exécuter des requêtes modifiant des données ; les méthodes sûres telles queGETetHEADsont toujours exécutées en modereadonly. Par conséquent, un gestionnaire dont la requête modifie des données (par exempleINSERTou DDL) doit autoriser au moins une méthode de modification : créer un tel gestionnaire avec uniquement des méthodes en lecture seule (par exempleGET, la valeur par défaut) lève une exception. Les requêtes dont les effets secondaires subsistent en modereadonlyconstituent un cas particulier :BACKUPetRESTOREont des effets secondaires durables, les instructions modifiant la sessionSET,SET ROLE,USE,BEGIN TRANSACTION,COMMIT,ROLLBACKetSET TRANSACTION SNAPSHOTmodifient un état de session ou de transaction qui persiste entre les requêtes lorsquesession_idest utilisé, etCREATE TEMPORARY TABLE/CREATE TEMPORARY VIEWcréent un objet qui existe pendant la session — pourtant, le modereadonlydes méthodes sûres ne bloque aucune d’entre elles. Les mutations d’une table temporaire existante ne sont pas non plus bloquées par le modereadonly. Les requêtes susceptibles d’en cibler une sont donc traitées de la même manière : unINSERTdont la table cible n’est pas qualifiée par une base de données (un nom non qualifié peut désigner une table temporaire de session), unDROP TEMPORARY TABLE, unDROP TABLE/TRUNCATE TABLEsur une table non qualifiée par une base de données, et unALTERsur une table non qualifiée par une base de données (ALTER TEMPORARY TABLEest la même instruction). Une cible qualifiée par une base de données ne peut jamais être une table temporaire ; ces requêtes ne sont donc pas soumises à cette règle. HTTP exige que les méthodes sûres soient exemptes d’effets secondaires (un gestionnaire déclaré pourGETrépond également àHEAD, pour lequel le corps de la réponse est supprimé et l’effet serait invisible). Un gestionnaire exécutant une telle requête doit donc répertorier uniquement des méthodes de modification : le créer ou le modifier pour inclure une méthode sûre lève une exception. Les instructions composites sont examinées en profondeur : pourstatement1 PARALLEL WITH statement2 ...etEXECUTE AS <user> <statement>, les règles ci-dessus s’appliquent aux instructions encapsulées, car ce sont elles qui sont exécutées (chacune dans une copie du contexte du gestionnaire, qui conserve le modereadonly). UnEXECUTE AS <user>seul fait exécuter toute la session sous un autre utilisateur ; il est donc lui-même considéré comme modifiant la session. De plus, tout gestionnaireEXECUTE AS— seul ou encapsulant une instruction — doit autoriser au moins une méthode de modification : l’impersonation nécessite le privilègeIMPERSONATE, que le modereadonlydes méthodes sûres refuse.TYPE— facultatif. Le seul type actuellement pris en charge estquery.AS— la requête SQL qui sera exécutée par ce gestionnaire. La requête peut être paramétrée. Sa validité syntaxique est vérifiée lors de la création ou de la modification du gestionnaire, mais elle n'est pas analysée davantage : par exemple, les tables auxquelles elle fait référence peuvent être absentes au moment de la création du gestionnaire. Les clausesFORMATet similaires appartiennent à la requête, et non à l'instructionCREATE/ALTERdans son ensemble. La requête peut être placée entre parenthèses pour lever toute ambiguïté. Une requêteINSERTne doit pas contenir de données en ligne après la clauseVALUESouFORMAT: la création ou la modification d'un tel gestionnaire lève une exception, car la charge utile en ligne ne peut pas être conservée dans la définition du gestionnaire ; les données doivent être fournies dans le corps HTTP (ou calculées par unINSERT ... SELECT). Une requête adressée à un gestionnaire dont la requête lit le corps — unINSERTqui obtient ses données depuis le corps, ou une requête utilisant le paramètre_request_body— doit déclarer sa longueur : une requête sans transfert fragmenté et sans en-têteContent-Lengthreçoit la réponse411 Length Required, car le corps serait sinon lu jusqu'à la fin du flux et une connexion interrompue serait acceptée comme une requête complète. Toutes les méthodes d'un tel gestionnaire doivent également accepter un corps (POST,PUTouDELETE) — le créer avec une méthode sûre dans la clauseMETHODS(par exemple,GETpar défaut) lève une exception, car une méthode sûre ne fournit jamais de corps de requête et la requête lirait silencieusement un corps vide ; unGETdéclaré est également pris en charge pourHEAD, de sorte que mélanger des méthodes sûres et des méthodes acceptant un corps rendrait ces invocations accessibles. UnINSERT ... SELECTne lit pas le corps (ses données proviennent duSELECT) et n'est donc pas soumis à ces exigences — sauf si sonSELECTlit depuis la fonction de tableinput, qui est alimentée par le corps de la requête. UnINSERTlisant le corps doit être la requête propre au gestionnaire :EXECUTE ASetPARALLEL WITHexécutent les instructions qu'ils englobent sans le corps de la requête ; l'encapsuler dans l'une de ces instructions est donc rejeté lors de la création, plutôt que d'ignorer silencieusement chaque téléversement. Une requête lisant le corps ne doit pas non plus utiliser le paramètre_request_body: il n'existe qu'un seul corps de requête, et la liaison de_request_bodyle consomme avant que la requête ne lise ses données d'entrée ; un tel gestionnaire est donc rejeté lors de la création, plutôt que de perdre silencieusement chaque téléversement — utilisez soit la propre entrée depuis le corps de la requête, soit_request_body, mais pas les deux. Les gestionnaires qui ne lisent pas le corps ne sont soumis à aucune exigence de ce type ; le corps d'une requête adressée à un tel gestionnaire est ignoré et n'est jamais ajouté à la requête du gestionnaire. Le texte de requête stocké est analysé de nouveau par le serveur avec une profondeur d'analyseur syntaxique et un nombre de retours arrière illimités chaque fois que le gestionnaire est rechargé ou exécuté ; ainsi, un gestionnaire créé dans une session avecmax_parser_depth/max_parser_backtracksaugmentés reste chargeable et exécutable avec les limites de session ordinaires.
Priorité
Les gestionnaires définis dans la configuration du serveur sont prioritaires par rapport aux gestionnaires définis en SQL. Les gestionnaires définis en SQL sont associés dans l’ordre lexicographique de leurs noms.
Paramètres
Les paramètres de requête des requêtes paramétrées sont fournis, comme pour les gestionnaires définis dans la configuration, à partir des sources suivantes :
- des paramètres d’URL HTTP dans la chaîne de requête, selon la convention
param_<name>(par exemple,?param_id=42lie{id:Type}) ; - des groupes de capture nommés dans une
URL REGEXP(par exemple,URL REGEXP '/users/(?P<id>\d+)'lie{id:Type}) ; - des champs de formulaire du corps de la requête, pour un gestionnaire dont la requête déclare des paramètres : un corps
application/x-www-form-urlencoded(par exemple,curl -d 'param_id=42') et les champs d’un corpsmultipart/form-datalient les paramètres{name:Type}de la même manière que les paramètres d’URL, pour les méthodes acceptant un corpsPOST,PUTetDELETE. Un paramètre présent à la fois dans l’URL et dans le corps prend sa valeur dans l’URL. Un corps analysé comme un formulaire est consommé par la couche de gestionnaires : il n’est pas transmis à la requête en tant que donnéesINSERT. Un gestionnaire dont la seule utilisation du corps est_request_bodyreçoit le corps brut au lieu qu’il soit analysé comme un formulaire ; un gestionnaire qui déclare_request_bodyavec d’autres paramètres reçoit les deux : une copie du corps brut, non analysé, est conservée dans_request_body(sous réserve dehttp_max_request_param_data_size) avant que le corps ne soit analysé comme un formulaire.
Les en-têtes HTTP ClickHouse standard (tels que X-ClickHouse-Database, X-ClickHouse-User, X-ClickHouse-Key) sont respectés comme d’habitude lors de l’appel d’un gestionnaire.
Les fonctions currentHandler et currentRequestURL peuvent être utilisées pour personnaliser le comportement de la requête selon le gestionnaire appelé et l’URL de la requête.
Contrôle d’accès
CREATE HANDLER, DROP HANDLER et ALTER HANDLER requièrent respectivement les privilèges CREATE HANDLER, DROP HANDLER et ALTER HANDLER.
La lecture de la table system.handlers requiert le privilège SHOW HANDLERS. Les secrets pouvant être intégrés à la requête d’un gestionnaire y sont masqués, à moins que l’utilisateur ne soit également autorisé à les consulter (voir system.handlers).
L’appel d’un gestionnaire ne requiert aucun privilège distinct, mais les privilèges sont vérifiés comme d’habitude lors de l’exécution de la requête, et l’authentification fonctionne normalement. Pour encapsuler l’accès à certaines requêtes, créez une VIEW avec SQL SECURITY DEFINER et définissez un gestionnaire qui effectue un SELECT sur cette vue.
Stockage
Les gestionnaires sont enregistrés dans un stockage local ou Keeper, à l’instar des collections nommées, configuré dans la section query_rules_storage du fichier de configuration :
<query_rules_storage>
<type>local</type> <!-- or zookeeper -->
<path>/var/lib/clickhouse/handlers/</path>
</query_rules_storage>Avec le stockage Keeper, les gestionnaires sont automatiquement synchronisés sur l’ensemble des répliques. Une clause ON CLUSTER explicite est donc redondante et conduirait chaque réplique à tenter de créer le même gestionnaire. Activez le paramètre ignore_on_cluster_for_replicated_handler_queries afin que CREATE, ALTER et DROP HANDLER ignorent ON CLUSTER lorsque le stockage est répliqué, comme 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 ...]Remplace le gestionnaire par un nouveau. La requête ALTER ne peut inclure qu’un sous-ensemble de clauses. Elle peut, par exemple, servir à modifier uniquement l’URL ou la requête. Les clauses non spécifiées conservent leurs valeurs précédentes. PROTOCOL ANY supprime une restriction de protocole existante et rend le gestionnaire à nouveau actif sur tous les endpoints HTTP.
DROP HANDLER
DROP HANDLER [IF EXISTS] nameSupprime le gestionnaire portant le nom spécifié.
Introspection
La table system.handlers répertorie tous les gestionnaires définis en SQL. La table system.query_log enregistre, pour chaque requête, le nom du gestionnaire ainsi que le chemin de la requête HTTP (sans la chaîne de requête) dans les colonnes http_handler_name et http_request_url.
Exemple
CREATE HANDLER my_handler URL '/my_handler' AS SELECT version();$ curl 'http://localhost:8123/my_handler'Un gestionnaire paramétré avec une URL d’expression régulière :
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 fait partie de la famille d’instructions CREATE et est lié à ALTER et DROP.