> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-trino-dialect.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> توثيق الدوال المعرّفة من قبل المستخدم في WebAssembly

# الدوال المعرّفة من قبل المستخدم في WebAssembly

export const ExperimentalBadge = () => {
  return <a href="https://clickhouse.com/docs/reference/settings/beta-and-experimental-features#experimental-features" className="experimentalBadge">
            <div className="experimentalIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.25" d="M5.5 2H10.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M9.50015 2V6.19625L13.4283 12.7425C13.4738 12.8183 13.4985 12.9049 13.4996 12.9934C13.5008 13.0818 13.4785 13.169 13.435 13.246C13.3914 13.323 13.3283 13.3871 13.2519 13.4317C13.1755 13.4764 13.0886 13.4999 13.0002 13.5H3.00015C2.91164 13.5 2.8247 13.4766 2.74822 13.432C2.67174 13.3874 2.60847 13.3233 2.56487 13.2463C2.52126 13.1693 2.49889 13.082 2.50004 12.9935C2.50119 12.905 2.52582 12.8184 2.5714 12.7425L6.50015 6.19625V2" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M4.47656 9.56754C5.30344 9.41254 6.47656 9.47942 7.99969 10.25C10.0153 11.2707 11.4216 11.0569 12.2184 10.7282" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            ميزة تجريبية
        </a>;
};

export const CloudNotSupportedBadge = () => {
  return <a href="https://clickhouse.com/docs/products/cloud/guides/cloud-compatibility#list-of-unsupported-features" className="cloudNotSupportedBadge">
            <div className="cloudNotSupportedIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.5" d="M6.33366 12.6666L12.3739 12.6667C13.6593 12.6667 14.7073 11.6187 14.7073 10.3334C14.7073 9.04804 13.6593 8.00003 12.3739 8.00003C12.3739 8.00003 12.3337 7.66659 12.0003 7.33325M10.667 5.33322C8.00033 2.33325 4.45395 4.78537 4.14195 6.68203C2.55728 6.7627 1.29395 8.06203 1.29395 9.6667C1.29395 11.3234 2.66699 12.6666 4.00033 12.6666" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.5" d="M2.66699 14L12.0003 4.66663" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>

        </div>
            غير مدعوم في ClickHouse Cloud
        </a>;
};

يدعم ClickHouse إنشاء الدوال المعرّفة من قبل المستخدم (UDFs) والمكتوبة بلغة WebAssembly. يتيح لك ذلك تنفيذ منطق مخصص مكتوب بلغات مثل Rust أو C أو C++ أو غيرها، عبر تجميعه في وحدات WebAssembly.

<CloudNotSupportedBadge />

<ExperimentalBadge />

<div id="overview">
  ## نظرة عامة
</div>

وحدة WebAssembly هي ملف ثنائي مُجمَّع يحتوي على دالة واحدة أو أكثر يمكن استدعاؤها من ClickHouse.
يمكنك التفكير في الوحدة على أنها مكتبة أو كائن مشترك تُحمّله مرة واحدة ثم تعيد استخدامه مرات عديدة.

يمكن كتابة وحدة WebAssembly التي تحتوي على UDFs بأي لغة يمكن تجميعها إلى WebAssembly، مثل Rust أو C أو C++.

يعمل الكود المُجمَّع إلى WebAssembly ("شيفرة الضيف") والذي ينفّذه ClickHouse ("host") داخل بيئة معزولة لا تتيح الوصول إلا إلى مساحة ذاكرة مخصصة.

يُصدِّر كود الضيف دوالًا يمكن لـ ClickHouse استدعاؤها، وتشمل هذه الدوال تلك التي تنفّذ منطقك المخصص (المستخدم لتعريف UDFs)، بالإضافة إلى دوال الدعم المطلوبة لإدارة الذاكرة وتبادل البيانات بين ClickHouse وكود WebAssembly.

يجب تجميع الكود الخاص بك إلى WebAssembly "freestanding" (المعروف أيضًا باسم `wasm32-unknown-unknown`) من دون أي تبعيات على نظام تشغيل أو مكتبة قياسية. كما لا يكون مدعومًا إلا هدف WebAssembly الافتراضي ذو 32 بت (من دون امتداد `wasm64`).
يجب أن تلتزم الوحدة بأحد بروتوكولات الاتصال (ABIs) المدعومة للتفاعل مع ClickHouse.

بعد التجميع، يُحمَّل الكود الثنائي للوحدة إلى ClickHouse عبر إدراجه في جدول `system.webassembly_modules`.
بعد ذلك، يمكنك إنشاء UDFs تشير إلى الدوال التي تُصدِّرها الوحدة باستخدام العبارة `CREATE FUNCTION ... LANGUAGE WASM`.

<div id="prerequisites">
  ## المتطلبات الأساسية
</div>

فعِّل دعم WebAssembly في إعدادات ClickHouse الخاصة بك:

