
# Gonilniki zbirk podatkov

merql ločuje prenos povezave od narečja zbirke podatkov. Adapter `DatabaseConnection` izvaja SQL prek PDO ali mysqli, medtem ko `Driver` obravnava metapodatke SQL, specifične za mehanizem zbirke podatkov, in citiranje identifikatorjev. Vgrajeni gonilniki so na voljo za MySQL in SQLite.

```php
use Merql\Driver\DriverFactory;

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

## Vmesnik Driver

Vsak gonilnik implementira pet 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 | Namen |
|---|---|
| `quoteIdentifier` | Citira ime tabele ali stolpca za uporabo v SQL. |
| `listTables` | Vrne vse uporabniške tabele v trenutni zbirki podatkov. |
| `readSchema` | Prebere imena in vrste stolpcev, primarni ključ ter enolične ključe za tabelo. |
| `readForeignKeys` | Vrne preslikavo odvisnosti med podrejenimi in nadrejenimi tabelami. |
| `selectAll` | Sestavi poizvedbo `SELECT *` za tabelo. |

## MysqlDriver

Za metapodatke uporablja poizvedbe `INFORMATION_SCHEMA`, identifikatorje pa citira z obratnimi narekovaji.

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

Pri poizvedbah za seznam tabel uporabi `INFORMATION_SCHEMA.TABLES`, filtriran glede na trenutno zbirko podatkov in vrsto `BASE TABLE`. Pri branju sheme za stolpce uporabi poizvedbe `INFORMATION_SCHEMA.COLUMNS`, za primarne in enolične ključe pa `INFORMATION_SCHEMA.KEY_COLUMN_USAGE`.

## SqliteDriver

Za metapodatke uporablja poizvedbe `PRAGMA`, identifikatorje pa citira z dvojnimi narekovaji.

```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` samodejno zazna ustrezen gonilnik iz `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
```

Če identiteta gonilnika povezave ni prepoznana, se sproži `RuntimeException` s sporočilom `Unsupported database driver: <name>`. merql ne vključuje gonilnika PostgreSQL.

### Registracija gonilnikov po meri

Za zbirke podatkov poleg MySQL in SQLite napišite razred, ki implementira `Driver`, in ga registrirajte. `PostgresDriver` spodaj označuje vaš lastni razred:

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

Razred gonilnika mora implementirati vmesnik `Driver`. Prvi argument za `register()` je identiteta gonilnika povezave.

### Ponastavitev

Za testiranje ponastavite tovarniški objekt na privzeto stanje:

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

## Pomočnik za povezavo

Razred `Connection` sestavi adapterje z ustreznimi privzetimi nastavitvami. Adapterji PDO ob napaki sprožijo izjemo, uporabljajo asociativni način pridobivanja, prave pripravljene stavke in pridobivanje vrednosti v obliki nizov. Adapter mysqli nastavi `utf8mb4`, uporablja način izjem mysqli in pripravljene stavke ter normalizira pridobljene vrednosti v enako obliko niza ali 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()` sprejme katerikoli DSN, ki ga lahko odpre PDO, vendar lahko merql posnetke zajema in uveljavlja le prek registriranega gonilnika. Povezava `pgsql:` se odpre (če je nameščen `pdo_pgsql`), vendar konstruktorji `Merql::init()`, `Snapshotter`, `Applier` in `GuardedApplier` pokličejo `DriverFactory::create()`, ki sproži `RuntimeException: Unsupported database driver: pgsql`, razen če ste zanj [registrirali gonilnik](#registering-custom-drivers) ali ga posredujete.

`Connection::sqlite()` samodejno omogoči tudi `PRAGMA foreign_keys = ON`, ki ga SQLite zahteva za uveljavljanje tujih ključev.

## Kje se uporabljajo gonilniki

Gonilniki se uporabljajo v celotnem merql:

- **Snapshotter** uporablja `listTables`, `readSchema` in `selectAll` za zajem stanja zbirke podatkov.
- **Applier** uporablja `readForeignKeys` za določanje vrstnega reda operacij ter posreduje gonilnik komponenti `SqlGenerator` za citiranje identifikatorjev.
- **SqlGenerator** uporablja `quoteIdentifier` za sestavljanje varnih stavkov SQL.

Če povezavo posredujete v `Snapshotter` ali `Applier`, ne da bi določili gonilnik, se samodejno pokliče `DriverFactory::create()`.
