
# برامج تشغيل قواعد البيانات

يفصل merql بين نقل الاتصال ولهجة قاعدة البيانات. ينفذ محول `DatabaseConnection` SQL عبر PDO أو mysqli، بينما يتولى `Driver` البيانات الوصفية الخاصة بمحرك قاعدة البيانات SQL واقتباس المعرّفات. تتوفر برامج تشغيل مدمجة لـ MySQL وSQLite.

```php
use Merql\Driver\DriverFactory;

$driver = DriverFactory::create($connection);
// Auto-detects MySQL or SQLite from the connection driver identity.
```

## واجهة برنامج التشغيل

ينفذ كل برنامج تشغيل خمس دوال:

```php
use Merql\Driver\Driver;

interface Driver
{
    public function quoteIdentifier(string $name): string;
    public function listTables(DatabaseConnection $connection): array;
    public function readSchema(DatabaseConnection $connection, string $table): TableSchema;
    public function readForeignKeys(DatabaseConnection $connection): array;
    public function selectAll(string $table): string;
}
```

| الدالة | الغرض |
|---|---|
| `quoteIdentifier` | اقتباس اسم جدول أو عمود لاستخدامه في SQL. |
| `listTables` | إرجاع جميع جداول المستخدم في قاعدة البيانات الحالية. |
| `readSchema` | قراءة أسماء الأعمدة وأنواعها والمفتاح الأساسي والمفاتيح الفريدة لجدول. |
| `readForeignKeys` | إرجاع خريطة تربط الجداول التابعة بالجداول الأصلية. |
| `selectAll` | إنشاء استعلام `SELECT *` لجدول. |

## MysqlDriver

يستخدم استعلامات `INFORMATION_SCHEMA` للبيانات الوصفية، ويقتبس المعرّفات باستخدام العلامة الخلفية.

```php
use Merql\Driver\MysqlDriver;

$driver = new MysqlDriver();

$driver->quoteIdentifier('posts');
// `posts`

$driver->quoteIdentifier('my`table');
// `my``table`

$tables = $driver->listTables($connection);
// ['comments', 'posts', 'users']

$schema = $driver->readSchema($connection, 'posts');
// TableSchema with columns, primaryKey, uniqueKeys

$fks = $driver->readForeignKeys($connection);
// ['comments' => ['posts', 'users']]
```

تستعلم عمليات سرد الجداول عن `INFORMATION_SCHEMA.TABLES` مع التصفية حسب قاعدة البيانات الحالية ونوع `BASE TABLE`. وتستعلم قراءة المخطط عن `INFORMATION_SCHEMA.COLUMNS` للأعمدة و`INFORMATION_SCHEMA.KEY_COLUMN_USAGE` للمفاتيح الأساسية والفريدة.

## SqliteDriver

يستخدم استعلامات `PRAGMA` للبيانات الوصفية، ويقتبس المعرّفات بعلامتي اقتباس مزدوجتين.

```php
use Merql\Driver\SqliteDriver;

$driver = new SqliteDriver();

$driver->quoteIdentifier('posts');
// "posts"

$driver->quoteIdentifier('my"table');
// "my""table"

$tables = $driver->listTables($connection);
// Queries sqlite_master, excludes sqlite_* internal tables.

$schema = $driver->readSchema($connection, 'posts');
// Uses PRAGMA table_info() and PRAGMA index_list().

$fks = $driver->readForeignKeys($connection);
// Uses PRAGMA foreign_key_list() for each table.
```

## DriverFactory

تكتشف `DriverFactory` برنامج التشغيل الصحيح تلقائيًا من `DatabaseConnection::driverName()`.

```php
use Merql\Connection;
use Merql\Driver\DriverFactory;

$connection = Connection::mysql('127.0.0.1', 'mydb', 'root', '');
$driver = DriverFactory::create($connection);
// Returns MysqlDriver

