
# Driver Database

merql memisahkan transport koneksi dari dialek database. Adaptor `DatabaseConnection` menjalankan SQL melalui PDO atau mysqli, sedangkan `Driver` menangani metadata khusus mesin database SQL dan pengutipan identifier. Driver bawaan tersedia untuk MySQL dan SQLite.

```php
use Merql\Driver\DriverFactory;

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

## Antarmuka Driver

Setiap driver menerapkan lima metode:

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

| Metode | Tujuan |
|---|---|
| `quoteIdentifier` | Mengutip nama tabel atau kolom untuk digunakan dalam SQL. |
| `listTables` | Mengembalikan semua tabel pengguna dalam database saat ini. |
| `readSchema` | Membaca nama dan jenis kolom, kunci utama, serta kunci unik untuk tabel. |
| `readForeignKeys` | Mengembalikan pemetaan dependensi dari tabel anak ke tabel induk. |
| `selectAll` | Membuat kueri `SELECT *` untuk tabel. |

## MysqlDriver

Menggunakan kueri `INFORMATION_SCHEMA` untuk metadata dan pengutipan backtick untuk identifier.

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

Kueri daftar tabel `INFORMATION_SCHEMA.TABLES` yang difilter berdasarkan database saat ini dan jenis `BASE TABLE`. Pembacaan skema mengueri `INFORMATION_SCHEMA.COLUMNS` untuk kolom dan `INFORMATION_SCHEMA.KEY_COLUMN_USAGE` untuk kunci utama dan unik.

## SqliteDriver

Menggunakan kueri `PRAGMA` untuk metadata dan pengutipan tanda kutip ganda untuk identifier.

```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` mendeteksi driver yang tepat secara otomatis dari `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
```

Jika identitas driver koneksi tidak dikenali, `RuntimeException` akan dilempar dengan pesan `Unsupported database driver: <name>`. merql tidak menyertakan driver PostgreSQL.

### Mendaftarkan driver khusus

Untuk database selain MySQL dan SQLite, tulis kelas yang menerapkan `Driver` lalu daftarkan. `PostgresDriver` di bawah ini mewakili kelas Anda sendiri:

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

Kelas driver harus menerapkan antarmuka `Driver`. Argumen pertama untuk `register()` adalah identitas driver koneksi.

### Reset

Untuk pengujian, reset factory ke keadaan default:

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

## Helper koneksi

Kelas `Connection` membuat adaptor dengan default yang sesuai. Adaptor PDO menggunakan pengecualian saat terjadi kesalahan, mode pengambilan asosiatif, pernyataan yang disiapkan sungguhan, dan hasil pengambilan yang diubah menjadi string. Adaptor mysqli menetapkan `utf8mb4`, menggunakan mode pengecualian mysqli dan pernyataan yang disiapkan, serta menormalkan nilai yang diambil ke bentuk string atau null yang sama.

```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()` menerima DSN apa pun yang dapat dibuka PDO, tetapi merql hanya dapat mengambil snapshot dan menerapkan perubahan melalui driver terdaftar. Koneksi `pgsql:` terbuka (jika `pdo_pgsql` terinstal), tetapi konstruktor `Merql::init()`, `Snapshotter`, `Applier`, dan `GuardedApplier` memanggil `DriverFactory::create()`, yang melempar `RuntimeException: Unsupported database driver: pgsql` kecuali Anda telah mendaftarkan driver untuknya dengan [mendaftarkan driver](#registering-custom-drivers) atau meneruskan driver.

`Connection::sqlite()` juga mengaktifkan `PRAGMA foreign_keys = ON` secara otomatis, yang diperlukan SQLite untuk menerapkan kunci asing.

## Penggunaan driver

Driver digunakan di seluruh merql:

- **Snapshotter** menggunakan `listTables`, `readSchema`, dan `selectAll` untuk merekam keadaan database.
- **Applier** menggunakan `readForeignKeys` untuk menentukan urutan operasi dan meneruskan driver ke `SqlGenerator` untuk mengutip identifier.
- **SqlGenerator** menggunakan `quoteIdentifier` untuk membuat SQL yang aman.

Jika Anda meneruskan koneksi ke `Snapshotter` atau `Applier` tanpa menentukan driver, `DriverFactory::create()` akan dipanggil secara otomatis.
