
# SQL genereerimine

`SqlGenerator` teisendab väärtuse `MergeResult` parameetriseeritud SQL-lauseteks. Kõik väärtused seotakse parameetritena ega paigutata kunagi SQL stringi sisse.

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

Kasutage väärtust `generateGuarded()`, kui üle vaadatud liitmisplaani tohib rakendada ainult juhul, kui aktiivsed read vastavad endiselt eeldatud hetktõmmisele.

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

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

## Lausete vorming

Iga lause on assotsiatiivne massiiv kahe võtmega:

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

Identifikaatorid (tabelite ja veergude nimed) pannakse jutumärkidesse andmebaasi draiveri vormingu järgi. MySQL kasutab tagurpidi ühekordseid jutumärke, SQLite topeltjutumärke. Kui draiverit pole määratud, kasutatakse vaikimisi tagurpidi ühekordseid jutumärke.

## Operatsioonide järjestus

Operatsioonid rühmitatakse tüübi järgi ja käivitatakse järgmises järjestuses:

1. Kõigepealt **INSERTid** – uued read peavad olemas olema enne, kui uuendused saavad neile viidata.
2. Seejärel **UPDATEid**.
3. Viimasena **DELETEid** – viited tuleb eemaldada enne ridade kustutamist.

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

## Võõrvõtmete järjestus

Kui võõrvõtmete sõltuvused on esitatud, järjestatakse iga rühma operatsioonid topoloogiliselt. Lisamiste ja uuenduste korral töödeldakse vanemtabeleid enne tütartabeleid. Kustutamiste korral töödeldakse tütartabeleid enne vanemtabeleid.

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

`ForeignKeyResolver` järjestab sõltuvusgraafi topoloogiliselt. Lõpmatute tsüklite vältimiseks tuvastatakse ringviited ja katkestatakse need.

### Võõrvõtmete automaatne tuvastamine

`Applier` loeb võõrvõtmete seosed automaatselt andmebaasist:

```php
use Merql\Apply\Applier;

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

## Genereeritud SQL näited

### INSERT

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

Kaasatakse kõik operatsiooni väärtuste hulgas olevad veerud.

### UPDATE

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

Identiteediveerud (primaarvõti) jäetakse SET-klauslist välja ja neid kasutatakse WHERE-klauslis. WHERE-klausli väärtused saadakse reavõtme dekodeerimisel.

### DELETE

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

WHERE-klauslis kasutatakse ainult identiteediveerge.

### Piiratud UPDATE

Piiratud uuendused sisaldavad nii identiteediklausleid kui ka eeldatud aktiivse rea klausleid.

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

Kui real pole enam eeldatud `status`, ei mõjuta lause ühtegi rida ja piiratud rakendamine tühistab tehingu.

### Piiratud INSERT

Piiratud lisamised kasutavad väärtust `NOT EXISTS`, et vältida sisestamist identiteediga, mis tekkis pärast plaani loomist.

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

## NULL-i käsitlemine

NULL-väärtused edastatakse parameetrite massiivis väärtustena PHP `null`. Ühenduse adapter hoolitseb sidumise eest:

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

Proovikäigu väljundis kuvatakse NULL-väärtused jutumärkideta literaalstringina `NULL`.

## Identiteediveergude tuvastamine

`SqlGenerator` määrab rea identiteedi moodustavad veerud baashetktõmmise `TableSnapshot::identityColumns` põhjal. Neid veerge kasutatakse UPDATE- ja DELETE-lausete WHERE-klauslites ning jäetakse UPDATE-lausete SET-klauslitest välja.

Kui baashetktõmmist pole saadaval, kasutatakse varuvariandina väärtuste massiivi esimest veergu. Kui väärtuste massiiv on tühi, eeldatakse `id`.

## Draiveri edastamine

Identifikaatorite jutumärkidesse panemise juhtimiseks edastage `Driver`-i eksemplar:

```php
use Merql\Driver\SqliteDriver;

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