
# データベースドライバー

merqlは接続の転送方式とデータベース方言を分離します。
`DatabaseConnection` アダプターはPDOまたはmysqli経由で SQL を実行し、`Driver` はデータベースエンジン固有のメタデータ SQL と識別子の引用符処理を担います。MySQLとSQLite用の組み込みドライバーが用意されています。

```php
use Merql\Driver\DriverFactory;

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

## Driverインターフェース

すべてのドライバーは次の5つのメソッドを実装します：

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

| メソッド | 目的 |
|---|---|
| `quoteIdentifier` | SQL で使用するテーブル名または列名を引用符で囲みます。 |
| `listTables` | 現在のデータベース内にあるすべてのユーザーテーブルを返します。 |
| `readSchema` | テーブルの列名、型、主キー、一意キーを読み取ります。 |
| `readForeignKeys` | 子テーブルから親テーブルへの依存関係マップを返します。 |
| `selectAll` | テーブル用の `SELECT *` クエリを構築します。 |

## MysqlDriver

メタデータの取得には `INFORMATION_SCHEMA` クエリを使い、識別子はバッククォートで囲みます。

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

テーブル一覧の取得では、現在のデータベースと `BASE TABLE` 型で絞り込んだ `INFORMATION_SCHEMA.TABLES` をクエリします。スキーマの読み取りでは、列に `INFORMATION_SCHEMA.COLUMNS`、主キーと一意キーに `INFORMATION_SCHEMA.KEY_COLUMN_USAGE` をクエリします。

## SqliteDriver

メタデータの取得には `PRAGMA` クエリを使い、識別子は二重引用符で囲みます。

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

接続ドライバーの識別情報が認識されない場合、メッセージ `Unsupported database driver: <name>` を持つ `RuntimeException` がスローされます。merqlにはPostgreSQL用ドライバーは含まれていません。

### カスタムドライバーの登録

MySQLとSQLite以外のデータベースでは、`Driver` を実装するクラスを作成して登録します。以下の `PostgresDriver` は独自のクラスに置き換えてください：

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

ドライバークラスは `Driver` インターフェースを実装する必要があります。`register()` の最初の引数は接続ドライバーの識別情報です。

### リセット

テスト時にファクトリーをデフォルト状態に戻します：

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

## 接続ヘルパー

`Connection` クラスは適切なデフォルト設定でアダプターを構築します。PDOアダプターでは、エラー時の例外、連想配列形式の取得、ネイティブのプリペアドステートメント、文字列化された取得値が使われます。mysqliアダプターは `utf8mb4` を設定し、mysqliの例外モードとプリペアドステートメントを使って、取得値を同じ文字列または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()` はPDOが開ける任意のDSNを受け付けますが、merqlでスナップショットの作成と適用を行うには、登録済みドライバーが必要です。`pgsql:` 接続は（`pdo_pgsql` がインストールされていれば）開けますが、`Merql::init()` と `Snapshotter`、`Applier`、`GuardedApplier` のコンストラクターは `DriverFactory::create()` を呼び出します。ドライバーを[登録する](#registering-custom-drivers)か、ドライバーを渡さない限り、`RuntimeException: Unsupported database driver: pgsql` がスローされます。

`Connection::sqlite()` は `PRAGMA foreign_keys = ON` も自動的に有効にします。これはSQLiteで外部キー制約を有効にするために必要です。

## ドライバーの使用箇所

merqlではさまざまな箇所でドライバーを使用します：

- **Snapshotter** は `listTables`、`readSchema`、`selectAll` を使ってデータベースの状態を取得します。
- **Applier** は `readForeignKeys` を使って操作順を決め、識別子を引用符で囲むためにドライバーを `SqlGenerator` に渡します。
- **SqlGenerator** は `quoteIdentifier` を使って安全な SQL を構築します。

ドライバーを指定せずに `Snapshotter` または `Applier` に接続を渡すと、`DriverFactory::create()` が自動的に呼び出されます。
