
# Драйвери баз даних

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.
```

## Інтерфейс Driver

Кожен драйвер реалізує п'ять методів:

```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:

- **Snapshotter** використовує `listTables`, `readSchema` і `selectAll` для фіксації стану бази даних.
- **Applier** використовує `readForeignKeys` для визначення порядку операцій і передає драйвер до `SqlGenerator` для екранування ідентифікаторів.
- **SqlGenerator** використовує `quoteIdentifier` для формування безпечних SQL.

Якщо передати з'єднання до `Snapshotter` або `Applier` без указання драйвера, `DriverFactory::create()` викликається автоматично.