```xml theme={null}
<clickhouse>
    <allow_experimental_webassembly_udf>true</allow_experimental_webassembly_udf>
    <webassembly_udf_engine>wasmtime</webassembly_udf_engine>
</clickhouse>
```

التنفيذات المتاحة لـ Engine:

* `wasmtime` (الافتراضي، والموصى به) — يستخدم [WasmTime](https://github.com/bytecodealliance/wasmtime)
* `wasmedge` — يستخدم [WasmEdge](https://github.com/WasmEdge/WasmEdge)

<div id="quick-start">
  ## البدء السريع
</div>

يوضح هذا المثال سير العمل الكامل لإنشاء WebAssembly UDF من خلال تنفيذ حاسبة [حدسية Collatz](https://en.wikipedia.org/wiki/Collatz_conjecture).

سنكتب الشيفرة بتنسيق WebAssembly Text ‏(WAT)، وهو تمثيل مقروء للبشر لـ WebAssembly، لذا لا نحتاج إلى أي لغة برمجة في هذه المرحلة.
يتطلب ClickHouse أن تكون الوحدة بتنسيق ثنائي، لذا سنستخدم المُحوِّل لتحويل WAT إلى WASM.
لإجراء هذا التحويل، يمكنك استخدام `wat2wasm` من [WebAssembly Binary Toolkit (WABT)](https://github.com/WebAssembly/wabt) أو الأمر `parse` من [wasm-tools](https://github.com/bytecodealliance/wasm-tools).

```bash theme={null}
cat << 'EOF' | wasm-tools parse | clickhouse client -q "INSERT INTO system.webassembly_modules (name, code) SELECT 'collatz', code FROM input('code String') FORMAT RawBlob"
(module
  (func $next (param $n i32) (result i32)
    local.get $n i32.const 1 i32.and
    (if (result i32)
      (then local.get $n i32.const 3 i32.mul i32.const 1 i32.add)
      (else local.get $n i32.const 2 i32.div_u)))
  (func $steps (export "steps") (param $n i32) (result i32)
    (local $count i32)
    local.get $n i32.const 1 i32.lt_u
    (if (then i32.const 0 return))
    (block $done (loop $loop
      local.get $n i32.const 1 i32.eq br_if $done
      local.get $n call $next local.set $n
      local.get $count i32.const 1 i32.add local.set $count
      br $loop))
    local.get $count)
)
EOF
```

في المقتطف أعلاه، نمرّر شيفرة WASM الثنائية مباشرةً إلى عميل ClickHouse باستخدام `FORMAT RawBlob` لإدراجها في جدول `system.webassembly_modules`.

ثم نعرّف دالة UDF تشير إلى الدالة `steps` التي تصدّرها هذه الوحدة:

```sql theme={null}
CREATE FUNCTION collatz_steps LANGUAGE WASM ARGUMENTS (n UInt32) RETURNS UInt32 FROM 'collatz' :: 'steps';
```

لاحظ أننا نحدّد اسم الدالة من الوحدة بعد `::`، لأنه يختلف عن اسم UDF.

يمكننا الآن استخدام الدالة `collatz_steps` في استعلاماتنا:

```sql theme={null}
SELECT groupArray(collatz_steps(number :: UInt32))
FROM numbers(1, 100)
FORMAT TSV
```

يُحوَّل العمود `number` صراحةً إلى `UInt32`، لأن دوال WebAssembly تتطلب تطابقًا تامًا مع أنواع البيانات المحددة في التوقيع ضمن تعليمة `CREATE FUNCTION`.

في النتيجة، حصلنا على متتالية خطوات Collatz للأعداد من 1 إلى 100، وهي تقابل المتتالية [A006577 from the OEIS](https://oeis.org/A006577).

```text theme={null}
[0,1,7,2,5,8,16,3,19,6,14,9,9,17,17,4,12,20,20,7,7,15,15,10,23,10,111,18,18,18,106,5,26,13,13,21,21,21,34,8,109,8,29,16,16,16,104,11,24,24,24,11,11,112,112,19,32,19,32,19,19,107,107,6,27,27,27,14,14,14,102,22,115,22,14,22,22,35,35,9,22,110,110,9,9,30,30,17,30,17,92,17,17,105,105,12,118,25,25,25]
```

<div id="manage-wasm-modules-via-system-table">
  ## إدارة وحدات WASM عبر جدول النظام
</div>

تُخزَّن وحدات WebAssembly في جدول `system.webassembly_modules` بالبنية التالية:

* **الأعمدة**
  * `name` String — اسم الوحدة. يجب ألا يكون فارغًا، وأن يقتصر على محارف الكلمات فقط.
  * `code` String — شيفرة WASM الثنائية الخام. للكتابة فقط؛ وتُرجع عمليات القراءة سلسلة فارغة.
  * `hash` UInt256 — قيمة SHA256 للملف الثنائي للوحدة (تكون صفرًا إذا كانت موجودة على القرص ولكن لم تُحمَّل بعد).

تتم إدارة الوحدات عبر عمليات SQL القياسية على هذا الجدول:

<div id="insert-a-module">
  ### إضافة وحدة
</div>

```sql theme={null}
INSERT INTO system.webassembly_modules (name, code)
SELECT 'my_module', base64Decode('AGFzbQEAAAA...');
```

اختياريًا، قدّم قيمة تجزئة للتحقق من السلامة:

```sql theme={null}
INSERT INTO system.webassembly_modules (name, code, hash)
SELECT 'my_module', base64Decode('...'), reinterpretAsUInt256(unhex('369f...c57d'));
```

إذا لم تتطابق قيمة hash المُقدَّمة مع قيمة SHA256 المحسوبة لشفرة الوحدة، فستفشل عملية الإدراج. وقد يكون ذلك مفيدًا عند جلب الوحدات من مصادر خارجية مثل S3 أو HTTP.

<div id="distribute-a-module-across-a-cluster">
  ### توزيع وحدة عبر عنقود
</div>

`system.webassembly_modules` هو جدول خاص بكل مثيل — لا تصل عملية `INSERT` إلا إلى النسخة المتماثلة التي تتولى الاتصال. ولا توجد صيغة `ON CLUSTER` لتعليمة `INSERT`، لذا سيفشل `CREATE FUNCTION ... ON CLUSTER` لاحقًا على النسخ المتماثلة التي لا تحتوي على الوحدة:

```text theme={null}
Code: 674. DB::Exception: WebAssembly module 'collatz' not found:
while adding user defined function `collatz_steps`. (RESOURCE_NOT_FOUND)
```

لتوزيع عملية `insert` على جميع العقد، اكتب إلى دالة الجدول `cluster` بدلًا من الجدول المحلي `system.webassembly_modules`:

```bash theme={null}
cat collatz.wasm | clickhouse client -q "
  INSERT INTO FUNCTION cluster('default', 'system', 'webassembly_modules') (name, code)
  SELECT 'collatz', code FROM input('code String') FORMAT RawBlob"
```

<Note>
  يعتمد هذا النمط على أن يمرّ مسار الكتابة الموزعة الأساسي على كل replica داخل كل shard، وهذا لا يحدث إلا عندما يكون العنقود مضبوطًا على `internal_replication=false`. عند استخدام `internal_replication=true` (وهو الإعداد `default` في العناقيد التي تستخدم `ReplicatedMergeTree` لتتولى النسخ المتماثل بنفسها)، تُرسَل عملية `insert` إلى replica سليمة واحدة فقط لكل shard، ولا يجري نسخ `system.webassembly_modules` عبر هذا المسار — لذا ستبقى بعض الـ replicas من دون الوحدة. في هذا الإعداد، تحتاج إلى تنفيذ `insert` على كل replica على حدة، على سبيل المثال بالتكرار على `system.clusters` والكتابة باستخدام `remote(...)` لكل host، أو بنسخ الملف `binary` إلى `user_scripts/wasm/` على كل host.

  يمكنك التحقق من `internal_replication` لعنقود معيّن باستخدام `SELECT cluster, shard_num, internal_replication FROM system.clusters`.
</Note>

بعد تنفيذ `insert` المتشعب، تصبح الوحدة موجودة على كل replica وينجح `CREATE FUNCTION ... ON CLUSTER`:

```sql theme={null}
CREATE FUNCTION collatz_steps ON CLUSTER 'default'
LANGUAGE WASM FROM 'collatz' :: 'steps'
ARGUMENTS (n UInt32) RETURNS UInt32;
```

يمكنك التحقق من تحميل الوحدة على جميع النسخ المتماثلة باستخدام `clusterAllReplicas`:

```sql theme={null}
SELECT hostName(), name FROM clusterAllReplicas('default', system.webassembly_modules) WHERE name = 'collatz';
```

تكون عمليات الإدراج في `system.webassembly_modules` غير مؤثرة عند تكرارها بالنسبة إلى زوج `(name, hash)` نفسه، لذا فإن إعادة تنفيذ عملية الإدراج الموزعة آمنة وتُعد وسيلة مناسبة لإصلاح الحالة بعد استبدال نسخة متماثلة. لاحظ أن الخوادم المُضافة حديثًا لا تتلقى الوحدات الموجودة بأثر رجعي — يجب عليك إعادة تنفيذ عملية الإدراج على العنقود المُحدَّث، أو وضع الملف التنفيذي في الدليل `user_scripts/wasm/` على المضيف الجديد.

<div id="list-modules">
  ### عرض الوحدات
</div>

```sql theme={null}
SELECT name, lower(hex(reinterpretAsFixedString(hash))) AS sha256 FROM system.webassembly_modules

   ┌─name────┬─sha256───────────────────────────────────────────────────────────┐
1. │ collatz │ a084a10b7b5cb07db198bc93bf1f3c1f8cb8ef279df7a4f6b66b1cdd55d79c48 │
   └─────────┴──────────────────────────────────────────────────────────────────┘
```

<div id="delete-a-module">
  ### حذف وحدة
</div>

يتم الحذف باستخدام العبارة `DELETE FROM system.webassembly_modules WHERE name = '...'`.
يجب أن يكون شرط التصفية إما `name = 'literal'` لإجراء تطابق تام، أو `name LIKE 'pattern'` لحذف كل وحدة يطابق اسمها النمط؛ ولا تُقبل أي صيغ أخرى.

```sql theme={null}
DELETE FROM system.webassembly_modules WHERE name = 'collatz';

-- Bulk-delete every module whose name starts with `tmp_` (literal underscore is escaped as `\_`):
DELETE FROM system.webassembly_modules WHERE name LIKE 'tmp\_%';
```

إذا كان أي من UDFs الحالية يشير إلى إحدى الوحدات المطابقة، فسيفشل الحذف، لذا يجب عليك حذف تلك الـ UDFs أولاً.

<div id="create-a-webassembly-udf">
  ## إنشاء دالة UDF باستخدام WebAssembly
</div>

**الصيغة**:

```sql theme={null}
CREATE [OR REPLACE] FUNCTION function_name
LANGUAGE WASM
FROM 'module_name' [:: 'source_function_name']
ARGUMENTS ( [name type[, ...]] | [type[, ...]] )
RETURNS return_type
[ABI ROW_DIRECT | ABI BUFFERED_V1 | ABI ASSEMBLYSCRIPT]
[DETERMINISTIC]
[SHA256_HASH 'hex']
[SETTINGS key = value[, ...]];
```

**المعلمات**:

* `function_name`: اسم الدالة في ClickHouse. قد يختلف عن اسم الدالة المُصدَّرة في الوحدة.
* `FROM 'module_name' :: 'source_function_name'`: اسم وحدة WASM المُحمَّلة واسم الدالة في وحدة WASM المراد استخدامها (تكون القيمة الافتراضية `function&#95;name`)
* `ARGUMENTS`: قائمة بأسماء الوسيطات وأنواعها (الأسماء اختيارية وتُستخدم مع تنسيقات التسلسل التي تدعم الحقول المُسمّاة)
* `ABI`: إصدار واجهة التطبيق الثنائية
  * `ROW_DIRECT`: مواءمة مباشرة للأنواع، مع معالجة صفًا بصف
  * `BUFFERED_V1`: معالجة قائمة على الكتل مع التسلسل
  * `ASSEMBLYSCRIPT`: معالجة صفًا بصف للوحدات الناتجة عن مصرّف [AssemblyScript](https://www.assemblyscript.org). تُطابَق الأنواع الرقمية مع الأنواع البدائية في AssemblyScript؛ ويُطابَق `String` في ClickHouse مع `string` في AssemblyScript.
* `DETERMINISTIC`: يصرّح بأن الدالة حتمية — أي إنها تعيد دائمًا الناتج نفسه للمدخلات نفسها. عند تحديد ذلك، قد يطوي ClickHouse الاستدعاءات الثابتة التي تكون فيها جميع الوسيطات ثوابت: تُقيَّم الدالة مرة واحدة في مرحلة تحليل الاستعلام، ثم يُعاد استخدام النتيجة لكل صف.
* `SHA256_HASH`: قيمة التجزئة المتوقعة للوحدة للتحقق منها (تُملأ تلقائيًا إذا أُهملت)، ويمكن استخدامها لضمان تحميل وحدة WASM الصحيحة عبر النسخ المتماثلة المختلفة.
* `SETTINGS`: إعدادات خاصة بكل دالة
  * `serialization_format` String — التنسيق المستخدم لتسلسل كتل الوسيطات المُمرّرة إلى الوحدة وتحليل النتيجة المُعادة. يُستخدم فقط بواسطة `ABI BUFFERED_V1`. القيم المدعومة: `MsgPack` و`JSONEachRow` و`CSV` و`TSV` و`TSVRaw` و`RowBinary` و`Buffers`. القيمة الافتراضية: `MsgPack`. يجب أن تُرجع التنسيقات المعتمدة على الكتل مثل `Buffers` عمودًا واحدًا يطابق نوعه توقيع الدالة المُصرَّح به.
  * `webassembly_udf_enable_fuel` Bool — يفعّل حصة وقود محدودة للدالة. القيمة الافتراضية: `true`. عند ضبطه على `false`، يُتجاهل إعداد الاستعلام `webassembly_udf_max_fuel` لهذه الدالة. قد يؤدي تعطيل حدود الوقود إلى تحسين الأداء عند استخدام المحرك `wasmtime`. ومع ذلك، بالنسبة إلى شيفرة الضيف غير الموثوق بها أو التي تحتوي على أخطاء، فقد يزيد ذلك من خطر استمرار التنفيذ بلا قيود.

<div id="abis-versions">
  ## إصدارات ABI
</div>

للتفاعل مع ClickHouse، يجب أن تلتزم وحدات WebAssembly بإحدى واجهات التطبيق الثنائية (ABI) المدعومة.

* `ROW_DIRECT`: تعيين مباشر للأنواع (الأنواع البدائية `Int32` و`UInt32` و`Int64` و`UInt64` و`Float32` و`Float64` فقط)
* `BUFFERED_V1`: أنواع معقدة مع دعم التسلسل
* `ASSEMBLYSCRIPT`: تكامل صفًا بصف مع وحدات [AssemblyScript](https://www.assemblyscript.org)؛ يدعم الأنواع الرقمية و`String`.

<div id="abi-row_direct">
  ### ABI ROW\_DIRECT
</div>

يستدعي دالة WASM مُصدَّرة مباشرةً لكل صف.

* يجب أن تكون الوسائط وأنواع الإرجاع من الأنواع الرقمية `Int32/UInt32/Int64/UInt64/Float32/Float64/Int128/UInt128`.
* النوع Strings غير مدعوم في واجهة ABI هذه.
* يجب أن تتطابق التواقيع مع ما هو مُصدَّر من WASM (`i32/i64/f32/f64/v128`).
* لا يلزم أن يصدّر الوحدة أي دوال دعم.

على سبيل المثال، دالة بالتوقيع التالي:

```
(func (param i32 i64 f32) (result f64) ...)
```

يمكن إنشاؤه على النحو التالي:

```sql theme={null}
CREATE FUNCTION my_func ARGUMENTS (Int32, UInt64, Float32) RETURNS Float64 ...
```

لا يميّز WebAssembly بين الوسيطات الموقَّعة وغير الموقَّعة، بل يستخدم تعليمات مختلفة لتفسير القيم. لذلك، يجب أن يتطابق حجم الوسيطة تمامًا، بينما يتحدد ما إذا كانت موقَّعة أم غير موقَّعة من خلال العمليات داخل الدالة.

<div id="abi-buffered_v1">
  ### ABI BUFFERED\_V1
</div>

<Note>
  واجهة ABI هذه تجريبية وقد تتغير في الإصدارات المستقبلية.
</Note>

يعالج كتلًا كاملة دفعةً واحدة باستخدام (إلغاء) التسلسل عبر ذاكرة WASM. ويدعم أي أنواع للوسيطات وأنواع الإرجاع.

يُتبادل البيانات عبر مخازن مؤقتة في ذاكرة WASM. المخزن المؤقت بنية بحجم 8 بايتات تحتوي على مؤشر إلى البيانات وحجمها (قيمتا `u32` بترتيب little-endian). تُمرَّر المخازن المؤقتة باستخدام مقابض، أي مؤشرات إلى هذه البنية لا إلى البيانات نفسها. يجب أن تصدّر شيفرة الضيف دالتين لإنشاء هذه المخازن المؤقتة وإتلافها.

لكل كتلة إدخال، يقوم ClickHouse بما يلي:

1. يُسلسل أعمدة الوسيطات باستخدام `serialization_format` الخاصة بالدالة (`MsgPack` افتراضيًا). تكتب التنسيقات المعتمدة على الصفوف القيم صفًا تلو الآخر، مع قيم الوسيطات بالترتيب المُعلن في `ARGUMENTS`؛ أما التنسيقات ذات الحقول المسماة فتستخدم أسماء الوسيطات، لذا يجب الإعلان عنها في `ARGUMENTS` عند استخدام هذه التنسيقات.
2. يستدعي `clickhouse_create_buffer` الذي تصدّره الوحدة، ثم ينسخ البيانات المتسلسلة إلى الذاكرة التي يشير إليها المخزن المؤقت المُعاد.
3. يستدعي الدالة المعرّفة من قبل المستخدم بوسيطتي `i32`: مقبض مخزن الإدخال المؤقت (`0` إذا لم تكن للدالة وسائط) وعدد الصفوف. تُرجع الدالة قيمة `i32` واحدة، وهي مقبض مخزن النتائج المؤقت الذي تخصصه شيفرة الضيف بنفسها؛ وتؤدي إعادة `0` إلى فشل الاستعلام مع ظهور خطأ.
4. يقرأ مخزن النتائج المؤقت: يجب أن يحتوي على عمود واحد بالضبط، وبنفس عدد الصفوف تمامًا، ومُسلسَل بالتنسيق نفسه. بالنسبة إلى التنسيقات ذات الحقول المسماة، مثل `JSONEachRow`، يجب أن يكون اسم عمود النتائج `result`.
5. يستدعي `clickhouse_destroy_buffer` لكلٍّ من مقابض مخزن الإدخال المؤقت (إن وُجد) ومخزن النتائج المؤقت. يجب ألا تحرر شيفرة الضيف مخزن النتائج المؤقت بنفسها، وألا تعيد مقبض الإدخال كنتيجة، وإلا فسيُتلَف مرتين.

تُستدعى الدالة مرة واحدة لكل كتلة إدخال: يُقسَّم الاستعلام الكبير إلى عدة كتل بواسطة مسار تنفيذ الاستعلام (ويمكن أيضًا تقييد الحد الأقصى لعدد الصفوف لكل استدعاء باستخدام الإعداد `webassembly_udf_max_input_block_size`)، لذا فإن عدد الصفوف المُمرَّر في كل استدعاء هو حجم تلك الكتلة، وليس حجم الاستعلام بأكمله.

<Note>
  تحدد التنسيقات المختارة البايتات الدقيقة داخل المخازن المؤقتة، ويجب أن تفك شيفرة الضيف ترميزها وتعيد ترميزها بنفسها. وبشكل خاص، بالنسبة إلى قيم `String`:

  * `MsgPack`: يكتب ClickHouse قيم `String` باستخدام عائلة الأنواع `bin` (`0xc4`/`0xc5`/`0xc6`) وليس عائلة `str`. تُقبل كلتا العائلتين عند تحليل النتيجة.
  * `RowBinary`: تُسبق كل قيمة `String` بطولها بالبايتات على هيئة varint غير موقّع ([LEB128](https://en.wikipedia.org/wiki/LEB128))، وليس عددًا صحيحًا بعرض ثابت.
  * `JSONEachRow`: يجب أن يكون كل صف نتائج كائنًا يتضمن المفتاح `result`، مثلًا `{"result":"value"}`.
</Note>

```
(module
  ;; Allocate a new buffer of specified size
  ;; Returns: handle to Buffer structure (not direct data pointer!) with pointer to data and size
  (func (export "clickhouse_create_buffer")
    (param $size i32)    ;; Size of data to allocate
    (result i32))        ;; Returns buffer handle with enough space

  ;; Free a buffer by its handle
  (func (export "clickhouse_destroy_buffer")
    (param $handle i32)  ;; Buffer handle to free
    (result))            ;; No return value

    ;; User-defined function
    (func (export "user_defined_function1")
      (param $input_buffer_handle i32)  ;; Input buffer handle
      (param $n i32)                    ;; Number of rows in input
      (result i32))                     ;; Returns output buffer handle
)
```

مثال كامل بلغة C المستقلة ذاتيًا: تعكس `str_reverse` بايتات كل سلسلة إدخال باستخدام `serialization_format = 'RowBinary'`. يُعاد استخدام مثيلات الوحدة عبر الكتل، لذا يجب أن تحرر `clickhouse_destroy_buffer` الذاكرة فعليًا — وهنا يُعاد ضبط المُخصِّص بعد إتلاف جميع المخازن المؤقتة.

```c theme={null}
#include <stdint.h>

typedef struct {
    uint8_t * data;
    uint32_t size;
} ClickHouseBuffer;

#define HEAP_SIZE (1 << 24)
static _Alignas(16) uint8_t heap[HEAP_SIZE];
static uint32_t heap_pos = 0;
static uint32_t live_buffers = 0;

__attribute__((export_name("clickhouse_create_buffer")))
ClickHouseBuffer * clickhouse_create_buffer(uint32_t size)
{
    uint32_t total = (sizeof(ClickHouseBuffer) + size + 15u) & ~15u;
    if (heap_pos + total > HEAP_SIZE)
        return 0; /* a zero handle makes the host fail the query with an error */
    ClickHouseBuffer * buf = (ClickHouseBuffer *) &heap[heap_pos];
    buf->data = &heap[heap_pos + sizeof(ClickHouseBuffer)];
    buf->size = size;
    heap_pos += total;
    ++live_buffers;
    return buf;
}

__attribute__((export_name("clickhouse_destroy_buffer")))
void clickhouse_destroy_buffer(ClickHouseBuffer * buf)
{
    if (--live_buffers == 0)
        heap_pos = 0;
}

/* RowBinary prefixes each String with its byte length as an unsigned varint (LEB128) */
static uint64_t read_varint(const uint8_t ** pp)
{
    uint64_t value = 0;
    for (int shift = 0;; shift += 7)
    {
        uint8_t b = *(*pp)++;
        value |= (uint64_t)(b & 0x7f) << shift;
        if (!(b & 0x80))
            return value;
    }
}

static void write_varint(uint8_t ** pp, uint64_t value)
{
    while (value >= 0x80)
    {
        *(*pp)++ = (uint8_t)(value | 0x80);
        value >>= 7;
    }
    *(*pp)++ = (uint8_t)value;
}

/* Reverses the bytes of each input string */
__attribute__((export_name("str_reverse")))
ClickHouseBuffer * str_reverse(ClickHouseBuffer * input, uint32_t num_rows)
{
    ClickHouseBuffer * out = clickhouse_create_buffer(input->size);
    if (!out)
        return 0;
    const uint8_t * in = input->data;
    uint8_t * o = out->data;
    for (uint32_t row = 0; row < num_rows; ++row)
    {
        uint64_t len = read_varint(&in);
        write_varint(&o, len);
        for (uint64_t i = 0; i < len; ++i)
            o[i] = in[len - 1 - i];
        in += len;
        o += len;
    }
    out->size = (uint32_t)(o - out->data);
    return out;
}
```

أنشئ باستخدام clang و`wasm-ld` (المرفق مع LLVM/lld):

```bash theme={null}
clang --target=wasm32 -ffreestanding -nostdlib -fno-builtin -c str_reverse.c
wasm-ld --no-entry str_reverse.o -o str_reverse.wasm
```

يجب أن تكون الدالات ظاهرة ضمن صادرات الوحدة: إما بتمييزها باستخدام `__attribute__((export_name("...")))` كما هو موضح أعلاه، أو بالربط باستخدام `wasm-ld --export-all`. يمنع الخيار `-fno-builtin` ‏clang من تحويل حلقات البايت البسيطة إلى استدعاءات لـ `memcpy`/`memset`، وهما غير متاحين دون مكتبة قياسية.

حمّل الوحدة وأنشئ الدالة:

```bash theme={null}
cat str_reverse.wasm | clickhouse client -q "INSERT INTO system.webassembly_modules (name, code) SELECT 'str_reverse', code FROM input('code String') FORMAT RawBlob"
```

```sql theme={null}
CREATE FUNCTION str_reverse LANGUAGE WASM ABI BUFFERED_V1
FROM 'str_reverse' :: 'str_reverse'
ARGUMENTS (s String) RETURNS String
SETTINGS serialization_format = 'RowBinary';

SELECT str_reverse(toString(number + 100)) FROM numbers(3);
```

```text theme={null}
001
101
201
```

<div id="abi-assemblyscript">
  ### ABI ASSEMBLYSCRIPT
</div>

يستهدف الوحدات التي يُنتجها [AssemblyScript](https://www.assemblyscript.org) مصرّف. ويؤدي كل صف إلى استدعاء واحد للدالة المُصدَّرة، مع مواءمة قيم ClickHouse مع الأنواع البدائية وكائنات السلاسل النصية في AssemblyScript.

**الأنواع المدعومة**:

* رقمية: `Int8`/`UInt8`، `Int16`/`UInt16` (تُوسَّع إلى `i32` عند الحد الفاصل)، `Int32`/`UInt32`، `Int64`/`UInt64`، `Float32`، `Float64`

* `String` — تُحوَّل إلى `string` في AssemblyScript ‏(UTF-16 في ذاكرة WASM). ويتولى ClickHouse التحويل بين UTF-8 وUTF-16 تلقائيًا.

* لا تُدعَم فئات AssemblyScript المخصّصة كأنواع للوسائط أو الإرجاع، لأن معرّفات الفئات في بيئة التشغيل الخاصة بها ليست مستقرة عبر عمليات الترجمة البرمجية (راجع [AssemblyScript#2982](https://github.com/AssemblyScript/assemblyscript/issues/2982)).

**متطلبات الوحدة**:

يجب ترجمة الوحدة برمجيًا باستخدام بيئة التشغيل المُدارة في AssemblyScript، بحيث يتم تصدير `__new` و`__pin` و`__unpin`. وتعتمد عليها الآلية القياسية للتعامل مع السلاسل النصية الواردة والصادرة. الاستدعاء الموصى به:

```bash theme={null}
asc src.ts --runtime incremental --exportRuntime -o src.wasm
```

يستورد AssemblyScript أيضًا `env.abort` لأخطاء وقت التشغيل (مثل نفاد الذاكرة وتجاوز الحدود وما إلى ذلك). ويوفّر ClickHouse هذا الاستيراد تلقائيًا: فعند استدعاء `abort`، يفشل الاستعلام الجاري مع استثناء `WASM_ERROR` يتضمّن رسالة AssemblyScript بعد فك ترميزها وموضع المصدر.

**مثال**:

```typescript theme={null}
// src.ts
export function add(a: u32, b: u32): u32 {
  return a + b;
}

export function greet(name: string): string {
  return "Hello, " + name + "!";
}
```

بعد إجراء التحويل البرمجي باستخدام `asc` وتحميل ملف `.wasm` الناتج إلى `system.webassembly_modules`، عرّف UDFs كما يلي:

```sql theme={null}
CREATE FUNCTION as_add
    LANGUAGE WASM ABI ASSEMBLYSCRIPT
    FROM 'as_example' :: 'add'
    ARGUMENTS (a UInt32, b UInt32) RETURNS UInt32;

CREATE FUNCTION as_greet
    LANGUAGE WASM ABI ASSEMBLYSCRIPT
    FROM 'as_example' :: 'greet'
    ARGUMENTS (name String) RETURNS String;
```

<div id="note-for-developing-udfs-in-rust">
  ### ملاحظة حول تطوير UDFs في Rust
</div>

بالنسبة إلى برامج Rust، نوفر crate مساعدة [clickhouse-wasm-udf](https://crates.io/crates/clickhouse-wasm-udf) لتبسيط تطوير WebAssembly UDFs لـ ClickHouse. توفّر هذه الـ crate دوالًا لإدارة الذاكرة، لذلك لا تحتاج إلى تنفيذ الدالتين `clickhouse_create_buffer` و`clickhouse_destroy_buffer` يدويًا، وإنما يكفي إضافة الـ crate كاعتمادية. كما تتوفر macro ‏`#[clickhouse_wasm_udf]` لتغليف دوال Rust العادية بالصيغة المطلوبة لـ ABI.

وباستخدام هذه الـ crate، يمكنك كتابة UDFs على النحو التالي:

```rust theme={null}

use clickhouse_wasm_udf_bindgen::clickhouse_udf;

#[clickhouse_udf]
pub fn some_udf(data: String) -> HashMap<String, String> {
    // Your implementation here
}

```

ستولِّد وحدات الماكرو دالةً مغلِّفةً تقبل بُنى المخزن المؤقت وتُعيدها، وتتولى تلقائيًا عمليتَي التسلسل وفك التسلسل باستخدام `serde`.

<div id="host-api-available-to-modules">
  ## واجهة برمجة التطبيقات المضيفة المتاحة للوحدات
</div>

يمكن للوحدات استيراد دوال المضيف التالية واستخدامها:

* `clickhouse_server_version() -> i64` — تُرجع إصدار ClickHouse server كعدد صحيح (على سبيل المثال 25011001 للإصدار v25.11.1.1).
* `clickhouse_throw(ptr: i32, size: i32)` — يُطلق خطأً بالرسالة المحددة. يقبل مؤشراً إلى موقع الذاكرة الذي يحتوي على سلسلة رسالة الخطأ، بالإضافة إلى حجم السلسلة.
* `clickhouse_log(ptr: i32, size: i32)` — يسجل رسالة في السجل النصي لـ ClickHouse server.
* `clickhouse_random(ptr: i32, size: i32)` — يملأ الذاكرة ببايتات عشوائية.
* `env.abort(message: i32, fileName: i32, line: i32, column: i32)` — متاح للوحدات المتوافقة مع AssemblyScript. يؤدي استدعاؤه (أو التسبب في trap لوقت تشغيل AssemblyScript يستدعيه) إلى إنهاء UDF مع استثناء `WASM_ERROR` يتضمن الرسالة المفككة وموقع المصدر. ولا تتأثر الوحدات التي لا تستورد `env.abort`.

<div id="settings">
  ## الإعدادات
</div>

تتحكم الإعدادات التالية على مستوى الاستعلام في تنفيذ WebAssembly UDF:

* `webassembly_udf_max_fuel` — حد الوقود لكل عملية تنفيذ لمثيل WebAssembly UDF. تستهلك كل تعليمة في WebAssembly مقدارًا من الوقود. تُضرب القيمة في 1024 قبل تمريرها إلى بيئة التشغيل، لذا فإن `webassembly_udf_max_fuel = 1` يعادل تقريبًا 1024 وحدة وقود. عيّنه إلى 0 لإلغاء أي حد فعلي. ينطبق ذلك فقط على الدوال التي تكون قيمة الإعداد الخاص بكل دالة `webassembly_udf_enable_fuel` فيها هي `true`، وهذا هو الإعداد الافتراضي.

* `webassembly_udf_max_memory` — حد الذاكرة بالبايت لكل مثيل WebAssembly UDF.

* `webassembly_udf_max_input_block_size` — الحد الأقصى لعدد الصفوف التي تُمرَّر إلى WebAssembly UDF في كتلة واحدة. عيّنه إلى 0 لمعالجة جميع الصفوف دفعة واحدة.

* `webassembly_udf_max_instances` — الحد الأقصى لعدد مثيلات WebAssembly UDF التي يمكن تشغيلها بالتوازي لكل دالة.

مثال على الاستخدام:

```sql theme={null}
SET webassembly_udf_max_fuel = 200000;
SELECT my_wasm_udf(column) FROM table;
```

<div id="see-also">
  ## انظر أيضًا
</div>

* [نظرة عامة على UDF في ClickHouse](/ar/reference/functions/regular-functions/udf)
