Gå til indhold

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.

PHP
use Merql\Identity\PrimaryKeyIdentity;

$identity = new PrimaryKeyIdentity(['id']);
$key = $identity->key(['id' => 42, 'title' => 'Hello']);
// "42"

Grænsefladen RowIdentity

Alle identitetsstrategier implementerer to metoder:

PHP
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.

PHP
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.

PHP
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.

PHP
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.

PHP
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:

  1. Hvis tabellen har en primærnøgle, bruges primærnøglekolonnerne.
  2. Hvis tabellen har unikke nøgler, bruges kolonnerne fra den første unikke nøgle.
  3. 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.

PHP
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:

PHP
use Merql\Merql;

Merql::init($connection, identityRules: $rules);
Merql::snapshot('live', ['wp_options']);

Regelsæt har en stabil serialisering af arrays:

PHP
$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.

PHP
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:

PHP
$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:

PHP
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.

PHP
// 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:

PHP
$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.