
# SQL ģenerēšana

`SqlGenerator` pārveido `MergeResult` par parametrizētiem SQL priekšrakstiem. Visas vērtības tiek nodotas kā parametri, un tās nekad netiek ievietotas tieši SQL virknē.

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

Izmantojiet `generateGuarded()`, ja pārskatītu apvienošanas plānu drīkst lietot tikai tad, ja tiešsaistes rindas joprojām atbilst paredzētajam momentuzņēmumam.

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

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

## Priekšraksta formāts

Katrs priekšraksts ir asociatīvs masīvs ar divām atslēgām:

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

Identifikatori (tabulu un kolonnu nosaukumi) tiek citēti atbilstoši datubāzes draivera noteikumiem. MySQL izmanto atpakaļpēdiņas, SQLite izmanto dubultpēdiņas. Ja draiveris nav norādīts, pēc noklusējuma izmanto atpakaļpēdiņas.

## Darbību secība

Darbības tiek grupētas pēc tipa un izpildītas šādā secībā:

1. Vispirms **INSERT** — jaunajām rindām jāpastāv, pirms atjauninājumi var uz tām atsaukties.
2. Otrkārt, **UPDATE**.
3. Visbeidzot, **DELETE** — pirms dzēšanas jānoņem atsauces uz rindām.

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

## Ārējo atslēgu secība

Ja ir norādītas ārējo atslēgu atkarības, darbības katrā grupā tiek sakārtotas topoloģiski. Ievietošanai un atjaunināšanai vispirms apstrādā vecāktabulas, pēc tam pakārtotās tabulas. Dzēšanai vispirms apstrādā pakārtotās tabulas, pēc tam vecāktabulas.

```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` veic atkarību grafa topoloģisko kārtošanu. Cikliskas atkarības tiek noteiktas un pārtrauktas, lai novērstu bezgalīgas cilpas.

### Automātiska ārējo atslēgu noteikšana

`Applier` automātiski nolasa ārējo atslēgu saites no datubāzes:

```php
use Merql\Apply\Applier;

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

## Ģenerētā SQL piemēri

### INSERT

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

Tiek iekļautas visas darbības vērtībās norādītās kolonnas.

### UPDATE

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

Identitātes kolonnas (primārā atslēga) netiek iekļautas SET klauzulā, bet tiek izmantotas WHERE klauzulā. WHERE klauzulas vērtības tiek iegūtas, dekodējot rindas atslēgu.

### DELETE

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

WHERE klauzulā ir tikai identitātes kolonnas.

### Aizsargāts UPDATE

Aizsargātos atjauninājumos ir gan identitātes klauzulas, gan paredzētās tiešsaistes rindas klauzulas.

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

Ja rindai vairs nav paredzētā `status`, priekšraksts ietekmē nulli rindu un aizsargātā izpilde atceļ transakciju.

### Aizsargāts INSERT

Aizsargātās ievietošanas izmanto `NOT EXISTS`, lai neievietotu rindu ar identitāti, kas parādījusies pēc plāna izveides.

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

## NULL apstrāde

NULL vērtības parametru masīvā tiek nodotas kā PHP `null`. Savienojuma adapteris apstrādā piesaisti:

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

Pārbaudes režīma izvadē NULL vērtības tiek attēlotas kā literāla virkne `NULL` bez pēdiņām.

## Identitātes kolonnas noteikšana

`SqlGenerator` nosaka, kuras kolonnas veido rindas identitāti, aplūkojot bāzes momentuzņēmuma `TableSnapshot::identityColumns`. Šīs kolonnas tiek izmantotas UPDATE un DELETE priekšrakstu WHERE klauzulās un UPDATE priekšrakstu SET klauzulās netiek iekļautas.

Ja nav pieejams pamata momentuzņēmums, kā atkāpšanās variants tiek izmantota vērtību masīva pirmā kolonna. Ja vērtību masīvs ir tukšs, tiek pieņemts, ka tā ir `id`.

## Draivera nodošana

Lai kontrolētu identifikatoru citēšanu, nododiet `Driver` instanci:

```php
use Merql\Driver\SqliteDriver;

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