
# Drivers de banco de dados

merql separa o transporte da conexão do dialeto do banco de dados. Um adaptador `DatabaseConnection` executa SQL via PDO ou mysqli, enquanto um `Driver` lida com metadados SQL específicos do mecanismo de banco de dados e com a citação de identificadores. Há drivers integrados para MySQL e SQLite.

```php
use Merql\Driver\DriverFactory;

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

## A interface Driver

Cada driver implementa cinco métodos:

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

| Método | Finalidade |
|---|---|
| `quoteIdentifier` | Coloca entre aspas o nome de uma tabela ou coluna para uso em SQL. |
| `listTables` | Retorna todas as tabelas de usuário no banco de dados atual. |
| `readSchema` | Lê os nomes e tipos das colunas, a chave primária e as chaves exclusivas de uma tabela. |
| `readForeignKeys` | Retorna um mapa das dependências entre tabelas filhas e tabelas pai. |
| `selectAll` | Cria uma consulta `SELECT *` para uma tabela. |

## MysqlDriver

Usa consultas `INFORMATION_SCHEMA` para os metadados e aspas invertidas para delimitar identificadores.

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

As consultas de listagem de tabelas usam `INFORMATION_SCHEMA.TABLES` filtrado pelo banco de dados atual e pelo tipo `BASE TABLE`. As consultas de leitura do esquema usam `INFORMATION_SCHEMA.COLUMNS` para as colunas e `INFORMATION_SCHEMA.KEY_COLUMN_USAGE` para as chaves primárias e exclusivas.

## SqliteDriver

Usa consultas `PRAGMA` para os metadados e aspas duplas para delimitar identificadores.

```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` detecta automaticamente o driver correto a partir de `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 a identidade do driver da conexão não for reconhecida, será lançado um `RuntimeException` com a mensagem `Unsupported database driver: <name>`. merql não inclui um driver PostgreSQL.

### Registrando drivers personalizados

Para bancos de dados além de MySQL e SQLite, escreva uma classe que implemente `Driver` e registre-a. `PostgresDriver` abaixo representa sua própria 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.
```

A classe do driver deve implementar a interface `Driver`. O primeiro argumento de `register()` é a identidade do driver da conexão.

### Redefinição

Para testar, redefina a fábrica para seu estado padrão:

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

## Auxiliar de conexão

A classe `Connection` cria adaptadores com padrões sensatos. Os adaptadores PDO usam exceções em caso de erro, modo de busca associativo, prepared statements reais e valores buscados convertidos em strings. O adaptador mysqli define `utf8mb4`, usa o modo de exceção do mysqli, prepared statements e normaliza os valores buscados para o mesmo formato de string ou 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()` aceita qualquer DSN que o PDO possa abrir, mas merql só consegue criar snapshots e aplicar alterações por meio de um driver registrado. Uma conexão `pgsql:` é aberta (quando `pdo_pgsql` estiver instalado), mas os construtores `Merql::init()`, `Snapshotter`, `Applier` e `GuardedApplier` chamam `DriverFactory::create()`, que lança `RuntimeException: Unsupported database driver: pgsql`, a menos que você tenha [registrado um driver](#registering-custom-drivers) para ela ou informe um.

`Connection::sqlite()` também ativa `PRAGMA foreign_keys = ON` automaticamente, requisito de SQLite para aplicar restrições de chaves estrangeiras.

## Onde os drivers são usados

Os drivers são usados em todo o merql:

- **Snapshotter** usa `listTables`, `readSchema` e `selectAll` para capturar o estado do banco de dados.
- **Applier** usa `readForeignKeys` para determinar a ordem das operações e passa o driver para `SqlGenerator` para citar identificadores.
- **SqlGenerator** usa `quoteIdentifier` para criar SQL seguros.

Se você passar uma conexão para `Snapshotter` ou `Applier` sem especificar um driver, `DriverFactory::create()` será chamado automaticamente.
