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

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