Identita riadka
Identita riadka určuje, ako merql páruje riadky naprieč snímkami. Pri porovnávaní základu s našou verziou musí vedieť, že riadok X v základe je ten istý logický riadok ako riadok Y v našej verzii, aj keď sa hodnoty niektorých stĺpcov zmenili. Pravidlo identity určuje, ktoré stĺpce tvoria kľúč riadka.
use Merql\Identity\PrimaryKeyIdentity;
$identity = new PrimaryKeyIdentity(['id']);
$key = $identity->key(['id' => 42, 'title' => 'Hello']);
// "42"
Rozhranie RowIdentity
Všetky stratégie identity implementujú dve metódy:
interface RowIdentity
{
public function key(array $row): string;
public function columns(): array;
}
key() vytvorí z údajov riadka jedinečný textový identifikátor. columns() vráti názvy stĺpcov použitých na určenie identity.
PrimaryKeyIdentity
Predvolená stratégia. Páruje riadky podľa stĺpcov ich primárneho kľúča.
use Merql\Identity\PrimaryKeyIdentity;
// Single-column PK.
$identity = new PrimaryKeyIdentity(['id']);
$key = $identity->key(['id' => 42, 'title' => 'Hello']);
// "42"
// Composite PK.
$identity = new PrimaryKeyIdentity(['tenant_id', 'user_id']);
$key = $identity->key(['tenant_id' => 1, 'user_id' => 99, 'name' => 'Alice']);
// "1\x1F99" (unit separator between parts)
Túto stratégiu používa merql predvolene, ak má tabuľka v schéme definovaný primárny kľúč.
NaturalKeyIdentity
V tabuľkách bez primárneho kľúča s automatickým zvyšovaním páruje riadky podľa stĺpcov s obmedzením jedinečnosti. Hodí sa, keď je PK UUID alebo keď tabuľka používa zložené obmedzenie jedinečnosti ako svoju logickú identitu.
use Merql\Identity\NaturalKeyIdentity;
$identity = new NaturalKeyIdentity(['email']);
$key = $identity->key(['id' => 1, 'email' => 'alice@example.com', 'name' => 'Alice']);
// "alice@example.com"
$identity = new NaturalKeyIdentity(['country', 'postal_code']);
$key = $identity->key(['country' => 'US', 'postal_code' => '90210', 'city' => 'Beverly Hills']);
// "US\x1F90210"
ContentHashIdentity
Náhradné riešenie pre tabuľky bez primárneho kľúča a obmedzení jedinečnosti. Vypočíta hash SHA-256 všetkých hodnôt stĺpcov. Dva riadky sú rovnaké, ak sa zhodujú všetky stĺpce.
use Merql\Identity\ContentHashIdentity;
$identity = new ContentHashIdentity(['name', 'value']);
$key = $identity->key(['name' => 'color', 'value' => 'blue']);
// "a3f2..." (SHA-256 hash)
Identita založená na hashi obsahu má obmedzenie: ak sa obsah riadka zmení, zmení sa aj jeho hash, takže riadok sa namiesto aktualizácie javí ako odstránenie starého hashu a vloženie nového. Ide o nevyhnutný kompromis, keď nie sú k dispozícii stabilné identifikačné stĺpce.
IdentityRule
IdentityRule je serializovateľná konfigurácia identity používaná pri zachytávaní snímok
a v plánoch zlúčenia.
use Merql\Identity\IdentityRule;
$rule = IdentityRule::natural(['option_name']);
$key = $rule->key(['option_name' => 'siteurl']);
// "siteurl"
Typy pravidiel:
| Typ | Význam |
|---|---|
primary |
Stĺpce primárneho kľúča databázy |
natural |
Stabilné jedinečné obchodné stĺpce |
composite |
Stĺpce zloženého logického identifikátora |
content |
Záložná možnosť: hash obsahu |
IdentityRule::forSchema($schema) určuje predvolené pravidlo:
- Ak má tabuľka primárny kľúč, použijú sa jeho stĺpce.
- Ak má tabuľka jedinečné kľúče, použijú sa stĺpce prvého z nich.
- Ak neexistuje ani jeden, použijú sa všetky stĺpce (záložná možnosť: hash obsahu).
IdentityRuleSet
Použite IdentityRuleSet, keď hostiteľská aplikácia pozná vhodnejšie pravidlo identity než
predvolené pravidlo schémy.
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;
use Merql\Snapshot\Snapshotter;
$rules = new IdentityRuleSet([
'wp_options' => IdentityRule::natural(['option_name']),
'wp_term_relationships' => IdentityRule::composite(['object_id', 'term_taxonomy_id']),
]);
$snapshotter = new Snapshotter($connection, identityRules: $rules);
$snapshot = $snapshotter->capture('live', ['wp_options', 'wp_term_relationships']);
Snapshotter určuje IdentityRule pre každú zachytenú tabuľku. Vybrané
stĺpce sa ukladajú do TableSnapshot::identityColumns a používajú sa pri všetkých
nasledujúcich operáciách porovnávania rozdielov, zlúčenia, plánovania, SQL a návratu zmien.
Merql::init() môže pri použití statického rozhrania dostať rovnakú množinu pravidiel:
use Merql\Merql;
Merql::init($connection, identityRules: $rules);
Merql::snapshot('live', ['wp_options']);
Množiny pravidiel majú stabilnú serializáciu polí:
$payload = $rules->toArray();
$rules = IdentityRuleSet::fromArray($payload);
Nejednoznačná identita
Ak dva zachytené riadky vytvoria rovnaký identifikačný kľúč, zachytenie zlyhá namiesto toho, aby jeden riadok prepísalo druhým.
use Merql\Exceptions\SnapshotException;
try {
$snapshotter->capture('live', ['wp_postmeta']);
} catch (SnapshotException $e) {
// The configured identity rule matched multiple rows.
}
Hostiteľské aplikácie môžu pred zachytením overiť nespracované riadky pomocou IdentityRuleSet::conflictsFor(),
ak chcú nejednoznačnosť oznámiť vopred:
$conflicts = $rules->conflictsFor(
'wp_postmeta',
IdentityRule::natural(['post_id', 'meta_key']),
$rows,
);
V prípade metatabuliek WordPress je post_id + meta_key zvyčajne užitočné na zoskupovanie
a zobrazenie, ale nie je bezpečné ako identita riadka, pretože duplicitné kľúče metaúdajov sú platné.
Na identifikáciu riadka použite primárny kľúč tabuľky, pokiaľ hostiteľská aplikácia nedokáže,
že prirodzený kľúč je jedinečný.
Kódovanie kľúča riadka
Kľúče riadkov sú zakódované reťazce, ktoré spájajú hodnoty viacerých stĺpcov oddeľovačom jednotiek (\x1F). Hodnoty obsahujúce oddeľovač alebo znak percenta sa escapujú, aby sa predišlo kolíziám:
use Merql\Snapshot\Snapshotter;
// Encode.
$key = Snapshotter::buildRowKey(
['tenant_id' => '1', 'user_id' => '99'],
['tenant_id', 'user_id'],
);
// "1\x1F99"
// Decode.
$parts = Snapshotter::decodeRowKey($key);
// ['1', '99']
Pravidlá escapovania:
| Pôvodná hodnota | Escapovaná hodnota |
|---|---|
% |
%25 |
\x1F (oddeľovač jednotiek) |
%1F |
Tým sa zabezpečí, že hodnota obsahujúca \x1F sa nerozdelí na viacero častí kľúča. Kódovanie je deterministické a reverzibilné.
Rozdiely v automatickom inkrementovaní
Keď obe strany vložia nové riadky, automaticky inkrementované ID sa budú medzi vetvami líšiť. Riadok #100 u nás môže byť úplne iný záznam než riadok #100 u nich.
merql to rieši párovaním podľa primárneho kľúča. Ak obe strany vložia riadok s rovnakým automaticky inkrementovaným ID, označí sa to ako konflikt insert_insert. Aby ste tomu predišli, v tabuľkách, kde sa očakávajú súbežné vkladania, používajte prirodzené kľúče (jedinečné stĺpce, ktoré sú stabilné naprieč vetvami).
// A natural key on (slug) avoids auto-increment collisions.
$identity = new NaturalKeyIdentity(['slug']);
Identita v snímkach
Stĺpce identity vybrané pri zachytávaní sa ukladajú spolu s každým TableSnapshot:
$tableSnapshot = $snapshot->getTable('posts');
$tableSnapshot->identityColumns;
// ['id']
Pomocou týchto stĺpcov vytvára SqlGenerator klauzuly WHERE pre príkazy UPDATE a DELETE a plány zlúčenia z nich poskytujú dekódované hodnoty identity na kontrolu, výber, podmienené vykonanie a návrat zmien.