يدعم ClickHouse عدة أنواع من الدوال المعرّفة من قبل المستخدم (UDFs):
- تبدأ UDFs القابلة للتنفيذ برنامجًا خارجيًا أو برنامجًا نصيًا (بايثون، Bash، إلخ)، وتُمرِّر إليه blocks من البيانات عبر STDIN / STDOUT. استخدمها لدمج التعليمات البرمجية أو الأدوات الحالية دون الحاجة إلى إعادة تجميع ClickHouse. وهي تفرض overhead أعلى لكل استدعاء مقارنةً بالخيارات التي تعمل داخل العملية، لذا فهي الأنسب للمنطق الأثقل أو عند الحاجة إلى runtime مختلف.
- تُعرَّف SQL UDFs باستخدام
CREATE FUNCTIONبالكامل ضمن SQL. وتُضمَّن/تُوسَّع داخل query plan (من دون حدٍّ فاصل على مستوى العملية)، مما يجعلها lightweight ومثالية لإعادة استخدام منطق expression أو تبسيط columns المحسوبة المعقدة. - تُشغِّل Experimental WebAssembly UDFs تعليمات برمجية مُجمَّعة إلى WebAssembly داخل sandbox ضمن server process. وهي توفّر overhead أقل لكل استدعاء مقارنةً بالبرامج التنفيذية الخارجية، مع عزل أفضل من الامتدادات native، مما يجعلها مناسبة للخوارزميات المخصّصة المكتوبة بلغات يمكنها الاستهداف إلى WASM (مثل C/C++/Rust).
- تتيح Experimental UDFs القابلة للتنفيذ المعتمدة على برامج التشغيل لـ برنامج التشغيل يوفّره المشغّل أن يحوّل مقطع شيفرة مُقدَّمًا في
CREATE FUNCTION ... ENGINE = DriverName(...) AS '...'إلى UDF قابلة للتنفيذ عند وقت إنشاء الدالة (على سبيل المثال، عن طريق تجميعه). وهي تستند إلى UDFs القابلة للتنفيذ وتتطلب إعداد برنامج التشغيل على جانب server.
الدوال المعرّفة من قبل المستخدم القابلة للتنفيذ
يمكن لـ ClickHouse استدعاء أي برنامج خارجي قابل للتنفيذ أو برنامج نصي لمعالجة البيانات.
يمكن أن يوجد تكوين الدوال المعرّفة من قبل المستخدم القابلة للتنفيذ في ملف XML واحد أو أكثر.
ويُحدَّد مسار هذا التكوين في المَعلمة user_defined_executable_functions_config.
يتضمن تكوين الدالة الإعدادات التالية:
| Parameter | الوصف | مطلوب | القيمة الافتراضية |
|---|---|---|---|
name |
اسم دالة | نعم | - |
command |
اسم البرنامج النصي المطلوب تنفيذه، أو الأمر إذا كانت execute_direct تساوي false |
نعم | - |
argument |
وصف الوسيطة مع type وname الاختياري الخاص بها. تُوصَف كل وسيطة في إعداد منفصل. ويكون تحديد الاسم ضروريًا إذا كانت أسماء الوسائط جزءًا من التسلسل لدالة معرّفة من قبل المستخدم بتنسيق مثل Native أو JSONEachRow |
نعم | c + argument_number |
format |
تنسيق تُمرَّر به الوسائط إلى الأمر. ومن المتوقع أيضًا أن يستخدم خرج الأمر التنسيق نفسه | نعم | - |
return_type |
نوع القيمة المُعادة | نعم | - |
return_name |
اسم القيمة المُعادة. ويكون تحديد اسم الإرجاع ضروريًا إذا كان اسم الإرجاع جزءًا من التسلسل لدالة معرّفة من قبل المستخدم بتنسيق مثل Native أو JSONEachRow | اختياري | result |
type |
نوع التنفيذ. إذا ضُبطت type على executable، فسيُشغَّل أمر واحد. وإذا ضُبطت على executable_pool، فسيُنشأ مجمّع من الأوامر |
نعم | - |
max_command_execution_time |
الحد الأقصى لوقت التنفيذ بالثواني لمعالجة كتلة من البيانات. يكون هذا الإعداد صالحًا فقط لأوامر executable_pool |
اختياري | 10 |
command_termination_timeout |
المدة بالثواني التي يجب أن ينهي خلالها الأمر عمله بعد إغلاق الأنبوب الخاص به. بعد ذلك، تُرسَل الإشارة SIGTERM إلى العملية التي تنفذ الأمر |
اختياري | 10 |
command_read_timeout |
مهلة قراءة البيانات من stdout الخاص بالأمر، بالمللي ثانية |
اختياري | 10000 |
command_write_timeout |
مهلة كتابة البيانات إلى stdin الخاص بالأمر، بالمللي ثانية |
اختياري | 10000 |
pool_size |
حجم مجمّع الأوامر | اختياري | 16 |
send_chunk_header |
يتحكم في ما إذا كان سيتم إرسال عدد الصفوف قبل إرسال دفعة من البيانات إلى العملية | اختياري | false |
execute_direct |
إذا كانت execute_direct = 1، فسيُبحث عن command داخل مجلد user_scripts المحدد بواسطة user_scripts_path. ويمكن تحديد وسائط إضافية للبرنامج النصي باستخدام فاصل من المسافات البيضاء. مثال: script_name arg1 arg2. وإذا كانت execute_direct = 0، فسيُمرَّر command كوسيطة إلى bin/sh -c |
اختياري | 1 |
lifetime |
فترة إعادة تحميل الدالة بالثواني. إذا ضُبطت على 0 فلن تُعاد تحميل الدالة |
اختياري | 0 |
deterministic |
ما إذا كانت الدالة حتمية (تعيد النتيجة نفسها للإدخال نفسه) | اختياري | false |
stderr_reaction |
كيفية التعامل مع خرج stderr الخاص بالأمر. القيم: none (تجاهل)، log (تسجيل كل خرج stderr فورًا)، log_first (تسجيل أول 4 KiB بعد الخروج)، log_last (تسجيل آخر 4 KiB بعد الخروج)، throw (طرح استثناء فورًا عند أي خرج من stderr). عند استخدام log_first أو log_last مع رمز خروج غير صفري، يُضمَّن محتوى stderr في رسالة الاستثناء |
اختياري | log_last |
check_exit_code |
إذا كانت القيمة true، فسيتحقق ClickHouse من رمز خروج الأمر. ويتسبب رمز الخروج غير الصفري في حدوث استثناء |
اختياري | true |
يجب أن يقرأ الأمر الوسائط من STDIN وأن يكتب النتيجة إلى STDOUT. ويجب أن يعالج الأمر الوسائط على نحو تكراري. أي إنه بعد معالجة دفعة من الوسائط، يجب أن ينتظر الدفعة التالية.
الدوال المعرّفة من قبل المستخدم القابلة للتنفيذ
أمثلة
UDF من برنامج نصي مضمن
أنشئ test_function_sum يدويًا مع ضبط execute_direct على القيمة 0 باستخدام إعدادات XML أو YAML.
الملف test_function.xml (/etc/clickhouse-server/test_function.xml مع إعدادات المسار الافتراضية).
<functions>
<function>
<type>executable</type>
<name>test_function_sum</name>
<return_type>UInt64</return_type>
<argument>
<type>UInt64</type>
<name>lhs</name>
</argument>
<argument>
<type>UInt64</type>
<name>rhs</name>
</argument>
<format>TabSeparated</format>
<command>cd /; clickhouse-local --input-format TabSeparated --output-format TabSeparated --structure 'x UInt64, y UInt64' --query "SELECT x + y FROM table"</command>
<execute_direct>0</execute_direct>
<deterministic>true</deterministic>
</function>
</functions>الملف test_function.yaml (/etc/clickhouse-server/test_function.yaml مع إعدادات المسار الافتراضية).
functions:
type: executable
name: test_function_sum
return_type: UInt64
argument:
- type: UInt64
name: lhs
- type: UInt64
name: rhs
format: TabSeparated
command: 'cd /; clickhouse-local --input-format TabSeparated --output-format TabSeparated --structure ''x UInt64, y UInt64'' --query "SELECT x + y FROM table"'
execute_direct: 0
deterministic: trueSELECT test_function_sum(2, 2);┌─test_function_sum(2, 2)─┐
│ 4 │
└─────────────────────────┘UDF من برنامج نصي بايثون
في هذا المثال، ننشئ UDF يقرأ قيمة من STDIN ويُرجعها كسلسلة نصية.
أنشئ test_function باستخدام تهيئة XML أو YAML.
الملف test_function.xml (/etc/clickhouse-server/test_function.xml مع إعدادات المسار الافتراضية).
<functions>
<function>
<type>executable</type>
<name>test_function_python</name>
<return_type>String</return_type>
<argument>
<type>UInt64</type>
<name>value</name>
</argument>
<format>TabSeparated</format>
<command>test_function.py</command>
</function>
</functions>الملف test_function.yaml (/etc/clickhouse-server/test_function.yaml مع إعدادات المسار الافتراضية).
functions:
type: executable
name: test_function_python
return_type: String
argument:
- type: UInt64
name: value
format: TabSeparated
command: test_function.pyأنشئ ملف البرنامج النصي test_function.py داخل مجلد user_scripts (/var/lib/clickhouse/user_scripts/test_function.py مع إعدادات المسار الافتراضية).
#!/usr/bin/python3
import sys
if __name__ == '__main__':
for line in sys.stdin:
print("Value " + line, end='')
sys.stdout.flush()SELECT test_function_python(toUInt64(2));┌─test_function_python(2)─┐
│ Value 2 │
└─────────────────────────┘اقرأ قيمتين من STDIN وأعِد مجموعهما على شكل كائن JSON
أنشئ test_function_sum_json باستخدام وسيطات مُسمّاة وتنسيق JSONEachRow، وذلك باستخدام تهيئة XML أو YAML.
الملف test_function.xml (/etc/clickhouse-server/test_function.xml مع إعدادات المسار الافتراضية).
<functions>
<function>
<type>executable</type>
<name>test_function_sum_json</name>
<return_type>UInt64</return_type>
<return_name>result_name</return_name>
<argument>
<type>UInt64</type>
<name>argument_1</name>
</argument>
<argument>
<type>UInt64</type>
<name>argument_2</name>
</argument>
<format>JSONEachRow</format>
<command>test_function_sum_json.py</command>
</function>
</functions>الملف test_function.yaml (/etc/clickhouse-server/test_function.yaml مع إعدادات المسار الافتراضية).
functions:
type: executable
name: test_function_sum_json
return_type: UInt64
return_name: result_name
argument:
- type: UInt64
name: argument_1
- type: UInt64
name: argument_2
format: JSONEachRow
command: test_function_sum_json.pyأنشئ ملف البرنامج النصي test_function_sum_json.py داخل المجلد user_scripts (/var/lib/clickhouse/user_scripts/test_function_sum_json.py مع إعدادات المسار الافتراضية).
#!/usr/bin/python3
import sys
import json
if __name__ == '__main__':
for line in sys.stdin:
value = json.loads(line)
first_arg = int(value['argument_1'])
second_arg = int(value['argument_2'])
result = {'result_name': first_arg + second_arg}
print(json.dumps(result), end='\n')
sys.stdout.flush()SELECT test_function_sum_json(2, 2);┌─test_function_sum_json(2, 2)─┐
│ 4 │
└──────────────────────────────┘استخدام المعلمات في إعداد command
يمكن للدوال المعرّفة من قبل المستخدم القابلة للتنفيذ أخذ معلمات ثابتة تُضبط في إعداد command (يعمل هذا فقط مع الدوال المعرّفة من قبل المستخدم من النوع executable).
ويتطلب ذلك أيضًا الخيار execute_direct لضمان عدم وجود ثغرة ناتجة عن توسيع وسيطات الصدفة.
ملف test_function_parameter_python.xml (/etc/clickhouse-server/test_function_parameter_python.xml مع إعدادات المسار الافتراضية).
<functions>
<function>
<type>executable</type>
<execute_direct>true</execute_direct>
<name>test_function_parameter_python</name>
<return_type>String</return_type>
<argument>
<type>UInt64</type>
</argument>
<format>TabSeparated</format>
<command>test_function_parameter_python.py {test_parameter:UInt64}</command>
</function>
</functions>ملف test_function_parameter_python.yaml (/etc/clickhouse-server/test_function_parameter_python.yaml مع إعدادات المسار الافتراضية).
functions:
type: executable
execute_direct: true
name: test_function_parameter_python
return_type: String
argument:
- type: UInt64
format: TabSeparated
command: test_function_parameter_python.py {test_parameter:UInt64}أنشئ ملف البرنامج النصي test_function_parameter_python.py داخل المجلد user_scripts (/var/lib/clickhouse/user_scripts/test_function_parameter_python.py مع إعدادات المسار الافتراضية).
#!/usr/bin/python3
import sys
if __name__ == "__main__":
for line in sys.stdin:
print("Parameter " + str(sys.argv[1]) + " value " + str(line), end="")
sys.stdout.flush()SELECT test_function_parameter_python(1)(2);┌─test_function_parameter_python(1)(2)─┐
│ Parameter 1 value 2 │
└──────────────────────────────────────┘UDF من برنامج نصي shell
في هذا المثال، ننشئ برنامجًا نصيًا لـ shell يُضاعف كل قيمة.
ملف test_function_shell.xml (/etc/clickhouse-server/test_function_shell.xml باستخدام إعدادات المسار الافتراضية).
<functions>
<function>
<type>executable</type>
<name>test_shell</name>
<return_type>String</return_type>
<argument>
<type>UInt8</type>
<name>value</name>
</argument>
<format>TabSeparated</format>
<command>test_shell.sh</command>
</function>
</functions>ملف test_function_shell.yaml (/etc/clickhouse-server/test_function_shell.yaml باستخدام إعدادات المسار الافتراضية).
functions:
type: executable
name: test_shell
return_type: String
argument:
- type: UInt8
name: value
format: TabSeparated
command: test_shell.shأنشئ ملف البرنامج النصي test_shell.sh داخل المجلد user_scripts (/var/lib/clickhouse/user_scripts/test_shell.sh باستخدام إعدادات المسار الافتراضية).
#!/bin/bash
while read read_data;
do printf "$(expr $read_data \* 2)\n";
doneSELECT test_shell(number) FROM numbers(10); ┌─test_shell(number)─┐
1. │ 0 │
2. │ 2 │
3. │ 4 │
4. │ 6 │
5. │ 8 │
6. │ 10 │
7. │ 12 │
8. │ 14 │
9. │ 16 │
10. │ 18 │
└────────────────────┘معالجة الأخطاء
قد تُطلق بعض الدوال استثناءً إذا كانت البيانات غير صالحة. في هذه الحالة، يُلغى الاستعلام ويُعاد نص الخطأ إلى العميل. في المعالجة الموزعة، إذا حدث استثناء على أحد الخوادم، تحاول الخوادم الأخرى أيضًا إلغاء الاستعلام.
تقييم تعبيرات الوسائط
في معظم لغات البرمجة، قد لا يُقيَّم أحد الوسائط مع بعض المعاملات.
وعادةً ما تكون هذه المعاملات هي && و || و ?:.
في ClickHouse، تُقيَّم وسائط الدوال (المعاملات) دائمًا.
ويرجع ذلك إلى أن أجزاءً كاملة من الأعمدة تُقيَّم دفعةً واحدة، بدلًا من حساب كل صف على حدة.
تنفيذ الدوال في معالجة الاستعلامات الموزعة
في معالجة الاستعلامات الموزعة، يُنفَّذ أكبر عدد ممكن من مراحل معالجة الاستعلام على الخوادم البعيدة، وتُنفَّذ بقية المراحل (مثل دمج النتائج الوسيطة وكل ما يلي ذلك) على الخادم الطالب.
وهذا يعني أن الدوال قد تُنفَّذ على خوادم مختلفة.
على سبيل المثال، في الاستعلام SELECT f(sum(g(x))) FROM distributed_table GROUP BY h(y),
- إذا كان
distributed_tableيحتوي على shardين على الأقل، فستُنفَّذ الدالتان 'g' و 'h' على الخوادم البعيدة، بينما تُنفَّذ الدالة 'f' على الخادم الطالب. - إذا كان
distributed_tableيحتوي على shard واحد فقط، فستُنفَّذ جميع الدوال 'f' و 'g' و 'h' على خادم هذا الـ shard.
عادةً لا تعتمد نتيجة الدالة على الخادم الذي تُنفَّذ عليه. لكن في بعض الحالات يكون ذلك مهمًا.
فعلى سبيل المثال، تستخدم الدوال التي تعمل مع القواميس القاموس الموجود على الخادم الذي تعمل عليه.
ومثال آخر هو الدالة hostName، التي تُرجع اسم الخادم الذي تعمل عليه، بحيث يمكن تنفيذ GROUP BY حسب الخوادم في استعلام SELECT.
إذا كانت دالة في استعلام ما تُنفَّذ على الخادم الطالب، لكنك تحتاج إلى تنفيذها على الخوادم البعيدة، فيمكنك تغليفها داخل الدالة التجميعية 'any' أو إضافتها إلى مفتاح في GROUP BY.
دوال SQL المعرفة من قبل المستخدم
يمكن إنشاء دوال مخصصة من تعبيرات lambda باستخدام عبارة CREATE FUNCTION. ولحذف هذه الدوال، استخدم عبارة DROP FUNCTION.
دوال WebAssembly المعرّفة من قبل المستخدم
تتيح لك دوال WebAssembly المعرّفة من قبل المستخدم (WASM UDFs) تشغيل تعليمات برمجية مخصّصة مُجمَّعة إلى WebAssembly داخل عملية خادم ClickHouse.
البدء السريع
فعِّل دعم WebAssembly التجريبي في تهيئة ClickHouse:
<clickhouse>
<allow_experimental_webassembly_udf>true</allow_experimental_webassembly_udf>
</clickhouse>أدرِج وحدة WASM المُجمَّعة في جدول النظام:
INSERT INTO system.webassembly_modules (name, code)
SELECT 'my_module', base64Decode('AGFzbQEAAAA...');أنشئ FUNCTION باستخدام وحدة WASM الخاصة بك:
CREATE FUNCTION my_function
LANGUAGE WASM
ABI ROW_DIRECT
FROM 'my_module'
ARGUMENTS (x UInt32, y UInt32)
RETURNS UInt32;استخدم FUNCTION في استعلاماتك:
SELECT my_function(10, 20);مزيد من المعلومات
راجِع وثائق دوال WebAssembly المعرّفة من قبل المستخدم لمزيد من التفاصيل.
الدوال المعرّفة من قبل المستخدم القابلة للتنفيذ المعتمد على برامج التشغيل
برنامج التشغيل هو مهايئ يوفّره المشغّل لتحويل مقتطف من شيفرة المستخدم إلى دالة معرّفة من قبل المستخدم قابلة للتنفيذ يمكن تشغيلها. عند إنشاء دالة باستخدام ENGINE = DriverName(...)، يشغّل ClickHouse الأمر create_command الخاص ببرنامج التشغيل، مع تمرير توقيع الدالة ومحتوى الشيفرة إليه. بعد ذلك، يترجم برنامج التشغيل هذا المحتوى أو يعالجه بطريقة أخرى، ثم يطبع إعدادات دالة معرّفة من قبل المستخدم قابلة للتنفيذ، ليقوم ClickHouse بعدها بتخزينها وتحميلها.
يتيح ذلك للمسؤولين توفير طريقة آمنة ومحدودة للمستخدمين لتعريف دوال بلغة برمجة كيفما كانت (على سبيل المثال، C بعد ترجمتها داخل حاوية معزولة) من دون منحهم إمكانية الوصول إلى ملفات تهيئة الخادم أو نظام الملفات. وتظل مجموعة برامج التشغيل المتاحة خاضعة بالكامل لسيطرة المشغّل.
تمكين برامج التشغيل
تكون UDFs القابلة للتنفيذ المعتمد على برامج التشغيل معطّلة افتراضيًا. لتمكينها:
-
عيّن خيار Experimental في إعدادات الخادم:
<clickhouse> <allow_experimental_executable_udf_drivers>true</allow_experimental_executable_udf_drivers> </clickhouse> -
وجّه
user_defined_executable_function_drivers_configإلى ملف configuration واحد أو أكثر لبرامج التشغيل (مع دعم نمطglob)، واضبط اختياريًاdynamic_user_defined_executable_functions_path، وهو الدليل الذي تُخزَّن فيه ملفات configuration المُولَّدة الخاصة بـ UDFs القابلة للتنفيذ:<clickhouse> <user_defined_executable_function_drivers_config>user_defined_executable_function_drivers_config.d/*_driver.xml</user_defined_executable_function_drivers_config> <dynamic_user_defined_executable_functions_path>/var/lib/clickhouse/dynamic_user_defined_executable_functions/</dynamic_user_defined_executable_functions_path> </clickhouse>
يُحمَّل سجل برامج التشغيل عند بدء تشغيل الخادم، ويُحدَّث عند SYSTEM RELOAD CONFIG، لذلك يمكن إضافة برامج التشغيل أو تعديلها أو إزالتها دون إعادة تشغيل الخادم.
إعداد برنامج التشغيل
يُعرَّف برنامج التشغيل من خلال ملف XML (أو YAML) يحتوي على عنصر <driver> في المستوى الأعلى. الحقول التالية مدعومة:
| الحقل | الوصف | مطلوب |
|---|---|---|
name |
اسم برنامج التشغيل، كما يُستخدم في CREATE FUNCTION ... ENGINE = <name>(...). |
نعم |
create_command |
المسار إلى البرنامج الذي يُستدعى لإنشاء UDF من مقتطف شيفرة. تُفسَّر المسارات النسبية بالاستناد إلى ملف إعداد برنامج التشغيل. | نعم |
drop_command |
المسار إلى البرنامج الذي يُستدعى عند حذف دالة تستند إلى برنامج التشغيل هذا. | لا |
engine_arguments |
يحدّد الوسيطات المسموح بها داخل ENGINE = DriverName(...). كل عنصر فرعي هو اسم وسيطة؛ ويُشير العنصر الفرعي <required>true</required> إلى أنها إلزامية. |
لا |
env |
متغيرات البيئة المُصدَّرة عند استدعاء أوامر برنامج التشغيل. | لا |
مثال على إعداد برنامج التشغيل:
<clickhouse>
<driver>
<name>DockerC</name>
<create_command>../user_defined_executable_function_drivers/docker_c_create.sh</create_command>
<drop_command>../user_defined_executable_function_drivers/docker_c_drop.sh</drop_command>
<engine_arguments>
<opt_level><required>false</required></opt_level>
</engine_arguments>
<env>
<CLICKHOUSE_C_DRIVER_MEMORY>256m</CLICKHOUSE_C_DRIVER_MEMORY>
<CLICKHOUSE_C_DRIVER_CPUS>1.0</CLICKHOUSE_C_DRIVER_CPUS>
</env>
</driver>
</clickhouse>بروتوكول استدعاء برنامج التشغيل
عند تنفيذ CREATE FUNCTION، يُستدعى create_command بعد ضبط متغيرات env المهيأة، مع تمرير الوسيطات التالية:
--name <function_name>--return <return_type>(إذا وُجد بندRETURNS)--args <signature>(إذا وُجد بندARGUMENTS)، حيث يمثّل التوقيع قائمة الوسيطات المعلنة، مثلx UInt8, y DateTime--<key> <value>لكل وسيط engine مُعلن وممرَّر فيENGINE = DriverName(key = value)
يُرسَل متن شيفرة المستخدم (النص بعد AS) إلى الإدخال القياسي للأمر. ويجب أن يطبع الأمر تهيئة UDF executable إلى المخرج القياسي. ويُكتشَف format تلقائيًا: فإذا بدأ المخرج بـ < عُدّ XML، وإلا عُدّ YAML. ويجب أن يطابق اسم الدالة المعرَّفة في التهيئة المُولَّدة الاسم الذي يجري إنشاؤه. وإذا أنهى create_command التنفيذ بحالة غير صفرية، يفشل statement مع Exception تتضمن رمز الخروج والخطأ القياسي لبرنامج التشغيل.
ويُستدعى drop_command، عند وجوده، بالطريقة نفسها (ولكن من دون متن شيفرة على stdin) عند حذف الدالة.
إنشاء function
CREATE [OR REPLACE] FUNCTION [IF NOT EXISTS] name [ON CLUSTER cluster]
ARGUMENTS (a UInt8, b String) RETURNS UInt64
ENGINE = DriverName(key1 = 'value1', key2 = 42)
AS '...code body...'يشغّل ClickHouse الأمر create_command الخاص ببرنامج التشغيل، ويكتب التهيئة الناتجة إلى dynamic_user_defined_executable_functions_path، ثم يكتشفها مُحمِّل UDF التنفيذي الحالي. بعد ذلك يمكن استدعاء الدالة مثل أي دالة أخرى.
حذف دالة
DROP FUNCTION [IF EXISTS] name [ON CLUSTER cluster]يستدعي DROP FUNCTION الأمر drop_command الخاص ببرنامج التشغيل (إن وُجد)، ويزيل التهيئة الديناميكية المُنشأة ودليل العمل الخاص بكل دالة، ويُعيد تحميل مُحمِّل UDF التنفيذي، ويزيل الاستعلام المحفوظ.
الاستمرارية وإعادة التشغيل
يُحفَظ الاستعلام الأصلي على هيئة عبارة ATTACH FUNCTION ... في دليل كائنات SQL المعرَّفة من قبل المستخدم، بحيث تبقى الدالة بعد إعادة تشغيل الخادم. عند بدء التشغيل، تُحمَّل التهيئات المُولَّدة في dynamic_user_defined_executable_functions_path مباشرةً من دون إعادة تشغيل برنامج التشغيل. وإذا لم تكن لعبارة ATTACH FUNCTION المحفوظة تهيئة مُولَّدة مطابقة (على سبيل المثال، إذا فُقد الدليل الديناميكي)، فستُعاد تشغيل برنامج التشغيل لإعادة إنشائها.
القيود
- هذه الميزة تجريبية ولا تتاح إلا عند تفعيل
allow_experimental_executable_udf_drivers. - الدوال المستندة إلى برامج التشغيل غير مدعومة مع التخزين المُكرَّر للدوال المعرّفة من قبل المستخدم (
ON CLUSTERو<user_defined_zookeeper_path>)، لأن الاستعلام الأصلي فقط هو الذي يُكرَّر، وليس الملفات الناتجة. - تؤدي عملية
RESTOREلدالة مستندة إلى برنامج تشغيل جرى أخذ نسخة احتياطية لها إلى الاحتفاظ بالاستعلام، لكنها لا تعيد تشغيل برنامج التشغيل؛ إذ تُطبَّق التهيئة المُولَّدة وتُحفَظ لاحقًا أثناء استرداد إعادة التشغيل.
مثال على برامج تشغيل C
تتضمن شجرة المصدر برامج تشغيل تجريبية لإثبات المفهوم ضمن programs/server/user_defined_executable_function_drivers_config.d/، وهي تُترجم جسم دالة بلغة C وتُشغّله. وهذه مجرد أمثلة ولا تُثبَّت عبر الحزم:
DockerC- يترجم الشيفرة ويشغّلها داخل حاويات Docker معزولة (--network=none --read-only --cap-drop=ALL --security-opt=no-new-privileges، بالإضافة إلى حدود الذاكرة/CPU/PID)، ويُنتج UDF من النوعexecutable_pool.GVisorC- نسخة بديلة تشغّل الملف التنفيذي المترجم ضمن بيئة التشغيل gVisorrunsc.UnsafeC- يترجم الشيفرة ويشغّلها مباشرة على المضيف من دون sandbox. وكما يوحي الاسم، فهو لا يوفّر أي عزل، وهو مخصّص فقط للبيئات الموثوقة ولأغراض الاختبار.
صُمِّمت برامج التشغيل التجريبية هذه كنقطة بداية؛ لذا راجع آليات العزل وعزّزها بما يناسب بيئتك قبل إتاحتها لمستخدمين غير موثوقين.