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

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:

- **Създателят на моментни снимки** използва `listTables`, `readSchema` и `selectAll`, за да заснеме състоянието на базата данни.
- **Прилагащият механизъм** използва `readForeignKeys`, за да определи реда на операциите, и подава драйвера на `SqlGenerator` за ограждане на идентификаторите с кавички.
- **Генераторът на SQL** използва `quoteIdentifier`, за да създава безопасни SQL.

Ако подадете връзка на `Snapshotter` или `Applier`, без да зададете драйвер, автоматично се извиква `DriverFactory::create()`.
