
# Rea identiteet

Rea identiteet määrab, kuidas merql sobitab ridu eri hetktõmmiste vahel. Aluse võrdlemisel meie omaga peab see teadma, et aluse rida X on meie omas sama loogiline rida Y, isegi kui mõne veeru väärtus muutus. Identiteedireegel määrab, millistest veergudest reavõti moodustatakse.

```php
use Merql\Identity\PrimaryKeyIdentity;

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

## RowIdentity liides

Kõik identiteedistrateegiad rakendavad kahte meetodit:

```php
interface RowIdentity
{
    public function key(array $row): string;
    public function columns(): array;
}
```

`key()` loob rea andmetest kordumatu string-tunnuse. `columns()` tagastab identiteedi jaoks kasutatavate veergude nimed.

## PrimaryKeyIdentity

Vaikimisi strateegia. Sobitab ridu nende primaarvõtme veergude järgi.

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

Seda kasutab merql vaikimisi, kui tabeli skeemis on määratud primaarvõti.

## NaturalKeyIdentity

Tabelite puhul, millel puudub automaatselt suurenev primaarvõti, sobitatakse read kordumatu piirangu veergude järgi. Kasulik, kui PK on UUID või kui tabeli loogiline identiteet põhineb liitkordumatul piirangul.

```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

Varuvariant tabelitele, millel pole primaarvõtit ega kordumatuid piiranguid. Arvutab kõigi veeruväärtuste põhjal SHA-256 räsi. Kaks rida on samad, kui kõik nende veerud kattuvad.

```php
use Merql\Identity\ContentHashIdentity;

$identity = new ContentHashIdentity(['name', 'value']);
$key = $identity->key(['name' => 'color', 'value' => 'blue']);
// "a3f2..." (SHA-256 hash)
```

Sisu räsil põhineval identiteedil on üks piirang: kui rea sisu muutub, muutub ka selle räsi. Seetõttu näib rida vana räsi kustutamise ja uue räsi lisamisena, mitte uuendusena. See on vältimatu kompromiss, kui püsivad identiteediveerud puuduvad.

## IdentityRule

`IdentityRule` on serialiseeritav identiteedi konfiguratsioon, mida kasutavad hetktõmmise jäädvustamine ja ühendamisplaanid.

```php
use Merql\Identity\IdentityRule;

$rule = IdentityRule::natural(['option_name']);
$key = $rule->key(['option_name' => 'siteurl']);
// "siteurl"
```

Reeglitüübid:

| Tüüp | Tähendus |
|---|---|
| `primary` | Andmebaasi primaarvõtme veerud |
| `natural` | Püsivad kordumatud äriveerud |
| `composite` | Liitloogilise identiteedi veerud |
| `content` | Sisu räsil põhinev varuvariant |

`IdentityRule::forSchema($schema)` lahendab vaike-reegli järgmiselt:

1. Kui tabelil on primaarvõti, kasuta selle veerge.
2. Kui tabelil on kordumatud võtmed, kasuta esimese sellise võtme veerge.
3. Kui kumbagi pole, kasuta kõiki veerge (sisu räsil põhinev varuvariant).

## IdentityRuleSet

Kasuta `IdentityRuleSet`, kui hostrakendus teab skeemi vaikeväärtusest paremat identiteedireeglit.

```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` lahendab iga jäädvustatud tabeli jaoks `IdentityRule`. Valitud veerud salvestatakse asukohta `TableSnapshot::identityColumns` ning neid kasutatakse kõigis järgnevates erinevuste leidmise, ühendamise, plaani, SQL ja tagasipööramise toimingutes.

`Merql::init()` saab staatilise fassaadi kasutamisel sama reeglistiku:

```php
use Merql\Merql;

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

Reeglistikud serialiseeritakse stabiilse massiivina:

```php
$payload = $rules->toArray();
$rules = IdentityRuleSet::fromArray($payload);
```

## Mitmetähenduslik identiteet

Kui kaks jäädvustatud rida annavad sama identiteedivõtme, jäädvustamine nurjub, selle asemel et üks rida teisega üle kirjutada.

```php
use Merql\Exceptions\SnapshotException;

try {
    $snapshotter->capture('live', ['wp_postmeta']);
} catch (SnapshotException $e) {
    // The configured identity rule matched multiple rows.
}
```

Hostrakendused saavad enne jäädvustamist kontrollida töötlemata ridu meetodiga `IdentityRuleSet::conflictsFor()`, et tuvastada mitmetähenduslikkus:

```php
$conflicts = $rules->conflictsFor(
    'wp_postmeta',
    IdentityRule::natural(['post_id', 'meta_key']),
    $rows,
);
```

WordPress meta-tabelite puhul on `post_id + meta_key` tavaliselt kasulik rühmitamiseks ja kuvamiseks, kuid rea identiteedina pole see ohutu, sest korduvad metavõtmed on lubatud. Kasuta rea identiteediks tabeli primaarvõtit, välja arvatud juhul, kui hostrakendus suudab tõestada loomuliku võtme kordumatust.

## Reavõtme kodeerimine

Reavõtmed on kodeeritud stringid, mis ühendavad mitme veeru väärtused ühikueraldajaga (`\x1F`). Eraldajat või protsendimärki sisaldavad väärtused paokodeeritakse kokkupõrgete vältimiseks:

```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']
```

Paokodeerimise reeglid:

| Algne | Paokodeeritud |
|---|---|
| `%` | `%25` |
| `\x1F` (ühikueraldaja) | `%1F` |

See tagab, et `\x1F` sisaldav väärtus ei jagune mitmeks võtmeosaks. Kodeering on deterministlik ja pööratav.

## Automaatse suurenemise lahknevus

Kui mõlemad harud lisavad uusi ridu, erinevad automaatselt suurenevad ID-d harude vahel. Rida #100 meie harus võib olla täiesti erinev kirje kui rida #100 nende harus.

merql lahendab selle primaarvõtme järgi sobitamisega. Kui mõlemad pooled lisavad sama automaatselt suureneva ID-ga rea, märgitakse see `insert_insert` konfliktina. Selle vältimiseks kasuta tabelites, kuhu eeldatavasti lisatakse kirjeid samal ajal, loomulikke võtmeid (kordumatuid veerge, mis püsivad harude vahel samad).

```php
// A natural key on (slug) avoids auto-increment collisions.
$identity = new NaturalKeyIdentity(['slug']);
```

## Identiteet hetktõmmistes

Jäädvustamisel valitud identiteediveerud salvestatakse iga `TableSnapshot` juurde:

```php
$tableSnapshot = $snapshot->getTable('posts');
$tableSnapshot->identityColumns;
// ['id']
```

`SqlGenerator` kasutab neid veerge lausete UPDATE ja DELETE tingimuste WHERE koostamiseks; ühendamisplaanid kasutavad neid dekodeeritud identiteediväärtuste esitamiseks ülevaatuseks, valikuks, kaitstud rakendamiseks ja tagasipööramiseks.
