
# Driver di database

merql separa il trasporto della connessione dal dialetto del database. Un adattatore `DatabaseConnection` esegue SQL tramite PDO o mysqli, mentre un `Driver` gestisce i metadati SQL specifici del motore di database e la quotatura degli identificatori. Sono disponibili driver integrati per MySQL e SQLite.

```php
use Merql\Driver\DriverFactory;

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

## L'interfaccia Driver

Ogni driver implementa cinque metodi:

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

| Metodo | Scopo |
|---|---|
| `quoteIdentifier` | Quota il nome di una tabella o colonna per usarlo in SQL. |
| `listTables` | Restituisce tutte le tabelle utente nel database corrente. |
| `readSchema` | Legge i nomi e i tipi delle colonne, la chiave primaria e le chiavi univoche di una tabella. |
| `readForeignKeys` | Restituisce una mappa delle dipendenze tra tabelle figlie e tabelle padre. |
| `selectAll` | Crea una query `SELECT *` per una tabella. |

## MysqlDriver

Usa query `INFORMATION_SCHEMA` per i metadati e la quotatura con backtick per gli identificatori.

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

L'elenco delle tabelle interroga `INFORMATION_SCHEMA.TABLES`, filtrato per il database corrente e il tipo `BASE TABLE`. La lettura dello schema interroga `INFORMATION_SCHEMA.COLUMNS` per le colonne e `INFORMATION_SCHEMA.KEY_COLUMN_USAGE` per le chiavi primarie e univoche.

## SqliteDriver

Usa query `PRAGMA` per i metadati e la quotatura con virgolette doppie per gli identificatori.

```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` rileva automaticamente il driver corretto da `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
```

Se l'identità del driver della connessione non viene riconosciuta, viene generata una `RuntimeException` con il messaggio `Unsupported database driver: <name>`. merql non include driver PostgreSQL.

### Registrazione di driver personalizzati

Per database diversi da MySQL e SQLite, scrivi una classe che implementi `Driver` e registrala. `PostgresDriver` qui sotto indica la tua classe:

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

La classe del driver deve implementare l'interfaccia `Driver`. Il primo argomento di `register()` è l'identità del driver della connessione.

### Ripristino

Per i test, ripristina la factory allo stato predefinito:

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

## Helper di connessione

La classe `Connection` crea adattatori con impostazioni predefinite sensate. Gli adattatori PDO usano eccezioni in caso di errore, modalità di fetch associativa, prepared statement reali e valori recuperati convertiti in stringhe. L'adattatore mysqli imposta `utf8mb4`, usa la modalità eccezioni mysqli, prepared statement e normalizza i valori recuperati allo stesso formato stringa o 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()` accetta qualsiasi DSN che PDO possa aprire, ma merql può acquisire snapshot e applicare modifiche solo tramite un driver registrato. Una connessione `pgsql:` si apre (se `pdo_pgsql` è installato), ma i costruttori di `Merql::init()`, `Snapshotter`, `Applier` e `GuardedApplier` chiamano `DriverFactory::create()`, che genera `RuntimeException: Unsupported database driver: pgsql` a meno che tu non abbia [registrato un driver](#registering-custom-drivers) per esso o non ne passi uno.

`Connection::sqlite()` abilita anche automaticamente `PRAGMA foreign_keys = ON`, che SQLite richiede per l'applicazione dei vincoli di chiave esterna.

## Dove vengono usati i driver

I driver sono usati in tutto merql:

- **Snapshotter** usa `listTables`, `readSchema` e `selectAll` per acquisire lo stato del database.
- **Applier** usa `readForeignKeys` per determinare l'ordine delle operazioni e passa il driver a `SqlGenerator` per la quotatura degli identificatori.
- **SqlGenerator** usa `quoteIdentifier` per creare SQL sicuro.

Se passi una connessione a `Snapshotter` o `Applier` senza specificare un driver, `DriverFactory::create()` viene chiamato automaticamente.