$connection = Connection::sqlite(':memory:');
$driver = DriverFactory::create($connection);
// Returns SqliteDriver
```

إذا لم تُعرف هوية برنامج تشغيل الاتصال، يُطلق `RuntimeException` بالرسالة `Unsupported database driver: <name>`. لا يتضمن merql برنامج تشغيل لـ PostgreSQL.

### تسجيل برامج تشغيل مخصصة

لقواعد البيانات غير MySQL وSQLite، اكتب فئة تنفذ `Driver` وسجّلها. تشير `PostgresDriver` أدناه إلى فئتك:

```php
use Merql\Driver\DriverFactory;

DriverFactory::register('pgsql', PostgresDriver::class);

$connection = Connection::fromDsn('pgsql:host=localhost;dbname=mydb', 'user', 'pass');
$driver = DriverFactory::create($connection);
// Returns your PostgresDriver instance.
```

يجب أن تنفذ فئة برنامج التشغيل واجهة `Driver`. الوسيط الأول لـ `register()` هو هوية برنامج تشغيل الاتصال.

### إعادة التعيين

للاختبار، أعد ضبط المصنع إلى حالته الافتراضية:

```php
DriverFactory::reset();
// Reverts to only MySQL and SQLite drivers.
```

## مساعد الاتصال

تنشئ الفئة `Connection` المحولات بإعدادات افتراضية مناسبة. تستخدم محولات PDO الاستثناءات عند الخطأ، ونمط جلب ترابطيًا، وعبارات معدة حقيقية، وقيمًا مجلبة كسلاسل نصية. يضبط محول mysqli الخيار `utf8mb4`، ويستخدم وضع استثناءات mysqli والعبارات المعدة، ويوحّد القيم المجلبة لتتخذ الشكل نفسه: سلسلة نصية أو null.

```php
use Merql\Connection;

// MySQL.
$connection = Connection::mysql('127.0.0.1', 'my_database', 'root', 'password');
$connection = Connection::mysql('db.example.com', 'mydb', 'user', 'pass', port: 3307);

// MySQLi.
$connection = Connection::mysqli('127.0.0.1', 'my_database', 'root', 'password');

// SQLite.
$connection = Connection::sqlite(':memory:');
$connection = Connection::sqlite('/var/data/app.sqlite');

// Any MySQL or SQLite PDO DSN.
$connection = Connection::fromDsn('mysql:host=127.0.0.1;dbname=mydb;charset=utf8mb4', 'user', 'pass');
```

تقبل `fromDsn()` أي DSN يستطيع PDO فتحه، لكن merql لا يستطيع التقاط اللقطات أو تطبيقها إلا عبر برنامج تشغيل مسجل. يفتح اتصال `pgsql:` عند تثبيت `pdo_pgsql`، لكن منشئي `Merql::init()` و`Snapshotter` و`Applier` و`GuardedApplier` يستدعون `DriverFactory::create()`، الذي يُطلق `RuntimeException: Unsupported database driver: pgsql` ما لم تكن قد [سجلت برنامج تشغيل](#registering-custom-drivers) لهذا الاتصال أو مررت واحدًا.

تمكّن `Connection::sqlite()` أيضًا `PRAGMA foreign_keys = ON` تلقائيًا، وهو ما يتطلبه SQLite لفرض المفاتيح الخارجية.

## مواضع استخدام برامج التشغيل

تُستخدم برامج التشغيل في أنحاء merql:

- **أداة التقاط اللقطات** تستخدم `listTables` و`readSchema` و`selectAll` لالتقاط حالة قاعدة البيانات.
- **أداة التطبيق** تستخدم `readForeignKeys` لتحديد ترتيب العمليات، وتمرر برنامج التشغيل إلى `SqlGenerator` لاقتباس المعرّفات.
- **مولّد SQL** يستخدم `quoteIdentifier` لإنشاء SQL آمن.

إذا مررت اتصالًا إلى `Snapshotter` أو `Applier` دون تحديد برنامج تشغيل، فسيُستدعى `DriverFactory::create()` تلقائيًا.
