
# SQL generavimas

`SqlGenerator` konvertuoja `MergeResult` į parametrizuotus SQL sakinius. Visos reikšmės perduodamos kaip parametrai ir niekada neįterpiamos į SQL eilutę.

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

Naudokite `generateGuarded()`, kai peržiūrėtas sujungimo planas turėtų būti pritaikytas tik tuo atveju, jei tiesioginės eilutės vis dar atitinka numatytą momentinę kopiją.

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

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

## Sakinio formatas

Kiekvienas sakinys yra asociatyvusis masyvas su dviem raktais:

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

Identifikatoriai (lentelių ir stulpelių pavadinimai) cituojami naudojant duomenų bazės tvarkyklės kabučių stilių. MySQL naudoja atvirkštines kabutes, o SQLite – dvigubas kabutes. Jei tvarkyklė nepateikta, pagal numatytąją nuostatą naudojamos atvirkštinės kabutės.

## Operacijų tvarka

Operacijos grupuojamos pagal tipą ir vykdomos tokia tvarka:

1. Pirmiausia vykdomi **INSERT** – naujos eilutės turi egzistuoti prieš nurodant jas atnaujinimuose.
2. Antrieji vykdomi **UPDATE**.
3. Galiausiai vykdomi **DELETE** – prieš ištrinant eilutes reikia pašalinti į jas nukreipiančias nuorodas.

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

## Svetimųjų raktų tvarka

Kai pateikiamos svetimųjų raktų priklausomybės, kiekvienos grupės operacijos rikiuojamos topologiškai. Įterpiant ir atnaujinant pirmiausia apdorojamos pirminės lentelės, tada antrinės. Trinant pirmiausia apdorojamos antrinės lentelės, tada pirminės.

```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` atlieka priklausomybių grafo topologinį rikiavimą. Ciklinės priklausomybės aptinkamos ir nutraukiamos, kad būtų išvengta begalinių ciklų.

### Automatinis svetimųjų raktų aptikimas

`Applier` automatiškai nuskaito svetimųjų raktų ryšius iš duomenų bazės:

```php
use Merql\Apply\Applier;

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

## Sugeneruotų SQL pavyzdžiai

### INSERT

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

Įtraukiami visi operacijos reikšmių stulpeliai.

### UPDATE

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

Tapatybės stulpeliai (pirminis raktas) neįtraukiami į SET sąlygą ir naudojami WHERE sąlygoje. WHERE sąlygos reikšmės gaunamos iššifravus eilutės raktą.

### DELETE

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

WHERE sąlygoje pateikiami tik tapatybės stulpeliai.

### Apsaugotas UPDATE

Apsaugoti atnaujinimai apima ir tapatybės sąlygas, ir numatytos tiesioginės eilutės sąlygas.

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

Jei eilutės `status` nebeatitinka numatytų, sakinys paveikia nulį eilučių ir apsaugotas pritaikymas atšaukia transakciją.

### Apsaugotas INSERT

Apsaugotuose įterpimuose naudojama `NOT EXISTS`, kad nebūtų įterpta eilutė su tapatybe, kuri atsirado po plano sudarymo.

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

## NULL reikšmių tvarkymas

NULL reikšmės parametrų masyve perduodamos kaip PHP `null`. Susiejimą atlieka ryšio adapteris:

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

Bandomojo vykdymo išvestyje NULL reikšmės pateikiamos kaip pažodinė eilutė `NULL` be kabučių.

## Tapatybės stulpelio aptikimas

`SqlGenerator` nustato, kurie stulpeliai sudaro eilutės tapatybę, nagrinėdama bazinės momentinės kopijos `TableSnapshot::identityColumns`. Šie stulpeliai naudojami UPDATE ir DELETE sakinių WHERE sąlygose, o UPDATE sakinių SET sąlygos iš jų neįtraukiamos.

Jei bazinė momentinė kopija nepasiekiama, kaip atsarginis variantas naudojamas pirmasis reikšmių masyvo stulpelis. Jei reikšmių masyvas tuščias, daroma prielaida, kad tai `id`.

## Tvarkyklės perdavimas

Norėdami valdyti identifikatorių kabučių naudojimą, perduokite `Driver` egzempliorių:

```php
use Merql\Driver\SqliteDriver;

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