Přejít k obsahu

Chráněné použití

GuardedApplier použije čistý MergeResult pouze tehdy, pokud cílová databáze stále odpovídá očekávanému aktuálnímu snímku. Hodí se, když byl plán sloučení zkontrolován dříve a aktuální řádky se mohly před použitím změnit.

Obvyklé nastavení přenáší úpravy testovacího prostředí do živé databáze. Základ tvoří živá databáze v okamžiku, kdy z ní bylo testovací prostředí zkopírováno, ours je upravená kopie a theirs je aktuální živá databáze. Cílem je živá databáze, takže očekávaným aktuálním snímkem je theirs a zapisují se operace, které živá databáze zatím neobsahuje (viz Výběr operací).

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áze předané do Merql::init(). Očekávaný aktuální snímek musí popisovat stejnou databázi: pořiďte ho z cíle a dostatečně nedávno, aby podmínky měly vypovídací hodnotu.

Předběžné podmínky

Chráněné SQL přidává do generovaných příkazů podmínky pro aktuální řádky:

  • u aktualizací musí v cíli existovat identita řádku a hodnoty sloupců neurčujících identitu se musí shodovat s očekávaným aktuálním řádkem,
  • u odstranění musí v cíli existovat identita řádku a hodnoty sloupců neurčujících identitu se musí shodovat s očekávaným aktuálním řádkem,
  • u vložení nesmí cílová identita již existovat.

Pokud některý příkaz ovlivní nula řádků, merql považuje plán za zastaralý. Transakce se vrátí zpět a vrácený ApplyResult obsahuje chybu Optimistic precondition failed for guarded merge statement.

V MySQL ovlivní aktualizace, jejíž hodnoty již odpovídají aktuálnímu řádku, nula řádků, protože MySQL počítá změněné řádky, nikoli nalezené. Aktualizace se proto vyhodnotí jako zastaralá, i když řádek odpovídá (#2). SQLite počítá nalezené řádky a přijme ji. Může k tomu dojít při aktualizaci merged, když obě strany provedly stejnou změnu řádku.

Výběr operací

Výsledek sloučení obsahuje operace obou stran, protože mění základ na sloučený stav. Při použití na cíli, který již obsahuje theirs, změny převzaté z theirs popisují stav, který cíl už má, a jejich podmínky selžou:

Operace Stav v cíli obsahujícím theirs Vybrat
ours vložení, aktualizace nebo odstranění Chybí Ano
merged aktualizace (obě strany změnily řádek) Obsahuje sloupce theirs, chybí sloupce ours Ano
theirs vložení Řádek existuje, takže chráněné vložení ovlivní nula řádků Ne
merged odstranění (obě strany odstranily řádek) Řádek chybí, takže chráněné odstranění ovlivní nula řádků Ne
theirs aktualizace nebo odstranění Již použito Ne

Předání celého výsledku Merql::merge() do applyGuarded() proto selže, jakmile theirs vložilo řádek nebo ho obě strany odstranily. Nejprve vyberte operace podle zdroje, jak je uvedeno výše, nebo pomocí ChangeGroupSelection vytvořeného z kontroly UI. Vynechte řádky uvedené v $plan->conflicts: vybraný výsledek je vždy čistý a operace konfliktního řádku obsahuje hodnotu ours v konfliktním sloupci (viz Plány sloučení).

Vybrané použití

Chráněné použití funguje s vybranými výsledky sloučení vytvořenými z plánu sloučení, včetně výběrů podle skupiny změn.

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));

Hostitelská aplikace tak může připravit podmnožinu zkontrolovaných skupin a zároveň ověřit, že se aktuální řádky od vytvoření plánu nezměnily.

Náhled SQL

Pomocí SqlGenerator::generateGuarded() zkontrolujte chráněné příkazy, aniž byste je provedli.

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é příkazy jsou stále parametrizované. Předběžné podmínky jsou vyjádřeny dalšími klauzulemi WHERE pro aktualizace a odstranění a kontrolami NOT EXISTS pro vložení.

Chráněné nebo běžné použití

Merql::apply() provádí stejné operace bez předběžných podmínek. Aktualizace a odstranění pak uspějí bez ohledu na to, zda se aktuální řádek od vytvoření plánu změnil, a vložení existujícího řádku selže kvůli chybě duplicitního klíče databáze. Běžné použití zvolte pro cíl, o němž víte, že stále obsahuje základ; chráněné použití zvolte, pokud se cíle od pořízení snímku mohli dotknout jiní zapisující.