
# SQL-generálás

A `SqlGenerator` egy `MergeResult`-t paraméterezett SQL-utasításokká alakít. Minden érték paraméterként van kötve, és soha nem kerül közvetlenül az SQL karakterláncba.

```php
use Merql\Apply\SqlGenerator;

$statements = SqlGenerator::generate($result);

foreach ($statements as $stmt) {
    echo $stmt['sql'] . "\n";
    // INSERT INTO `posts` (`id`, `title`, `status`) VALUES (?, ?, ?)
    print_r($stmt['params']);
    // [42, 'New Title', 'publish']
}
```

A `generateGuarded()` akkor használja, ha egy áttekintett egyesítési tervet csak akkor szabad alkalmazni, ha az éles sorok még mindig megfelelnek az elvárt pillanatképnek.

```php
use Merql\Apply\SqlGenerator;
use Merql\Snapshot\SnapshotStore;

$statements = SqlGenerator::generateGuarded(
    $selectedResult,
    SnapshotStore::load('theirs'),
);
```

## Utasításformátum

Minden utasítás egy asszociatív tömb két kulccsal:

```php
[
    'sql'    => 'INSERT INTO `posts` (`id`, `title`) VALUES (?, ?)',
    'params' => [42, 'New Title'],
]
```

Az azonosítókat (tábla- és oszlopneveket) az adatbázis-illesztőprogram idézőjelezési stílusa szerint idézi a rendszer. A MySQL backtickeket, a SQLite dupla idézőjeleket használ. Ha nincs megadva illesztőprogram, alapértelmezésként backtickes idézőjelezés történik.

## Műveletek sorrendje

A műveletek típus szerint csoportosítva, a következő sorrendben futnak:

1. **INSERTs** először -- az új soroknak létezniük kell, mielőtt a frissítések hivatkozhatnak rájuk.
2. **UPDATEs** másodikként.
3. **DELETEs** utoljára -- törlés előtt meg kell szüntetni a sorokra való hivatkozásokat.

```php
$statements = SqlGenerator::generate($result);
// All INSERTs, then all UPDATEs, then all DELETEs.
```

## Idegen kulcsok szerinti sorrend

Ha megadja az idegen kulcsok függőségeit, az egyes csoportokon belüli műveletek topologikus rendezést kapnak. Beszúrásoknál és frissítéseknél a szülőtáblák feldolgozása megelőzi a gyermektáblákét. Törléseknél a gyermektáblák kerülnek sorra a szülőtáblák előtt.

```php
$fkDependencies = [
    'comments' => ['posts'],        // comments references posts
    'post_meta' => ['posts'],       // post_meta references posts
];

$statements = SqlGenerator::generate($result, fkDependencies: $fkDependencies);
// INSERTs: posts first, then comments and post_meta
// DELETEs: comments and post_meta first, then posts
```

A `ForeignKeyResolver` topologikusan rendezi a függőségi gráfot. A végtelen ciklusok elkerülése érdekében felismeri és megszakítja a körkörös függőségeket.

### Idegen kulcsok automatikus felismerése

A `Applier` automatikusan beolvassa az adatbázisból az idegenkulcs-kapcsolatokat:

```php
use Merql\Apply\Applier;

$applier = new Applier($connection);
$applied = $applier->apply($result);
// FK dependencies are read from the database via the driver.
```

## Létrehozott SQL-példák

### INSERT

```sql
INSERT INTO `posts` (`id`, `title`, `content`, `status`) VALUES (?, ?, ?, ?)
-- params: [42, 'New Post', 'Body text', 'draft']
```

A művelet értékei között szereplő összes oszlop belekerül.

### UPDATE

```sql
UPDATE `posts` SET `title` = ?, `status` = ? WHERE `id` = ?
-- params: ['New Title', 'publish', 42]
```

Az azonosító oszlopok (elsődleges kulcs) kimaradnak a SET záradékból, és a WHERE záradékban szerepelnek. A WHERE záradék értékeit a sor kulcsának dekódolásával kapja meg a rendszer.

### DELETE

```sql
DELETE FROM `posts` WHERE `id` = ?
-- params: [42]
```

A WHERE záradékban csak az azonosító oszlopok szerepelnek.

### Védett UPDATE

A védett frissítések az azonosító feltételeit és az elvárt éles sorra vonatkozó feltételeket is tartalmazzák.

```sql
UPDATE `posts` SET `title` = ? WHERE `id` = ? AND `status` = ?
-- params: ['New Title', 42, 'draft']
```

Ha a sor már nem rendelkezik az elvárt `status` értékkel, az utasítás nulla sort érint, és a védett alkalmazás visszagörgeti a tranzakciót.

### Védett INSERT

A védett beszúrások a `NOT EXISTS` használatával elkerülik, hogy olyan azonosítóhoz szúrjanak be sort, amely a terv létrehozása óta már megjelent.

```sql
INSERT INTO `posts` (`id`, `title`) SELECT ?, ? WHERE NOT EXISTS (
  SELECT 1 FROM `posts` WHERE `id` = ?
)
-- params: [42, 'New Post', 42]
```

## NULL-kezelés

A NULL értékeket PHP `null` értékként adja át a paramétertömbben. A kötést a kapcsolatadapter kezeli:

```php
$statements = SqlGenerator::generate($result);
// params might contain: [42, null, 'draft']
// The adapter binds null as SQL NULL
```

A végrehajtás nélküli kimenetben a NULL értékek idézőjelek nélkül, a `NULL` literális karakterláncként jelennek meg.

## Azonosító oszlopok felismerése

A `SqlGenerator` az alap pillanatkép `TableSnapshot::identityColumns` értéke alapján határozza meg, mely oszlopok alkotják a sor azonosítóját. Ezek kerülnek a WHERE záradékba az UPDATE és DELETE utasításoknál, és kimaradnak az UPDATE utasítások SET záradékából.

Ha nincs elérhető alap pillanatkép, a rendszer az értéktömb első oszlopát használja tartalékként. Ha az értéktömb üres, a `id` értéket feltételezi.

## Illesztőprogram átadása

Az azonosítók idézőjelezésének szabályozásához adjon át egy `Driver` példányt:

```php
use Merql\Driver\SqliteDriver;

$statements = SqlGenerator::generate(
    $result,
    driver: new SqliteDriver(),
);
// Uses double-quote quoting: "posts", "title"
```
