
# Sterowniki baz danych

merql oddziela transport połączenia od dialektu bazy danych. Adapter `DatabaseConnection` wykonuje SQL przez PDO lub mysqli, a `Driver` obsługuje metadane specyficzne dla silnika bazy danych SQL oraz cytowanie identyfikatorów. Wbudowane sterowniki są dostępne dla MySQL i SQLite.

```php
use Merql\Driver\DriverFactory;

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

## Interfejs Driver

Każdy sterownik implementuje pięć metod:

```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;
}
```

| Metoda | Zastosowanie |
|---|---|
| `quoteIdentifier` | Cytuje nazwę tabeli lub kolumny do użycia w SQL. |
| `listTables` | Zwraca wszystkie tabele użytkownika w bieżącej bazie danych. |
| `readSchema` | Odczytuje nazwy i typy kolumn, klucz główny oraz klucze unikatowe tabeli. |
| `readForeignKeys` | Zwraca mapę zależności tabel podrzędnych od tabel nadrzędnych. |
| `selectAll` | Buduje zapytanie `SELECT *` dla tabeli. |

## MysqlDriver

Używa zapytań `INFORMATION_SCHEMA` do pobierania metadanych i znaków odwrotnego apostrofu do cytowania identyfikatorów.

```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']]
```

Zapytania o listę tabel pobierają `INFORMATION_SCHEMA.TABLES`, filtrując według bieżącej bazy danych i typu `BASE TABLE`. Zapytania o schemat pobierają `INFORMATION_SCHEMA.COLUMNS` dla kolumn oraz `INFORMATION_SCHEMA.KEY_COLUMN_USAGE` dla kluczy głównych i unikatowych.

## SqliteDriver

Używa zapytań `PRAGMA` do pobierania metadanych i podwójnych cudzysłowów do cytowania identyfikatorów.

```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` automatycznie wykrywa właściwy sterownik na podstawie `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
```

Jeśli tożsamość sterownika połączenia nie zostanie rozpoznana, zgłaszany jest `RuntimeException` z komunikatem `Unsupported database driver: <name>`. merql nie zawiera sterownika PostgreSQL.

### Rejestrowanie niestandardowych sterowników

W przypadku baz danych innych niż MySQL i SQLite napisz klasę implementującą `Driver` i zarejestruj ją. `PostgresDriver` poniżej oznacza Twoją własną klasę:

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

Klasa sterownika musi implementować interfejs `Driver`. Pierwszy argument funkcji `register()` to tożsamość sterownika połączenia.

### Resetowanie

Na potrzeby testów przywróć fabrykę do stanu domyślnego:

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

## Pomocnik połączenia

Klasa `Connection` tworzy adaptery z rozsądnymi ustawieniami domyślnymi. Adaptery PDO zgłaszają wyjątki w razie błędu, używają asocjacyjnego trybu pobierania, natywnych przygotowanych zapytań oraz pobierania wartości jako ciągów znaków. Adapter mysqli ustawia `utf8mb4`, używa trybu wyjątków mysqli i przygotowanych zapytań oraz normalizuje pobrane wartości do tego samego formatu: ciąg znaków lub 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()` akceptuje dowolny DSN, który może otworzyć PDO, ale merql może tworzyć zrzuty i stosować zmiany tylko za pośrednictwem zarejestrowanego sterownika. Połączenie `pgsql:` zostanie otwarte (jeśli zainstalowano `pdo_pgsql`), ale konstruktory `Merql::init()` oraz `Snapshotter`, `Applier` i `GuardedApplier` wywołują `DriverFactory::create()`, które zgłasza `RuntimeException: Unsupported database driver: pgsql`, chyba że [zarejestrowano dla niego sterownik](#registering-custom-drivers) lub przekazano go jawnie.

`Connection::sqlite()` automatycznie włącza też `PRAGMA foreign_keys = ON`, którego SQLite wymaga do egzekwowania kluczy obcych.

## Gdzie używane są sterowniki

Sterowniki są używane w całym merql:

- **Snapshotter** używa `listTables`, `readSchema` i `selectAll` do przechwytywania stanu bazy danych.
- **Applier** używa `readForeignKeys` do określania kolejności operacji i przekazuje sterownik do `SqlGenerator` w celu cytowania identyfikatorów.
- **SqlGenerator** używa `quoteIdentifier` do budowania bezpiecznych zapytań SQL.

Jeśli przekażesz połączenie do `Snapshotter` lub `Applier` bez określenia sterownika, automatycznie wywołana zostanie funkcja `DriverFactory::create()`.
