
# SQL Ustvarjanje

`SqlGenerator` pretvori `MergeResult` v parametrizirane stavke SQL. Vse vrednosti so vezane kot parametri in nikoli vstavljene v niz SQL.

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

Uporabite `generateGuarded()`, kadar želite pregledani načrt združitve uporabiti samo, če se aktivne vrstice še vedno ujemajo s pričakovanim posnetkom.

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

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

## Oblika stavka

Vsak stavek je asociativna tabela z dvema ključema:

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

Identifikatorji (imena tabel in stolpcev) so navedeni v narekovajih glede na slog gonilnika podatkovne zbirke. MySQL uporablja povratne narekovaje, SQLite pa dvojne narekovaje. Če gonilnik ni naveden, se privzeto uporabijo povratni narekovaji.

## Vrstni red operacij

Operacije so razvrščene po vrsti in izvedene v tem vrstnem redu:

1. Najprej **INSERT** – nove vrstice morajo obstajati, preden se lahko nanje sklicujejo posodobitve.
2. Nato **UPDATE**.
3. Nazadnje **DELETE** – pred brisanjem je treba odstraniti sklice na vrstice.

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

## Vrstni red tujih ključev

Če so podane odvisnosti tujih ključev, se operacije znotraj posamezne skupine topološko razvrstijo. Pri vstavljanju in posodabljanju se najprej obdelajo nadrejene tabele, nato podrejene. Pri brisanju se najprej obdelajo podrejene tabele, nato nadrejene.

```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` izvede topološko razvrščanje grafa odvisnosti. Krožne odvisnosti zazna in prekine, da prepreči neskončne zanke.

### Samodejno zaznavanje tujih ključev

`Applier` samodejno prebere povezave tujih ključev iz podatkovne zbirke:

```php
use Merql\Apply\Applier;

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

## Primeri ustvarjenih SQL

### INSERT

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

Vključeni so vsi stolpci iz vrednosti operacije.

### UPDATE

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

Identifikacijski stolpci (primarni ključ) so izključeni iz stavka SET in uporabljeni v stavku WHERE. Vrednosti stavka WHERE izhajajo iz dekodiranja ključa vrstice.

### DELETE

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

V stavku WHERE so samo identifikacijski stolpci.

### Zaščitena posodobitev UPDATE

Zaščitene posodobitve vključujejo pogoje za identiteto in pričakovano stanje aktivne vrstice.

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

Če vrstica nima več pričakovane vrednosti `status`, stavek ne vpliva na nobeno vrstico, zaščitena uporaba pa povrne transakcijo.

### Zaščiteno vstavljanje INSERT

Zaščitena vstavljanja uporabljajo `NOT EXISTS`, da preprečijo vstavljanje v identiteto, ki se je pojavila po ustvarjanju načrta.

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

## Obravnava NULL

Vrednosti NULL se v nizu parametrov posredujejo kot PHP `null`. Povezovalnik povezave poskrbi za vezavo:

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

V izpisu suhega zagona so vrednosti NULL prikazane kot dobesedni niz `NULL` brez narekovajev.

## Zaznavanje identifikacijskih stolpcev

`SqlGenerator` določi, kateri stolpci sestavljajo identiteto vrstice, tako da pregleda `TableSnapshot::identityColumns` osnovnega posnetka. Ti stolpci se uporabijo v stavkih WHERE za UPDATE in DELETE ter izključijo iz stavkov SET za UPDATE.

Če osnovni posnetek ni na voljo, se kot nadomestna možnost uporabi prvi stolpec v nizu vrednosti. Če je niz vrednosti prazen, se predpostavi `id`.

## Posredovanje gonilnika

Za nadzor zapisa identifikatorjev v narekovaje posredujte primerek `Driver`:

```php
use Merql\Driver\SqliteDriver;

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