
# 데이터베이스 드라이버

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 인터페이스

모든 드라이버는 다음 다섯 메서드를 구현합니다:

```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**는 안전한 SQL을 구성할 때 `quoteIdentifier`를 사용합니다.

드라이버를 지정하지 않고 `Snapshotter` 또는 `Applier`에 연결을 전달하면 `DriverFactory::create()`가 자동으로 호출됩니다.
