
# Databázové ovladače

merql odděluje přenos připojení od dialektu databáze. Adaptér `DatabaseConnection` provádí SQL přes PDO nebo mysqli, zatímco `Driver` zpracovává metadata specifická pro databázový stroj SQL a uvozování identifikátorů. Vestavěné ovladače jsou k dispozici pro MySQL a SQLite.

```php
use Merql\Driver\DriverFactory;

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

## Rozhraní Driver

Každý ovladač implementuje pět 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 | Účel |
|---|---|
| `quoteIdentifier` | Uvozovkami ohraničí název tabulky nebo sloupce pro použití v SQL. |
| `listTables` | Vrátí všechny uživatelské tabulky v aktuální databázi. |
| `readSchema` | Načte názvy a typy sloupců, primární klíč a jedinečné klíče tabulky. |
| `readForeignKeys` | Vrátí mapu závislostí podřízených a nadřazených tabulek. |
| `selectAll` | Sestaví dotaz `SELECT *` pro tabulku. |

## MysqlDriver

Pro metadata používá dotazy `INFORMATION_SCHEMA` a identifikátory uzavírá do zpětných apostrofů.

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

Dotazy na výpis tabulek používají `INFORMATION_SCHEMA.TABLES` filtrované podle aktuální databáze a typu `BASE TABLE`. Čtení schématu používá `INFORMATION_SCHEMA.COLUMNS` pro sloupce a `INFORMATION_SCHEMA.KEY_COLUMN_USAGE` pro primární a jedinečné klíče.

## SqliteDriver

Pro metadata používá dotazy `PRAGMA` a identifikátory uzavírá do dvojitých uvozovek.

```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` automaticky rozpozná správný ovladač podle `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
```

Pokud identita ovladače připojení není rozpoznána, vyvolá se `RuntimeException` se zprávou `Unsupported database driver: <name>`. merql neobsahuje ovladač PostgreSQL.

### Registrace vlastních ovladačů

Pro databáze jiné než MySQL a SQLite napište třídu implementující `Driver` a zaregistrujte ji. `PostgresDriver` níže označuje vaši vlastní třídu:

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

Třída ovladače musí implementovat rozhraní `Driver`. První argument `register()` je identita ovladače připojení.

### Resetování

Pro testování obnovte výchozí stav továrny:

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

## Pomocná třída pro připojení

Třída `Connection` vytváří adaptéry s vhodným výchozím nastavením. Adaptéry PDO při chybě vyvolávají výjimky, používají asociativní režim načítání, skutečné připravené příkazy a převádějí načtené hodnoty na řetězce. Adaptér mysqli nastaví `utf8mb4`, používá režim výjimek mysqli a připravené příkazy a normalizuje načtené hodnoty do stejného formátu řetězec nebo 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()` přijímá libovolné DSN, ke kterému se PDO může připojit, ale merql může pořizovat snímky a provádět změny pouze prostřednictvím zaregistrovaného ovladače. Připojení `pgsql:` se otevře (pokud je nainstalováno `pdo_pgsql`), ale konstruktory `Merql::init()` a `Snapshotter`, `Applier` a `GuardedApplier` volají `DriverFactory::create()`, který vyvolá `RuntimeException: Unsupported database driver: pgsql`, pokud pro něj nemáte [zaregistrovaný ovladač](#registering-custom-drivers) nebo jej nepředáte.

`Connection::sqlite()` také automaticky povolí `PRAGMA foreign_keys = ON`, který SQLite vyžaduje pro vynucování cizích klíčů.

## Kde se ovladače používají

Ovladače se v merql používají napříč celým systémem:

- **Snapshotter** používá `listTables`, `readSchema` a `selectAll` k zachycení stavu databáze.
- **Applier** používá `readForeignKeys` k určení pořadí operací a předává ovladač `SqlGenerator` pro uvozování identifikátorů.
- **SqlGenerator** používá `quoteIdentifier` k sestavení bezpečných SQL.

Pokud předáte připojení do `Snapshotter` nebo `Applier` bez určení ovladače, automaticky se zavolá `DriverFactory::create()`.
