Rækkeidentitet
Rækkeidentitet afgør, hvordan merql matcher rækker på tværs af øjebliksbilleder. Når base sammenlignes med vores, skal systemet vide, at række X i base er den samme logiske række som række Y i vores, selvom nogle kolonneværdier er ændret. Identitetsreglen bestemmer, hvilke kolonner der udgør rækkenøglen.
use Merql\Identity\PrimaryKeyIdentity;
$identity = new PrimaryKeyIdentity(['id']);
$key = $identity->key(['id' => 42, 'title' => 'Hello']);
// "42"
Grænsefladen RowIdentity
Alle identitetsstrategier implementerer to metoder:
interface RowIdentity
{
public function key(array $row): string;
public function columns(): array;
}
key() opbygger en unik strengidentifikator ud fra en rækkes data. columns() returnerer de kolonnenavne, der bruges til identifikation.
PrimaryKeyIdentity
Standardstrategien. Matcher rækker efter deres primærnøglekolonner.
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)
Det er denne strategi, merql bruger som standard, når tabellen har en primærnøgle defineret i sit skema.
NaturalKeyIdentity
For tabeller uden en auto-increment-primærnøgle matches rækker efter kolonner med unikke begrænsninger. Nyttigt, når PK er en UUID, eller når tabellen bruger en sammensat unik begrænsning som sin logiske identitet.
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
Fallback for tabeller uden primærnøgle og uden unikke begrænsninger. Beregner en SHA-256-hash af alle kolonneværdier. To rækker er ens, hvis alle kolonner matcher.
use Merql\Identity\ContentHashIdentity;
$identity = new ContentHashIdentity(['name', 'value']);
$key = $identity->key(['name' => 'color', 'value' => 'blue']);
// "a3f2..." (SHA-256 hash)
Identitet baseret på indholdshash har en begrænsning: Hvis indholdet i en række ændres, ændres dens hash også. Rækken fremstår derfor som en sletning af den gamle hash og en indsættelse af den nye hash i stedet for en opdatering. Det er en uundgåelig afvejning, når der ikke findes stabile identitetskolonner.
IdentityRule
IdentityRule er den serialiserbare identitetskonfiguration, der bruges ved optagelse af øjebliksbilleder
og i mergeplaner.
use Merql\Identity\IdentityRule;
$rule = IdentityRule::natural(['option_name']);
$key = $rule->key(['option_name' => 'siteurl']);
// "siteurl"
Regeltyper:
| Type | Betydning |
|---|---|
primary |
Primærnøglekolonner i databasen |
natural |
Stabile, unikke forretningskolonner |
composite |
Kolonner for sammensat logisk identitet |
content |
Fallback med indholdshash |
IdentityRule::forSchema($schema) finder standardreglen:
- Hvis tabellen har en primærnøgle, bruges primærnøglekolonnerne.
- Hvis tabellen har unikke nøgler, bruges kolonnerne fra den første unikke nøgle.
- Hvis ingen af delene findes, bruges alle kolonner (fallback med indholdshash).
IdentityRuleSet
Brug IdentityRuleSet, når en værtsapplikation kender en bedre identitetsregel end
skemaets standardregel.
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 finder en IdentityRule for hver optaget tabel. De
valgte kolonner gemmes i TableSnapshot::identityColumns og bruges til alle
efterfølgende diff-, merge-, plan-, SQL- og rollback-handlinger.
Merql::init() kan modtage det samme regelsæt, når den statiske facade bruges:
use Merql\Merql;
Merql::init($connection, identityRules: $rules);
Merql::snapshot('live', ['wp_options']);
Regelsæt har en stabil serialisering af arrays:
$payload = $rules->toArray();
$rules = IdentityRuleSet::fromArray($payload);
Tvetydig identitet
Hvis to optagede rækker giver den samme identitetsnøgle, mislykkes optagelsen i stedet for at overskrive den ene række med den anden.
use Merql\Exceptions\SnapshotException;
try {
$snapshotter->capture('live', ['wp_postmeta']);
} catch (SnapshotException $e) {
// The configured identity rule matched multiple rows.
}
Værtsapplikationer kan kontrollere rå rækker på forhånd med IdentityRuleSet::conflictsFor(),
hvis de vil rapportere tvetydighed inden optagelsen:
$conflicts = $rules->conflictsFor(
'wp_postmeta',
IdentityRule::natural(['post_id', 'meta_key']),
$rows,
);
For WordPress-metatabeller er post_id + meta_key normalt nyttig til gruppering
og visning, men ikke sikker som rækkeidentitet, fordi dublerede metanøgler er gyldige.
Brug tabellens primærnøgle som rækkeidentitet, medmindre værtsapplikationen kan
bevise, at den naturlige nøgle er unik.
Kodning af rækkenøgler
Rækkenøgler er kodede strenge, der kombinerer flere kolonneværdier med en enhedsseparator (\x1F). Værdier, der indeholder separatoren eller procenttegnet, escapes for at forhindre kollisioner:
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']
Escaperingsregler:
| Oprindelig | Escapet |
|---|---|
% |
%25 |
\x1F (enhedsseparator) |
%1F |
Det sikrer, at en værdi, der indeholder \x1F, ikke opdeles i flere nøgledelværdier. Kodningen er deterministisk og reversibel.
Afvigelser i auto-increment
Når begge sider indsætter nye rækker, vil auto-increment-id'er være forskellige på tværs af grene. Række #100 i vores kan være en helt anden post end række #100 i deres.
merql håndterer dette ved at matche på primærnøgle. Hvis begge sider har indsat en række med samme auto-increment-id, markeres det som en konflikt af typen insert_insert. For at undgå dette skal du bruge naturlige nøgler (unikke kolonner, der er stabile på tværs af grene) i tabeller, hvor samtidige indsættelser forventes.
// A natural key on (slug) avoids auto-increment collisions.
$identity = new NaturalKeyIdentity(['slug']);
Identitet i øjebliksbilleder
De identitetskolonner, der vælges under optagelsen, gemmes sammen med hvert TableSnapshot:
$tableSnapshot = $snapshot->getTable('posts');
$tableSnapshot->identityColumns;
// ['id']
SqlGenerator bruger disse kolonner til at opbygge WHERE-betingelser til UPDATE- og DELETE-udsagn. Mergeplaner bruger dem til at vise afkodede identitetsværdier til gennemsyn, valg, kontrolleret anvendelse og rollback.