
# Použitie s kontrolou

`GuardedApplier` použije čisté `MergeResult` iba vtedy, ak cieľová databáza stále zodpovedá očakávanému aktuálnemu snímku. Hodí sa to, keď bol plán zlúčenia skontrolovaný skôr a aktuálne riadky sa pred jeho použitím mohli zmeniť.

Typickým postupom je presunúť úpravy zo sandboxu do živej databázy. Základ predstavuje živá databáza v čase, keď sa z nej vytvorila kópia sandboxu, naša strana je upravený sandbox a ich strana predstavuje aktuálny stav živej databázy. Cieľom je živá databáza, takže očakávaný aktuálny snímok je `theirs` a zapisujú sa operácie, ktoré živá databáza ešte neobsahuje (pozrite si [Výber operácií](#choosing-operations)).

```php
use Merql\Connection;
use Merql\Merql;
use Merql\Plan\OperationSelection;
use Merql\Plan\SelectedMergeResultFactory;
use Merql\Snapshot\SnapshotStore;

Merql::init(Connection::sqlite('/data/live.sqlite'));

$plan = Merql::plan('promote-1', 'base', 'ours', 'theirs');

// Skip rows with unresolved conflicts.
$conflicted = [];
foreach ($plan->conflicts as $conflict) {
    $conflicted[$conflict->table . "\0" . $conflict->rowKey] = true;
}

$ids = [];
foreach ($plan->operations as $operation) {
    $missing = $operation->source === 'ours'
        || ($operation->source === 'merged' && $operation->type === 'update');

    if ($missing && !isset($conflicted[$operation->table . "\0" . $operation->rowKey])) {
        $ids[] = $operation->id;
    }
}

$selected = (new SelectedMergeResultFactory())->fromOperationSelection(
    $plan,
    OperationSelection::fromIds($ids),
    SnapshotStore::load('base'),
);

$applied = Merql::applyGuarded($selected, SnapshotStore::load('theirs'));

if ($applied->hasErrors()) {
    foreach ($applied->errors() as $error) {
        echo $error . "\n";
    }
}
```

`Merql::applyGuarded()` zapisuje do databázy odovzdanej `Merql::init()`. Očakávaný aktuálny snímok musí opisovať tú istú databázu: získajte ho z cieľa a dostatočne nedávno, aby predpoklady mali zmysel.

## Predpoklady

Chránené SQL pridáva do generovaných príkazov predpoklady založené na aktuálnych riadkoch:

- pri aktualizáciách musí existovať identita cieľového riadka a hodnoty stĺpcov, ktoré nie sú súčasťou identity, sa musia zhodovať s očakávaným aktuálnym riadkom,
- pri odstraňovaní musí existovať identita cieľového riadka a hodnoty stĺpcov, ktoré nie sú súčasťou identity, sa musia zhodovať s očakávaným aktuálnym riadkom,
- pri vkladaní nesmie cieľová identita už existovať.

Ak ktorýkoľvek príkaz ovplyvní nula riadkov, merql považuje plán za zastaraný. Transakcia sa vráti späť a vrátené `ApplyResult` obsahuje chybu `Optimistic precondition failed for guarded merge statement.`

V MySQL ovplyvní aktualizácia, ktorej hodnoty sa už zhodujú s aktuálnym riadkom, tiež nula riadkov, pretože MySQL počíta zmenené riadky, nie nájdené riadky. Takáto aktualizácia zlyhá ako zastaraná, hoci sa riadok zhoduje ([#2](https://github.com/inline0/merql/issues/2)). SQLite počíta nájdené riadky a prijme ju. Môže k tomu dôjsť pri aktualizácii `merged`, keď obe strany vykonali na riadku rovnakú zmenu.

## Výber operácií

Výsledok zlúčenia obsahuje operácie oboch strán, pretože mení základný stav na zlúčený stav. Ak sa použije na cieľ, ktorý už obsahuje zmeny druhej strany, operácie pochádzajúce od nej opisujú zmeny, ktoré cieľ už má, a ich predpoklady zlyhajú:

| Operácia | Stav v cieli, ktorý obsahuje zmeny druhej strany | Vybrať |
|---|---|---|
| `ours` vloženie, aktualizácia alebo odstránenie | Chýba | Áno |
| `merged` aktualizácia (riadok zmenili obe strany) | Obsahuje stĺpce druhej strany, chýbajú stĺpce našej strany | Áno |
| `theirs` vloženie | Riadok existuje, takže chránené vloženie ovplyvní nula riadkov | Nie |
| `merged` odstránenie (riadok odstránili obe strany) | Riadok už neexistuje, takže chránené odstránenie ovplyvní nula riadkov | Nie |
| `theirs` aktualizácia alebo odstránenie | Už použité | Nie |

Odovzdanie celého výsledku `Merql::merge()` do `applyGuarded()` preto zlyhá, len čo druhá strana vloží riadok alebo obe strany riadok odstránia. Najprv vyberte operácie podľa zdroja, ako je uvedené vyššie, alebo pomocou `ChangeGroupSelection` vytvoreného z kontroly UI. Vynechajte riadky uvedené v `$plan->conflicts`: vybraný výsledok je vždy čistý a operácia pre konfliktný riadok obsahuje hodnotu našej strany v konfliktnom stĺpci (pozrite si [Plány zlúčenia](https://merql.inline0.dev/sk/docs/merge/plans#selecting-what-a-target-is-missing)).

## Použitie vybraných operácií

Chránené použitie funguje s vybranými výsledkami zlúčenia vytvorenými z plánu zlúčenia vrátane výberov podľa skupiny zmien.

```php
use Merql\Plan\ChangeGroupSelection;
use Merql\Plan\SelectedMergeResultFactory;
use Merql\Snapshot\SnapshotStore;

$selection = ChangeGroupSelection::fromIds([$plan->changeGroups[0]->id]);
$selected = (new SelectedMergeResultFactory())->fromChangeGroupSelection(
    $plan,
    $selection,
    SnapshotStore::load($plan->baseSnapshot),
);

$applied = Merql::applyGuarded($selected, SnapshotStore::load($plan->theirsSnapshot));
```

Hostiteľská aplikácia tak môže pripraviť podmnožinu skontrolovaných skupín a zároveň overiť, že sa aktuálne riadky od vytvorenia plánu nezmenili.

## Náhľad SQL

Pomocou `SqlGenerator::generateGuarded()` si môžete prezrieť chránené príkazy bez ich vykonania.

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

$statements = SqlGenerator::generateGuarded(
    $selected,
    SnapshotStore::load($plan->theirsSnapshot),
);

foreach ($statements as $statement) {
    echo $statement['sql'] . "\n";
}
```

Generované príkazy sú naďalej parametrizované. Predpoklady sú vyjadrené pomocou dodatočných klauzúl `WHERE` pre aktualizácie a odstraňovania a kontrol `NOT EXISTS` pre vkladania.

## Chránené alebo bežné použitie

`Merql::apply()` vykoná rovnaké operácie bez predpokladov. Aktualizácie a odstraňovania potom uspejú bez ohľadu na to, či sa aktuálny riadok od vytvorenia plánu zmenil, a vloženie už existujúceho riadka zlyhá s chybou databázy o duplicitnom kľúči. Bežné použitie zvoľte pre cieľ, o ktorom viete, že stále obsahuje základný stav, a chránené použitie vtedy, keď iné procesy mohli po vytvorení snímky cieľ upraviť.
