ينشئ معالج 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، وفي الرسائل التشخيصية، ولترتيب المعالجات.
عبارات SQL
PROTOCOL— اختياري. إذا حُدِّد اسم بروتوكول، فلن يكون المعالج نشطًا إلا للبروتوكول القابل للتركيب المحدد. وإلا، يكون المعالج نشطًا على جميع نقاط نهاية HTTP: منافذhttp/httpsالمضمّنة وكل مستمع بروتوكول قابل للتركيب من نوع HTTP. يختارPROTOCOL ANYصراحةً هذا السلوك الافتراضي الأخير؛ وفيALTER HANDLERيزيل تقييد بروتوكول تم تعيينه سابقًا. يمكن الإشارة إلى بروتوكول يحمل الاسم الحرفيanyباستخدام علامات الاقتباس الخلفية:PROTOCOL `any`.URL— إلزامي. يمكن أن يكون URL مطابقًا تمامًا، أوURL PREFIX، أوURL REGEXP. بالنسبة إلى عناوين URL المطابقة تمامًا والبادئات، يُتحقق من عدم الالتباس عند الإنشاء أو التعديل، ويُرفع استثناء عند وجود التباس. أما بالنسبة إلى regexp، فلا يمكن التحقق من الالتباس. تجري مطابقة 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قابلًا للتحميل والاستدعاء ضمن حدود الجلسة العادية.
priority
تكون للمعالجات المعرَّفة في تهيئة الخادم أولوية على المعالجات المعرَّفة في SQL. وتُطابَق المعالجات المعرَّفة في SQL بالترتيب المعجمي لأسمائها.
المعلمات
تُمرَّر معلمات الاستعلامات المُعلَّمة، كما هو الحال في المعالجات المُعرَّفة في التهيئة، من خلال:
- معلمات HTTP URL في سلسلة الاستعلام، باستخدام اصطلاح
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 وعرّف معالجًا ينفّذ SELECT من ذلك العرض.
التخزين
تُحفَظ المعالجات في مساحة تخزين محلية أو من نوع 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
DROP HANDLER [IF EXISTS] nameيحذف المعالج الذي يحمل الاسم المحدد.
الاستبطان
يسرد جدول system.handlers جميع المعالجات المعرّفة باستخدام SQL. ويسجّل جدول system.query_log اسم المعالج ومسار طلب HTTP (من دون سلسلة الاستعلام) لكل استعلام في العمودين http_handler_name وhttp_request_url.
مثال
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.