
# Generovanie SQL

`SqlGenerator` prevádza `MergeResult` na parametrizované príkazy SQL. Všetky hodnoty sa odovzdávajú ako parametre a nikdy sa nevkladajú priamo do reťazca 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']
}
```

`generateGuarded()` použite vtedy, keď sa má skontrolovaný plán zlúčenia použiť iba v prípade, že sa aktuálne riadky stále zhodujú s očakávanou snímkou.

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

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

## Formát príkazu

Každý príkaz je asociatívne pole s dvoma kľúčmi:

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

Identifikátory (názvy tabuliek a stĺpcov) sa uzatvárajú do úvodzoviek podľa pravidiel ovládača databázy. MySQL používa spätné apostrofy, SQLite používa dvojité úvodzovky. Ak ovládač nie je zadaný, predvolene sa používajú spätné apostrofy.

## Poradie operácií

Operácie sa zoskupia podľa typu a vykonajú v tomto poradí:

1. Najprv **INSERTs** – nové riadky musia existovať predtým, než sa na ne budú odkazovať aktualizácie.
2. Potom **UPDATEs**.
3. Nakoniec **DELETEs** – pred odstránením treba zrušiť odkazy na riadky.

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

## Poradie cudzích kľúčov

Ak sú zadané závislosti cudzích kľúčov, operácie v každej skupine sa zoradia topologicky. Pri vkladaní a aktualizáciách sa najprv spracujú nadradené tabuľky a potom podriadené tabuľky. Pri odstraňovaní sa najprv spracujú podriadené tabuľky a potom nadradené tabuľky.

```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` vykoná topologické triedenie grafu závislostí. Kruhové závislosti sa zistia a prerušia, aby sa zabránilo nekonečným slučkám.

### Automatické zisťovanie cudzích kľúčov

`Applier` automaticky načíta vzťahy cudzích kľúčov z databázy:

```php
use Merql\Apply\Applier;

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

## Príklady generovaných SQL

### INSERT

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

Zahrnú sa všetky stĺpce z hodnôt operácie.

### UPDATE

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

Stĺpce identity (primárny kľúč) sa vynechajú z klauzuly SET a použijú sa v klauzule WHERE. Hodnoty klauzuly WHERE sa získajú dekódovaním kľúča riadka.

### DELETE

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

V klauzule WHERE sa nachádzajú iba stĺpce identity.

### Chránený UPDATE

Chránené aktualizácie obsahujú klauzuly identity aj klauzuly očakávaného aktuálneho riadka.

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

Ak riadok už nemá očakávaný `status`, príkaz ovplyvní nula riadkov a chránené použitie vráti transakciu späť.

### Chránený INSERT

Chránené vkladania používajú `NOT EXISTS`, aby zabránili vloženiu identity, ktorá sa objavila po vytvorení plánu.

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

## Spracovanie NULL

Hodnoty NULL sa v poli parametrov odovzdávajú ako PHP `null`. Ovládač pripojenia sa postará o naviazanie:

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

Vo výstupe skúšobného spustenia sa hodnoty NULL zobrazujú ako doslovný reťazec `NULL` bez úvodzoviek.

## Zisťovanie stĺpca identity

`SqlGenerator` určí, ktoré stĺpce tvoria identitu riadka, podľa `TableSnapshot::identityColumns` základnej snímky. Tieto stĺpce sa použijú v klauzulách WHERE príkazov UPDATE a DELETE a vynechajú sa z klauzúl SET príkazov UPDATE.

Ak základná snímka nie je k dispozícii, ako záložný stĺpec sa použije prvý stĺpec v poli hodnôt. Ak je pole hodnôt prázdne, predpokladá sa `id`.

## Odovzdanie ovládača

Ak chcete určiť spôsob uzatvárania identifikátorov do úvodzoviek, odovzdajte inštanciu `Driver`:

```php
use Merql\Driver\SqliteDriver;

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